Use RenderThread for navigation bar ripples

Bug: 17506181
Change-Id: Icf3b80f8c4bc29fe85313381d4019dda3ef85ea9
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 076f1e2..1b57c24 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -725,6 +725,19 @@
             long propCy, long propRadius, long propPaint);
 
     @Override
+    public void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
+            CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx,
+            CanvasProperty<Float> ry, CanvasProperty<Paint> paint) {
+        nDrawRoundRect(mRenderer, left.getNativeContainer(), top.getNativeContainer(),
+                right.getNativeContainer(), bottom.getNativeContainer(),
+                rx.getNativeContainer(), ry.getNativeContainer(),
+                paint.getNativeContainer());
+    }
+
+    private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
+            long propRight, long propBottom, long propRx, long propRy, long propPaint);
+
+    @Override
     public void drawColor(int color) {
         drawColor(color, PorterDuff.Mode.SRC_OVER);
     }
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index c172969..e3eee71 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -113,6 +113,11 @@
     public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
             CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
 
+    public abstract void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
+            CanvasProperty<Float> right, CanvasProperty<Float> bottom,
+            CanvasProperty<Float> rx, CanvasProperty<Float> ry,
+            CanvasProperty<Paint> paint);
+
     public static void setProperty(String name, String value) {
         GLES20Canvas.setProperty(name, value);
     }
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 6080f2a..2e2b23f 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -480,6 +480,20 @@
     renderer->drawRoundRect(left, top, right, bottom, rx, ry, paint);
 }
 
+static void android_view_GLES20Canvas_drawRoundRectProps(JNIEnv* env, jobject clazz,
+        jlong rendererPtr, jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr,
+        jlong bottomPropPtr, jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) {
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
+    CanvasPropertyPrimitive* leftProp = reinterpret_cast<CanvasPropertyPrimitive*>(leftPropPtr);
+    CanvasPropertyPrimitive* topProp = reinterpret_cast<CanvasPropertyPrimitive*>(topPropPtr);
+    CanvasPropertyPrimitive* rightProp = reinterpret_cast<CanvasPropertyPrimitive*>(rightPropPtr);
+    CanvasPropertyPrimitive* bottomProp = reinterpret_cast<CanvasPropertyPrimitive*>(bottomPropPtr);
+    CanvasPropertyPrimitive* rxProp = reinterpret_cast<CanvasPropertyPrimitive*>(rxPropPtr);
+    CanvasPropertyPrimitive* ryProp = reinterpret_cast<CanvasPropertyPrimitive*>(ryPropPtr);
+    CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
+    renderer->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp);
+}
+
 static void android_view_GLES20Canvas_drawCircle(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat x, jfloat y, jfloat radius, jlong paintPtr) {
     DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
@@ -912,6 +926,7 @@
     { "nDrawRect",          "(JFFFFJ)V",       (void*) android_view_GLES20Canvas_drawRect },
     { "nDrawRects",         "(JJJ)V",          (void*) android_view_GLES20Canvas_drawRegionAsRects },
     { "nDrawRoundRect",     "(JFFFFFFJ)V",     (void*) android_view_GLES20Canvas_drawRoundRect },
+    { "nDrawRoundRect",     "(JJJJJJJJ)V",     (void*) android_view_GLES20Canvas_drawRoundRectProps },
     { "nDrawCircle",        "(JFFFJ)V",        (void*) android_view_GLES20Canvas_drawCircle },
     { "nDrawCircle",        "(JJJJJ)V",        (void*) android_view_GLES20Canvas_drawCircleProps },
     { "nDrawOval",          "(JFFFFJ)V",       (void*) android_view_GLES20Canvas_drawOval },
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 5ff7b7f..cb3ef9b 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1105,6 +1105,34 @@
     float mRy;
 };
 
