Merge "Move background from notification into a view."
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index 8348e9b..f867068 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -22,6 +22,14 @@
     android:focusable="true"
     android:clickable="true"
     >
+    <com.android.systemui.statusbar.NotificationBackgroundView android:id="@+id/backgroundNormal"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        />
+    <com.android.systemui.statusbar.NotificationBackgroundView android:id="@+id/backgroundDimmed"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        />
     <TextView
         android:id="@+id/more_text"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 8959a5b..2837cd6 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -6,6 +6,16 @@
     android:clickable="true"
     >
 
+    <com.android.systemui.statusbar.NotificationBackgroundView
+        android:id="@+id/backgroundNormal"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <com.android.systemui.statusbar.NotificationBackgroundView
+        android:id="@+id/backgroundDimmed"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
     <com.android.systemui.statusbar.NotificationContentView android:id="@+id/expanded"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 5e8c769..724b6a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -19,18 +19,17 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 
-import com.android.internal.R;
+import com.android.systemui.R;
 
 /**
  * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -38,13 +37,19 @@
  */
 public abstract class ActivatableNotificationView extends ExpandableOutlineView {
 
-    private static final long DOUBLETAP_TIMEOUT_MS = 1000;
+    private static final long DOUBLETAP_TIMEOUT_MS = 1200;
     private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
+    private static final int ACTIVATE_ANIMATION_LENGTH = 220;
+
+    private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
+            = new PathInterpolator(0.6f, 0, 0.5f, 1);
+    private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
+            = new PathInterpolator(0, 0, 0.5f, 1);
 
     private boolean mDimmed;
 
-    private int mBgResId = R.drawable.notification_quantum_bg;
-    private int mDimmedBgResId = R.drawable.notification_quantum_bg_dim;
+    private int mBgResId = com.android.internal.R.drawable.notification_quantum_bg;
+    private int mDimmedBgResId = com.android.internal.R.drawable.notification_quantum_bg_dim;
 
     private int mBgTint = 0;
     private int mDimmedBgTint = 0;
@@ -61,18 +66,30 @@
 
     private OnActivatedListener mOnActivatedListener;
 
-    protected Drawable mBackgroundNormal;
-    protected Drawable mBackgroundDimmed;
-    private ObjectAnimator mBackgroundAnimator;
+    private Interpolator mLinearOutSlowInInterpolator;
     private Interpolator mFastOutSlowInInterpolator;
 
+    private NotificationBackgroundView mBackgroundNormal;
+    private NotificationBackgroundView mBackgroundDimmed;
+    private ObjectAnimator mBackgroundAnimator;
+
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-        updateBackgroundResource();
-        setWillNotDraw(false);
         mFastOutSlowInInterpolator =
                 AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
+        mLinearOutSlowInInterpolator =
+                AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
+        setClipChildren(false);
+        setClipToPadding(false);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBackgroundNormal = (NotificationBackgroundView) findViewById(R.id.backgroundNormal);
+        mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed);
+        updateBackgroundResource();
     }
 
     private final Runnable mTapTimeoutRunnable = new Runnable() {
@@ -83,42 +100,6 @@
     };
 
     @Override
-    protected void onDraw(Canvas canvas) {
-        draw(canvas, mBackgroundNormal);
-        draw(canvas, mBackgroundDimmed);
-    }
-
-    private void draw(Canvas canvas, Drawable drawable) {
-        if (drawable != null) {
-            drawable.setBounds(0, mClipTopAmount, getWidth(), mActualHeight);
-            drawable.draw(canvas);
-        }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || who == mBackgroundNormal
-                || who == mBackgroundDimmed;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        drawableStateChanged(mBackgroundNormal);
-        drawableStateChanged(mBackgroundDimmed);
-    }
-
-    private void drawableStateChanged(Drawable d) {
-        if (d != null && d.isStateful()) {
-            d.setState(getDrawableState());
-        }
-    }
-
-    @Override
-    public void setOnClickListener(OnClickListener l) {
-        super.setOnClickListener(l);
-    }
-
-    @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (mDimmed) {
             return handleTouchEventDimmed(event);
@@ -146,10 +127,9 @@
             case MotionEvent.ACTION_UP:
                 if (isWithinTouchSlop(event)) {
                     if (!mActivated) {
-                        makeActive(event.getX(), event.getY());
+                        makeActive();
                         postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS);
                     } else {
-                        makeInactive();
                         performClick();
                     }
                 } else {
@@ -165,23 +145,58 @@
         return true;
     }
 
