diff options
author | Jorim Jaggi <jjaggi@google.com> | 2014-09-15 17:20:08 +0200 |
---|---|---|
committer | Jorim Jaggi <jjaggi@google.com> | 2014-09-16 17:51:55 +0000 |
commit | 072707dfad1da6f49f4d3ce58ca104f6c46a7266 (patch) | |
tree | bafc802d2d47b2b3aed527e701b11fa36057ae1d /packages | |
parent | 9ed2d5f7460973409e44018f5fa7b3456db1e658 (diff) | |
download | frameworks_base-072707dfad1da6f49f4d3ce58ca104f6c46a7266.zip frameworks_base-072707dfad1da6f49f4d3ce58ca104f6c46a7266.tar.gz frameworks_base-072707dfad1da6f49f4d3ce58ca104f6c46a7266.tar.bz2 |
Use RenderThread for navigation bar ripples
Bug: 17506181
Change-Id: Icf3b80f8c4bc29fe85313381d4019dda3ef85ea9
Diffstat (limited to 'packages')
4 files changed, 375 insertions, 122 deletions
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.app.ActivityManager; 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.Bundle; 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.AccessibilityEvent; 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 @@ public class KeyButtonView extends ImageView { // 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 @@ public class KeyButtonView extends ImageView { setClickable(true); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + setBackground(new KeyButtonRipple(context, this)); } @Override @@ -141,38 +140,6 @@ public class KeyButtonView extends ImageView { 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 @@ public class KeyButtonView extends ImageView { 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 @@ public class KeyButtonView extends ImageView { 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); - } - } - } |