summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorJorim Jaggi <jjaggi@google.com>2014-09-15 17:20:08 +0200
committerJorim Jaggi <jjaggi@google.com>2014-09-16 17:51:55 +0000
commit072707dfad1da6f49f4d3ce58ca104f6c46a7266 (patch)
treebafc802d2d47b2b3aed527e701b11fa36057ae1d /packages
parent9ed2d5f7460973409e44018f5fa7b3456db1e658 (diff)
downloadframeworks_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')
-rw-r--r--packages/SystemUI/proguard.flags5
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java361
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java128
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);
- }
- }
-
}