-    private void makeActive(float x, float y) {
-        mBackgroundDimmed.setHotspot(0, x, y);
+    private void makeActive() {
+        startActivateAnimation(false /* reverse */);
         mActivated = true;
         if (mOnActivatedListener != null) {
             mOnActivatedListener.onActivated(this);
         }
     }
 
+    private void startActivateAnimation(boolean reverse) {
+        int widthHalf = mBackgroundNormal.getWidth()/2;
+        int heightHalf = mBackgroundNormal.getActualHeight()/2;
+        float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf);
+        ValueAnimator animator =
+                mBackgroundNormal.createRevealAnimator(widthHalf, heightHalf, 0, radius);
+        mBackgroundNormal.setVisibility(View.VISIBLE);
+        Interpolator interpolator;
+        Interpolator alphaInterpolator;
+        if (!reverse) {
+            interpolator = mLinearOutSlowInInterpolator;
+            alphaInterpolator = mLinearOutSlowInInterpolator;
+        } else {
+            interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
+            alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
+        }
+        animator.setInterpolator(interpolator);
+        animator.setDuration(ACTIVATE_ANIMATION_LENGTH);
+        if (reverse) {
+            mBackgroundNormal.setAlpha(1f);
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mBackgroundNormal.setVisibility(View.INVISIBLE);
+                }
+            });
+            animator.reverse();
+        } else {
+            mBackgroundNormal.setAlpha(0.4f);
+            animator.start();
+        }
+        mBackgroundNormal.animate()
+                .alpha(reverse ? 0f : 1f)
+                .setInterpolator(alphaInterpolator)
+                .setDuration(ACTIVATE_ANIMATION_LENGTH);
+    }
+
     /**
      * Cancels the hotspot and makes the notification inactive.
      */
     private void makeInactive() {
         if (mActivated) {
-            // Make sure that we clear the hotspot from the center.
-            if (mBackgroundDimmed != null) {
-                mBackgroundDimmed.setHotspot(0, getWidth() / 2, getActualHeight() / 2);
-                mBackgroundDimmed.removeHotspot(0);
+            if (mDimmed) {
+                startActivateAnimation(true /* reverse */);
             }
             mActivated = false;
         }
@@ -227,16 +242,16 @@
 
     private void fadeBackgroundResource() {
         if (mDimmed) {
-            setBackgroundDimmed(mDimmedBgResId, mDimmedBgTint);
+            mBackgroundDimmed.setVisibility(View.VISIBLE);
         } else {
-            setBackgroundNormal(mBgResId, mBgTint);
+            mBackgroundNormal.setVisibility(View.VISIBLE);
         }
-        int startAlpha = mDimmed ? 255 : 0;
-        int endAlpha = mDimmed ? 0 : 255;
+        float startAlpha = mDimmed ? 1f : 0;
+        float endAlpha = mDimmed ? 0 : 1f;
         int duration = BACKGROUND_ANIMATION_LENGTH_MS;
         // Check whether there is already a background animation running.
         if (mBackgroundAnimator != null) {
-            startAlpha = (Integer) mBackgroundAnimator.getAnimatedValue();
+            startAlpha = (Float) mBackgroundAnimator.getAnimatedValue();
             duration = (int) mBackgroundAnimator.getCurrentPlayTime();
             mBackgroundAnimator.removeAllListeners();
             mBackgroundAnimator.cancel();
@@ -247,16 +262,16 @@
         }
         mBackgroundNormal.setAlpha(startAlpha);
         mBackgroundAnimator =
-                ObjectAnimator.ofInt(mBackgroundNormal, "alpha", startAlpha, endAlpha);
+                ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
         mBackgroundAnimator.setInterpolator(mFastOutSlowInInterpolator);
         mBackgroundAnimator.setDuration(duration);
         mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (mDimmed) {
-                    setBackgroundNormal(null);
+                    mBackgroundNormal.setVisibility(View.INVISIBLE);
                 } else {
-                    setBackgroundDimmed(null);
+                    mBackgroundDimmed.setVisibility(View.INVISIBLE);
                 }
                 mBackgroundAnimator = null;
             }
@@ -266,61 +281,17 @@
 
     private void updateBackgroundResource() {
         if (mDimmed) {
-            setBackgroundDimmed(mDimmedBgResId, mDimmedBgTint);
-            mBackgroundDimmed.setAlpha(255);
-            setBackgroundNormal(null);
+            mBackgroundDimmed.setVisibility(View.VISIBLE);
+            mBackgroundDimmed.setCustomBackground(mDimmedBgResId, mDimmedBgTint);
+            mBackgroundNormal.setVisibility(View.INVISIBLE);
         } else {
-            setBackgroundDimmed(null);
-            setBackgroundNormal(mBgResId, mBgTint);
-            mBackgroundNormal.setAlpha(255);
+            mBackgroundDimmed.setVisibility(View.INVISIBLE);
+            mBackgroundNormal.setVisibility(View.VISIBLE);
+            mBackgroundNormal.setCustomBackground(mBgResId, mBgTint);
+            mBackgroundNormal.setAlpha(1f);
         }
     }
 
-    /**
-     * Sets a background drawable for the normal state. As we need to change our bounds
-     * independently of layout, we need the notion of a background independently of the regular View
-     * background..
-     */
-    private void setBackgroundNormal(Drawable backgroundNormal) {
-        if (mBackgroundNormal != null) {
-            mBackgroundNormal.setCallback(null);
-            unscheduleDrawable(mBackgroundNormal);
-        }
-        mBackgroundNormal = backgroundNormal;
-        if (mBackgroundNormal != null) {
-            mBackgroundNormal.setCallback(this);
-        }
-        invalidate();
-    }
-
-    private void setBackgroundDimmed(Drawable overlay) {
-        if (mBackgroundDimmed != null) {
-            mBackgroundDimmed.setCallback(null);
-            unscheduleDrawable(mBackgroundDimmed);
-        }
-        mBackgroundDimmed = overlay;
-        if (mBackgroundDimmed != null) {
-            mBackgroundDimmed.setCallback(this);
-        }
-        invalidate();
-    }
-
-    private void setBackgroundNormal(int drawableResId, int tintColor) {
-        final Drawable d = getResources().getDrawable(drawableResId);
-        if (tintColor != 0) {
-            d.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
-        }
-        setBackgroundNormal(d);
-    }
-
-    private void setBackgroundDimmed(int drawableResId, int tintColor) {
-        final Drawable d = getResources().getDrawable(drawableResId);
-        if (tintColor != 0) {
-            d.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
-        }
-        setBackgroundDimmed(d);
-    }
-
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
@@ -330,14 +301,16 @@
     @Override
     public void setActualHeight(int actualHeight, boolean notifyListeners) {
         super.setActualHeight(actualHeight, notifyListeners);
-        invalidate();
         setPivotY(actualHeight / 2);
+        mBackgroundNormal.setActualHeight(actualHeight);
+        mBackgroundDimmed.setActualHeight(actualHeight);
     }
 
     @Override
     public void setClipTopAmount(int clipTopAmount) {
         super.setClipTopAmount(clipTopAmount);
-        invalidate();
+        mBackgroundNormal.setClipTopAmount(clipTopAmount);
+        mBackgroundDimmed.setClipTopAmount(clipTopAmount);
     }
 
     public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
new file mode 100644
index 0000000..e49ec64
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * A view that can be used for both the dimmed and normal background of an notification.
+ */
+public class NotificationBackgroundView extends View {
+
+    private Drawable mBackground;
+    private int mClipTopAmount;
+    private int mActualHeight;
+    private boolean mActualHeightInitialized;
+
+    public NotificationBackgroundView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (!mActualHeightInitialized && mActualHeight == 0) {
+            mActualHeight = getHeight();
+        }
+        mActualHeightInitialized = true;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        draw(canvas, mBackground);
+    }
+
+    private void draw(Canvas canvas, Drawable drawable) {
+        if (drawable != null) {
+            drawable.setBounds(0, mClipTopAmount, getWidth(), mActualHeight);
+            drawable.draw(canvas);
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || who == mBackground;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        drawableStateChanged(mBackground);
+    }
+
+    private void drawableStateChanged(Drawable d) {
+        if (d != null && d.isStateful()) {
+            d.setState(getDrawableState());
+        }
+    }
+
+    /**
+     * Sets a background drawable. As we need to change our bounds independently of layout, we need
+     * the notion of a background independently of the regular View background..
+     */
+    public void setCustomBackground(Drawable background) {
+        if (mBackground != null) {
+            mBackground.setCallback(null);
+            unscheduleDrawable(mBackground);
+        }
+        mBackground = background;
+        if (mBackground != null) {
+            mBackground.setCallback(this);
+        }
+        invalidate();
+    }
+
+    public void setCustomBackground(int drawableResId, int tintColor) {
+        final Drawable d = getResources().getDrawable(drawableResId);
+        if (tintColor != 0) {
+            d.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
+        }
+        setCustomBackground(d);
+    }
+
+    public void setActualHeight(int actualHeight) {
+        mActualHeight = actualHeight;
+        invalidate();
+    }
+
+    public int getActualHeight() {
+        return mActualHeight;
+    }
+
+    public void setClipTopAmount(int clipTopAmount) {
+        mClipTopAmount = clipTopAmount;
+        invalidate();
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+
+        // Prevents this view from creating a layer when alpha is animating.
+        return false;
+    }
+}