+class DrawRoundRectPropsOp : public DrawOp {
+public:
+    DrawRoundRectPropsOp(float* left, float* top, float* right, float* bottom,
+            float *rx, float *ry, const SkPaint* paint)
+            : DrawOp(paint), mLeft(left), mTop(top), mRight(right), mBottom(bottom),
+            mRx(rx), mRy(ry) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+        return renderer.drawRoundRect(*mLeft, *mTop, *mRight, *mBottom,
+                *mRx, *mRy, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t logFlags) const {
+        OP_LOG("Draw RoundRect Props " RECT_STRING ", rx %f, ry %f",
+                *mLeft, *mTop, *mRight, *mBottom, *mRx, *mRy);
+    }
+
+    virtual const char* name() { return "DrawRoundRectProps"; }
+
+private:
+    float* mLeft;
+    float* mTop;
+    float* mRight;
+    float* mBottom;
+    float* mRx;
+    float* mRy;
+};
+
 class DrawCircleOp : public DrawStrokableOp {
 public:
     DrawCircleOp(float x, float y, float radius, const SkPaint* paint)
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 7ee83e6..1f70921 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -275,6 +275,23 @@
     return DrawGlInfo::kStatusDone;
 }
 
+status_t DisplayListRenderer::drawRoundRect(
+        CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+        CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+        CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+        CanvasPropertyPaint* paint) {
+    mDisplayListData->refProperty(left);
+    mDisplayListData->refProperty(top);
+    mDisplayListData->refProperty(right);
+    mDisplayListData->refProperty(bottom);
+    mDisplayListData->refProperty(rx);
+    mDisplayListData->refProperty(ry);
+    mDisplayListData->refProperty(paint);
+    addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value,
+            &right->value, &bottom->value, &rx->value, &ry->value, &paint->value));
+    return DrawGlInfo::kStatusDone;
+}
+
 status_t DisplayListRenderer::drawCircle(float x, float y, float radius, const SkPaint* paint) {
     paint = refPaint(paint);
     addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, paint));
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index e9c937c..3a3fc3a 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -123,6 +123,10 @@
     virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
     virtual status_t drawRoundRect(float left, float top, float right, float bottom,
             float rx, float ry, const SkPaint* paint);
+    virtual status_t drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+                CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+                CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+                CanvasPropertyPaint* paint);
     virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint);
     virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
                 CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint);
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index c9e1618..47e24e8 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -1,8 +1,11 @@
 -keep class com.android.systemui.statusbar.policy.KeyButtonView {
   public float getDrawingAlpha();
+  public void setDrawingAlpha(float);
+}
+
+-keep class com.android.systemui.statusbar.policy.KeyButtonRipple {
   public float getGlowAlpha();
   public float getGlowScale();
-  public void setDrawingAlpha(float);
   public void setGlowAlpha(float);
   public void setGlowScale(float);
 }
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8fe0af8..37ee0ae 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -489,4 +489,7 @@
     <!-- Extra padding between the mobile data type icon and the strength indicator when the data
          type icon is wide for the tile in quick settings. -->
     <dimen name="wide_type_icon_start_padding_qs">3dp</dimen>
+
+    <!-- The maximum width of the navigation bar ripples. -->
+    <dimen name="key_button_ripple_max_width">95dp</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
new file mode 100644
index 0000000..a3765aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -0,0 +1,361 @@
+/*
+ * 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.policy;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.view.HardwareCanvas;
+import android.view.RenderNodeAnimator;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+public class KeyButtonRipple extends Drawable {
+
+    private static final float GLOW_MAX_SCALE_FACTOR = 1.35f;
+    private static final float GLOW_MAX_ALPHA = 0.2f;
+    private static final int ANIMATION_DURATION_SCALE = 350;
+    private static final int ANIMATION_DURATION_FADE = 450;
+
+    private Paint mRipplePaint;
+    private CanvasProperty<Float> mLeftProp;
+    private CanvasProperty<Float> mTopProp;
+    private CanvasProperty<Float> mRightProp;
+    private CanvasProperty<Float> mBottomProp;
+    private CanvasProperty<Float> mRxProp;
+    private CanvasProperty<Float> mRyProp;
+    private CanvasProperty<Paint> mPaintProp;
+    private float mGlowAlpha = 0f;
+    private float mGlowScale = 1f;
+    private boolean mPressed;
+    private boolean mDrawingHardwareGlow;
+    private int mMaxWidth;
+
+    private final Interpolator mInterpolator = new LogInterpolator();
+    private final Interpolator mAlphaExitInterpolator = PhoneStatusBar.ALPHA_OUT;
+    private boolean mSupportHardware;
+    private final View mTargetView;
+
+    private final HashSet<Animator> mRunningAnimations = new HashSet<>();
+    private final ArrayList<Animator> mTmpArray = new ArrayList<>();
+
+    public KeyButtonRipple(Context ctx, View targetView) {
+        mMaxWidth =  ctx.getResources().getDimensionPixelSize(R.dimen.key_button_ripple_max_width);
+        mTargetView = targetView;
+    }
+
+    private Paint getRipplePaint() {
+        if (mRipplePaint == null) {
+            mRipplePaint = new Paint();
+            mRipplePaint.setAntiAlias(true);
+            mRipplePaint.setColor(0xffffffff);
+        }
+        return mRipplePaint;
+    }
+
+    private void drawSoftware(Canvas canvas) {
+        if (mGlowAlpha > 0f) {
+            final Paint p = getRipplePaint();
+            p.setAlpha((int)(mGlowAlpha * 255f));
+
+            final float w = getBounds().width();
+            final float h = getBounds().height();
+            final boolean horizontal = w > h;
+            final float diameter = getRippleSize() * mGlowScale;
+            final float radius = diameter * .5f;
+            final float cx = w * .5f;
+            final float cy = h * .5f;
+            final float rx = horizontal ? radius : cx;
+            final float ry = horizontal ? cy : radius;
+            final float corner = horizontal ? cy : cx;
+
+            canvas.drawRoundRect(cx - rx, cy - ry,
+                    cx + rx, cy + ry,
+                    corner, corner, p);
+        }
+    }
+
+
+    @Override
+    public void draw(Canvas canvas) {
+        mSupportHardware = canvas.isHardwareAccelerated();
+        if (mSupportHardware) {
+            drawHardware((HardwareCanvas) canvas);
+        } else {
+            drawSoftware(canvas);
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        // Not supported.
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        // Not supported.
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    private boolean isHorizontal() {
+        return getBounds().width() > getBounds().height();
+    }
+
+    private void drawHardware(HardwareCanvas c) {
+        if (mDrawingHardwareGlow) {
+            c.drawRoundRect(mLeftProp, mTopProp, mRightProp, mBottomProp, mRxProp, mRyProp,
+                    mPaintProp);
+        }
+    }
+
+    public float getGlowAlpha() {
+        return mGlowAlpha;
+    }
+
+    public void setGlowAlpha(float x) {
+        mGlowAlpha = x;
+        invalidateSelf();
+    }
+
+    public float getGlowScale() {
+        return mGlowScale;
+    }
+
+    public void setGlowScale(float x) {
+        mGlowScale = x;
+        invalidateSelf();
+    }
+
+    @Override
+    protected boolean onStateChange(int[] state) {
+        boolean pressed = false;
+        for (int i = 0; i < state.length; i++) {
+            if (state[i] == android.R.attr.state_pressed) {
+                pressed = true;
+                break;
+            }
+        }
+        if (pressed != mPressed) {
+            setPressed(pressed);
+            mPressed = pressed;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isStateful() {
+        return true;
+    }
+
+    public void setPressed(boolean pressed) {
+        if (mSupportHardware) {
+            setPressedHardware(pressed);
+        } else {
+            setPressedSoftware(pressed);
+        }
+    }
+
+    private void cancelAnimations() {
+        mTmpArray.addAll(mRunningAnimations);
+        int size = mTmpArray.size();
+        for (int i = 0; i < size; i++) {
+            Animator a = mTmpArray.get(i);
+            a.cancel();
+        }
+        mTmpArray.clear();
+        mRunningAnimations.clear();
+    }
+
+    private void setPressedSoftware(boolean pressed) {
+        if (pressed) {
+            enterSoftware();
+        } else {
+            exitSoftware();
+        }
+    }
+
+    private void enterSoftware() {
+        cancelAnimations();
+        mGlowAlpha = GLOW_MAX_ALPHA;
+        ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
+                0f, GLOW_MAX_SCALE_FACTOR);
+        scaleAnimator.setInterpolator(mInterpolator);
+        scaleAnimator.setDuration(ANIMATION_DURATION_SCALE);
+        scaleAnimator.addListener(mAnimatorListener);
+        scaleAnimator.start();
+        mRunningAnimations.add(scaleAnimator);
+    }
+
+    private void exitSoftware() {
+        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, 0f);
+        alphaAnimator.setInterpolator(mAlphaExitInterpolator);
+        alphaAnimator.setDuration(ANIMATION_DURATION_FADE);
+        alphaAnimator.addListener(mAnimatorListener);
+        alphaAnimator.start();
+        mRunningAnimations.add(alphaAnimator);
+    }
+
+    private void setPressedHardware(boolean pressed) {
+        if (pressed) {
+            enterHardware();
+        } else {
+            exitHardware();
+        }
+    }
+
+    /**
+     * Sets the left/top property for the round rect to {@code prop} depending on whether we are
+     * horizontal or vertical mode.
+     */
+    private void setExtendStart(CanvasProperty<Float> prop) {
+        if (isHorizontal()) {
+            mLeftProp = prop;
+        } else {
+            mTopProp = prop;
+        }
+    }
+
+    private CanvasProperty<Float> getExtendStart() {
+        return isHorizontal() ? mLeftProp : mTopProp;
+    }
+
+    /**
+     * Sets the right/bottom property for the round rect to {@code prop} depending on whether we are
+     * horizontal or vertical mode.
+     */
+    private void setExtendEnd(CanvasProperty<Float> prop) {
+        if (isHorizontal()) {
+            mRightProp = prop;
+        } else {
+            mBottomProp = prop;
+        }
+    }
+
+    private CanvasProperty<Float> getExtendEnd() {
+        return isHorizontal() ? mRightProp : mBottomProp;
+    }
+
+    private int getExtendSize() {
+        return isHorizontal() ? getBounds().width() : getBounds().height();
+    }
+
+    private int getRippleSize() {
+        int size = isHorizontal() ? getBounds().width() : getBounds().height();
+        return Math.min(size, mMaxWidth);
+    }
+
+    private void enterHardware() {
+        cancelAnimations();
+        mDrawingHardwareGlow = true;
+        setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
+        final RenderNodeAnimator startAnim = new RenderNodeAnimator(getExtendStart(),
+                getExtendSize()/2 - GLOW_MAX_SCALE_FACTOR * getRippleSize()/2);
+        startAnim.setDuration(ANIMATION_DURATION_SCALE);
+        startAnim.setInterpolator(mInterpolator);
+        startAnim.addListener(mAnimatorListener);
+        startAnim.setTarget(mTargetView);
+
+        setExtendEnd(CanvasProperty.createFloat(getExtendSize() / 2));
+        final RenderNodeAnimator endAnim = new RenderNodeAnimator(getExtendEnd(),
+                getExtendSize()/2 + GLOW_MAX_SCALE_FACTOR * getRippleSize()/2);
+        endAnim.setDuration(ANIMATION_DURATION_SCALE);
+        endAnim.setInterpolator(mInterpolator);
+        endAnim.addListener(mAnimatorListener);
+        endAnim.setTarget(mTargetView);
+
+        if (isHorizontal()) {
+            mTopProp = CanvasProperty.createFloat(0f);
+            mBottomProp = CanvasProperty.createFloat(getBounds().height());
+            mRxProp = CanvasProperty.createFloat(getBounds().height()/2);
+            mRyProp = CanvasProperty.createFloat(getBounds().height()/2);
+        } else {
+            mLeftProp = CanvasProperty.createFloat(0f);
+            mRightProp = CanvasProperty.createFloat(getBounds().width());
+            mRxProp = CanvasProperty.createFloat(getBounds().width()/2);
+            mRyProp = CanvasProperty.createFloat(getBounds().width()/2);
+        }
+
+        mGlowScale = GLOW_MAX_SCALE_FACTOR;
+        mGlowAlpha = GLOW_MAX_ALPHA;
+        mRipplePaint = getRipplePaint();
+        mRipplePaint.setAlpha((int) (mGlowAlpha * 255));
+        mPaintProp = CanvasProperty.createPaint(mRipplePaint);
+
+        startAnim.start();
+        endAnim.start();
+        mRunningAnimations.add(startAnim);
+        mRunningAnimations.add(endAnim);
+
+        invalidateSelf();
+    }
+
+    private void exitHardware() {
+        mPaintProp = CanvasProperty.createPaint(getRipplePaint());
+        final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
+                RenderNodeAnimator.PAINT_ALPHA, 0);
+        opacityAnim.setDuration(ANIMATION_DURATION_FADE);
+        opacityAnim.setInterpolator(mAlphaExitInterpolator);
+        opacityAnim.addListener(mAnimatorListener);
+        opacityAnim.setTarget(mTargetView);
+
+        opacityAnim.start();
+        mRunningAnimations.add(opacityAnim);
+
+        invalidateSelf();
+    }
+
+    private final AnimatorListenerAdapter mAnimatorListener =
+            new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mRunningAnimations.remove(animation);
+            if (mRunningAnimations.isEmpty() && !mPressed) {
+                mDrawingHardwareGlow = false;
+                invalidateSelf();
+            }
+        }
+    };
+
+    /**
+     * Interpolator with a smooth log deceleration
+     */
+    private static final class LogInterpolator implements Interpolator {
+        @Override
+        public float getInterpolation(float input) {
+            return 1 - (float) Math.pow(400, -input * 1.4);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index b814b61..7cc75da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
 import android.graphics.Paint;
 import android.graphics.RectF;
 import android.hardware.input.InputManager;
@@ -32,11 +33,14 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.MathUtils;
 import android.view.HapticFeedbackConstants;
+import android.view.HardwareCanvas;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.RenderNodeAnimator;
 import android.view.SoundEffectConstants;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -44,6 +48,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ImageView;
 import java.lang.Math;
+import java.util.ArrayList;
 
 import com.android.systemui.R;
 
@@ -56,22 +61,15 @@
 
     // TODO: Get rid of this
     public static final float DEFAULT_QUIESCENT_ALPHA = 1f;
-    public static final float MAX_ALPHA = 0.15f;
-    public static final float GLOW_MAX_SCALE_FACTOR = 1.5f;
 
     private long mDownTime;
     private int mCode;
     private int mTouchSlop;
-    private float mGlowAlpha = 0f;
-    private float mGlowScale = 1f;
     private float mDrawingAlpha = 1f;
     private float mQuiescentAlpha = DEFAULT_QUIESCENT_ALPHA;
     private boolean mSupportsLongpress = true;
-    private AnimatorSet mPressedAnim;
-    private Animator mAnimateToQuiescent = new ObjectAnimator();
-    private Paint mRipplePaint;
-    private final TimeInterpolator mInterpolator = (TimeInterpolator) new LogInterpolator();
     private AudioManager mAudioManager;
+    private Animator mAnimateToQuiescent = new ObjectAnimator();
 
     private final Runnable mCheckLongPress = new Runnable() {
         public void run() {
@@ -110,6 +108,7 @@
         setClickable(true);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        setBackground(new KeyButtonRipple(context, this));
     }
 
     @Override
@@ -141,38 +140,6 @@
         return super.performAccessibilityAction(action, arguments);
     }
 
-    private Paint getRipplePaint() {
-        if (mRipplePaint == null) {
-            mRipplePaint = new Paint();
-            mRipplePaint.setAntiAlias(true);
-            mRipplePaint.setColor(0xffffffff);
-        }
-        return mRipplePaint;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        final Paint p = getRipplePaint();
-        p.setAlpha((int)(MAX_ALPHA * mDrawingAlpha * mGlowAlpha * 255));
-
-        final float w = getWidth();
-        final float h = getHeight();
-        final boolean horizontal = w > h;
-        final float diameter = (horizontal ? w : h) * mGlowScale;
-        final float radius = diameter * .5f;
-        final float cx = w * .5f;
-        final float cy = h * .5f;
-        final float rx = horizontal ? radius : cx;
-        final float ry = horizontal ? cy : radius;
-        final float corner = horizontal ? cy : cx;
-
-        canvas.drawRoundRect(cx - rx, cy - ry,
-                             cx + rx, cy + ry,
-                             corner, corner, p);
-
-        super.onDraw(canvas);
-    }
-
     public void setQuiescentAlpha(float alpha, boolean animate) {
         mAnimateToQuiescent.cancel();
         alpha = Math.min(Math.max(alpha, 0), 1);
@@ -204,76 +171,6 @@
         mDrawingAlpha = x;
     }
 
-    public float getGlowAlpha() {
-        return mGlowAlpha;
-    }
-
-    public void setGlowAlpha(float x) {
-        mGlowAlpha = x;
-        invalidate();
-    }
-
-    public float getGlowScale() {
-        return mGlowScale;
-    }
-
-    public void setGlowScale(float x) {
-        mGlowScale = x;
-        final float w = getWidth();
-        final float h = getHeight();
-        if (GLOW_MAX_SCALE_FACTOR <= 1.0f) {
-            // this only works if we know the glow will never leave our bounds
-            invalidate();
-        } else {
-            final float rx = (w * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
-            final float ry = (h * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
-            com.android.systemui.SwipeHelper.invalidateGlobalRegion(
-                    this,
-                    new RectF(getLeft() - rx,
-                              getTop() - ry,
-                              getRight() + rx,
-                              getBottom() + ry));
-
-            // also invalidate our immediate parent to help avoid situations where nearby glows
-            // interfere
-            ((View)getParent()).invalidate();
-        }
-    }
-
-    public void setPressed(boolean pressed) {
-        if (pressed != isPressed()) {
-            if (mPressedAnim != null && mPressedAnim.isRunning()) {
-                mPressedAnim.cancel();
-            }
-            final AnimatorSet as = mPressedAnim = new AnimatorSet();
-            final ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this,
-                    "glowScale", GLOW_MAX_SCALE_FACTOR);
-            scaleAnimator.setInterpolator(mInterpolator);
-            if (pressed) {
-                mGlowScale = 0f;
-                if (mGlowAlpha < mQuiescentAlpha)
-                    mGlowAlpha = mQuiescentAlpha;
-                setDrawingAlpha(1f);
-                as.playTogether(
-                    ObjectAnimator.ofFloat(this, "glowAlpha", 1f),
-                    scaleAnimator
-                );
-                as.setDuration(500);
-            } else {
-                mAnimateToQuiescent.cancel();
-                mAnimateToQuiescent = animateToQuiescent();
-                as.playTogether(
-                    ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, mGlowAlpha * .2f, 0f),
-                    scaleAnimator,
-                    mAnimateToQuiescent
-                );
-                as.setDuration(500);
-            }
-            as.start();
-        }
-        super.setPressed(pressed);
-    }
-
     public boolean onTouchEvent(MotionEvent ev) {
         final int action = ev.getAction();
         int x, y;
@@ -354,17 +251,6 @@
         InputManager.getInstance().injectInputEvent(ev,
                 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
     }
-
-    /**
-    * Interpolator with a smooth log deceleration
-    */
-    private static final class LogInterpolator implements TimeInterpolator {
-        @Override
-        public float getInterpolation(float input) {
-            return 1 - (float) Math.pow(400, -input * 1.4);
-        }
-    }
-
 }