Loading packages/SystemUI/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ android-support-v7-mediarouter \ android-support-v7-palette \ android-support-v14-preference \ android-support-v17-leanback android-support-v17-leanback \ colorextraction LOCAL_STATIC_JAVA_LIBRARIES := \ SystemUI-tags \ Loading packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +158 −55 Original line number Diff line number Diff line Loading @@ -19,39 +19,52 @@ package com.android.systemui.statusbar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; import android.view.animation.Interpolator; import com.android.systemui.R; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.statusbar.policy.ConfigurationController; import com.google.android.colorextraction.ColorExtractor; import com.google.android.colorextraction.drawable.GradientDrawable; /** * A view which can draw a scrim */ public class ScrimView extends View { private final Paint mPaint = new Paint(); private int mScrimColor; private boolean mIsEmpty = true; public class ScrimView extends View implements ConfigurationController.ConfigurationListener { private static final String TAG = "ScrimView"; private final ColorExtractor.GradientColors mColors; private boolean mDrawAsSrc; private float mViewAlpha = 1.0f; private ValueAnimator mAlphaAnimator; private Rect mExcludedRect = new Rect(); private boolean mHasExcludedArea; private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mViewAlpha = (float) animation.getAnimatedValue(); invalidate(); private Drawable mDrawable; private PorterDuffColorFilter mColorFilter; private int mTintColor; private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener = animation -> { if (mDrawable == null) { Log.w(TAG, "Trying to animate null drawable"); return; } mDrawable.setAlpha((int) (255 * (float) animation.getAnimatedValue())); }; private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() { @Override Loading @@ -76,72 +89,134 @@ public class ScrimView extends View public ScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ScrimView); mDrawable = new GradientDrawable(context); mDrawable.setCallback(this); mColors = new ColorExtractor.GradientColors(); updateScreenSize(); try { mScrimColor = ta.getColor(R.styleable.ScrimView_scrimColor, Color.BLACK); } finally { ta.recycle(); } // We need to know about configuration changes to update the gradient size // since it's independent from view bounds. ConfigurationController config = Dependency.get(ConfigurationController.class); config.addCallback(this); } @Override protected void onDraw(Canvas canvas) { if (mDrawAsSrc || (!mIsEmpty && mViewAlpha > 0f)) { PorterDuff.Mode mode = mDrawAsSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER; int color = getScrimColorWithAlpha(); if (mDrawAsSrc || mDrawable.getAlpha() > 0) { if (!mHasExcludedArea) { canvas.drawColor(color, mode); mDrawable.draw(canvas); } else { mPaint.setColor(color); if (mExcludedRect.top > 0) { canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint); canvas.save(); canvas.clipRect(0, 0, getWidth(), mExcludedRect.top); mDrawable.draw(canvas); canvas.restore(); } if (mExcludedRect.left > 0) { canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom, mPaint); canvas.save(); canvas.clipRect(0, mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom); mDrawable.draw(canvas); canvas.restore(); } if (mExcludedRect.right < getWidth()) { canvas.drawRect(mExcludedRect.right, mExcludedRect.top, getWidth(), mExcludedRect.bottom, mPaint); canvas.save(); canvas.clipRect(mExcludedRect.right, mExcludedRect.top, getWidth(), mExcludedRect.bottom); mDrawable.draw(canvas); canvas.restore(); } if (mExcludedRect.bottom < getHeight()) { canvas.drawRect(0, mExcludedRect.bottom, getWidth(), getHeight(), mPaint); canvas.save(); canvas.clipRect(0, mExcludedRect.bottom, getWidth(), getHeight()); mDrawable.draw(canvas); canvas.restore(); } } } } public int getScrimColorWithAlpha() { int color = mScrimColor; color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color), Color.green(color), Color.blue(color)); return color; public void setDrawable(Drawable drawable) { mDrawable = drawable; mDrawable.setCallback(this); mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom()); invalidate(); } @Override public void invalidateDrawable(@NonNull Drawable drawable) { super.invalidateDrawable(drawable); if (drawable == mDrawable) { invalidate(); } } public void setDrawAsSrc(boolean asSrc) { mDrawAsSrc = asSrc; mPaint.setXfermode(new PorterDuffXfermode(mDrawAsSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER)); invalidate(); PorterDuff.Mode mode = asSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER; mDrawable.setXfermode(new PorterDuffXfermode(mode)); } public void setScrimColor(int color) { if (color != mScrimColor) { mIsEmpty = Color.alpha(color) == 0; mScrimColor = color; @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (changed) { mDrawable.setBounds(left, top, right, bottom); invalidate(); } } public void setColors(@NonNull ColorExtractor.GradientColors colors) { if (colors == null) { throw new IllegalArgumentException("Colors cannot be null"); } if (mColors.equals(colors)) { return; } mColors.set(colors); updateColorWithTint(); } @VisibleForTesting Drawable getDrawable() { return mDrawable; } public void setTint(int color) { if (mTintColor == color) { return; } mTintColor = color; updateColorWithTint(); } private void updateColorWithTint() { if (mDrawable instanceof GradientDrawable) { // Optimization to blend colors and avoid a color filter GradientDrawable drawable = (GradientDrawable) mDrawable; float tintAmount = Color.alpha(mTintColor) / 255f; int mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount); int secondaryTinted = ColorUtils.blendARGB(mColors.getSecondaryColor(), mTintColor, tintAmount); drawable.setColors(mainTinted, secondaryTinted, false); } else { if (mColorFilter == null) { mColorFilter = new PorterDuffColorFilter(mTintColor, PorterDuff.Mode.SRC_OVER); } else { mColorFilter.setColor(mTintColor); } mDrawable.setColorFilter(Color.alpha(mTintColor) == 0 ? null : mColorFilter); mDrawable.invalidateSelf(); } if (mChangeRunnable != null) { mChangeRunnable.run(); } } } public int getScrimColor() { return mScrimColor; public int getTint() { return mTintColor; } @Override Loading @@ -150,23 +225,30 @@ public class ScrimView extends View } public void setViewAlpha(float alpha) { if (alpha != mViewAlpha) { mViewAlpha = alpha; if (mAlphaAnimator != null) { mAlphaAnimator.cancel(); } if (alpha != mViewAlpha) { mViewAlpha = alpha; invalidate(); mDrawable.setAlpha((int) (255 * alpha)); if (mChangeRunnable != null) { mChangeRunnable.run(); } } } @VisibleForTesting float getViewAlpha() { return mViewAlpha; } public void animateViewAlpha(float alpha, long durationOut, Interpolator interpolator) { if (mAlphaAnimator != null) { mAlphaAnimator.cancel(); } mAlphaAnimator = ValueAnimator.ofFloat(mViewAlpha, alpha); mAlphaAnimator = ValueAnimator.ofFloat(getViewAlpha(), alpha); mAlphaAnimator.addUpdateListener(mAlphaUpdateListener); mAlphaAnimator.addListener(mClearAnimatorListener); mAlphaAnimator.setInterpolator(interpolator); Loading @@ -193,4 +275,25 @@ public class ScrimView extends View public void setChangeRunnable(Runnable changeRunnable) { mChangeRunnable = changeRunnable; } @Override public void onConfigChanged(Configuration newConfig) { updateScreenSize(); } private void updateScreenSize() { if (mDrawable instanceof GradientDrawable) { WindowManager wm = mContext.getSystemService(WindowManager.class); if (wm == null) { Log.w(TAG, "Can't resize gradient drawable to fit the screen"); return; } Display display = wm.getDefaultDisplay(); if (display != null) { Point size = new Point(); display.getRealSize(size); ((GradientDrawable) mDrawable).setScreenSize(size.x, size.y); } } } } packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +96 −41 Original line number Diff line number Diff line Loading @@ -20,9 +20,12 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.app.WallpaperManager; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.support.v4.graphics.ColorUtils; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; Loading @@ -30,6 +33,7 @@ import android.view.ViewTreeObserver; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; Loading @@ -38,12 +42,14 @@ import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.stack.ViewState; import com.google.android.colorextraction.ColorExtractor; /** * Controls both the scrim behind the notifications and in front of the notifications (when a * security method gets shown). */ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnHeadsUpChangedListener { OnHeadsUpChangedListener, ColorExtractor.OnColorsChangedListener { public static final long ANIMATION_DURATION = 220; public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR = new PathInterpolator(0f, 0, 0.7f, 1f); Loading @@ -61,11 +67,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private final LightBarController mLightBarController; protected final ScrimView mScrimBehind; private final ScrimView mScrimInFront; protected final ScrimView mScrimInFront; private final UnlockMethodCache mUnlockMethodCache; private final View mHeadsUpScrim; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final ColorExtractor mColorExtractor; private ColorExtractor.GradientColors mLockColors; private ColorExtractor.GradientColors mSystemColors; private boolean mNeedsDrawableColorUpdate; protected float mScrimBehindAlpha; protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD; protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING; Loading Loading @@ -98,6 +109,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private boolean mSkipFirstFrame; private boolean mDontAnimateBouncerChanges; private boolean mKeyguardFadingOutInProgress; private boolean mAnimatingDozeUnlock; private ValueAnimator mKeyguardFadeoutAnimation; public ScrimController(LightBarController lightBarController, ScrimView scrimBehind, Loading @@ -111,12 +123,22 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mLightBarController = lightBarController; mScrimBehindAlpha = context.getResources().getFloat(R.dimen.scrim_behind_alpha); mColorExtractor = new ColorExtractor(context); mColorExtractor.setListener(this); mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK); mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM); mNeedsDrawableColorUpdate = true; updateHeadsUpScrim(false); updateScrims(); } public void setKeyguardShowing(boolean showing) { mKeyguardShowing = showing; // Showing/hiding the keyguard means that scrim colors // will probably have to be switched mNeedsDrawableColorUpdate = true; scheduleUpdate(); } Loading Loading @@ -157,6 +179,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public void setWakeAndUnlocking() { mWakeAndUnlocking = true; mAnimatingDozeUnlock = true; scheduleUpdate(); } Loading Loading @@ -192,7 +215,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public void animateKeyguardUnoccluding(long duration) { mAnimateChange = false; setScrimBehindColor(0f); setScrimBehindAlpha(0f); mAnimateChange = true; scheduleUpdate(); mDurationOverride = duration; Loading Loading @@ -246,23 +269,35 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } protected void updateScrims() { // Make sure we have the right gradients if (mNeedsDrawableColorUpdate) { mNeedsDrawableColorUpdate = false; if (mKeyguardShowing) { mScrimInFront.setColors(mLockColors); mScrimBehind.setColors(mLockColors); } else { mScrimInFront.setColors(mSystemColors); mScrimBehind.setColors(mSystemColors); } } if (mAnimateKeyguardFadingOut || mForceHideScrims) { setScrimInFrontColor(0f); setScrimBehindColor(0f); setScrimInFrontAlpha(0f); setScrimBehindAlpha(0f); } else if (mWakeAndUnlocking) { // During wake and unlock, we first hide everything behind a black scrim, which then // gets faded out from animateKeyguardFadingOut. if (mDozing) { setScrimInFrontColor(0f); setScrimBehindColor(1f); setScrimInFrontAlpha(0f); setScrimBehindAlpha(1f); } else { setScrimInFrontColor(1f); setScrimBehindColor(0f); setScrimInFrontAlpha(1f); setScrimBehindAlpha(0f); } } else if (!mKeyguardShowing && !mBouncerShowing) { updateScrimNormal(); setScrimInFrontColor(0); setScrimInFrontAlpha(0); } else { updateScrimKeyguard(); } Loading @@ -275,18 +310,18 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, float fraction = 1 - behindFraction; fraction = (float) Math.pow(fraction, 0.8f); behindFraction = (float) Math.pow(behindFraction, 0.8f); setScrimInFrontColor(fraction * getScrimInFrontAlpha()); setScrimBehindColor(behindFraction * mScrimBehindAlphaKeyguard); setScrimInFrontAlpha(fraction * getScrimInFrontAlpha()); setScrimBehindAlpha(behindFraction * mScrimBehindAlphaKeyguard); } else if (mBouncerShowing && !mBouncerIsKeyguard) { setScrimInFrontColor(getScrimInFrontAlpha()); setScrimInFrontAlpha(getScrimInFrontAlpha()); updateScrimNormal(); } else if (mBouncerShowing) { setScrimInFrontColor(0f); setScrimBehindColor(mScrimBehindAlpha); setScrimInFrontAlpha(0f); setScrimBehindAlpha(mScrimBehindAlpha); } else { float fraction = Math.max(0, Math.min(mFraction, 1)); setScrimInFrontColor(0f); setScrimBehindColor(fraction setScrimInFrontAlpha(0f); setScrimBehindAlpha(fraction * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking) + mScrimBehindAlphaUnlocking); } Loading @@ -297,30 +332,29 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, // let's start this 20% of the way down the screen frac = frac * 1.2f - 0.2f; if (frac <= 0) { setScrimBehindColor(0); setScrimBehindAlpha(0); } else { // woo, special effects final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); setScrimBehindColor(k * mScrimBehindAlpha); setScrimBehindAlpha(k * mScrimBehindAlpha); } } private void setScrimBehindColor(float alpha) { setScrimColor(mScrimBehind, alpha); private void setScrimBehindAlpha(float alpha) { setScrimAlpha(mScrimBehind, alpha); } private void setScrimInFrontColor(float alpha) { setScrimColor(mScrimInFront, alpha); private void setScrimInFrontAlpha(float alpha) { setScrimAlpha(mScrimInFront, alpha); if (alpha == 0f) { mScrimInFront.setClickable(false); } else { // Eat touch events (unless dozing). mScrimInFront.setClickable(!mDozing); } } private void setScrimColor(View scrim, float alpha) { private void setScrimAlpha(View scrim, float alpha) { updateScrim(mAnimateChange, scrim, alpha, getCurrentScrimAlpha(scrim)); } Loading @@ -346,15 +380,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } } protected void updateScrimColor(View scrim) { private void updateScrimColor(View scrim) { float alpha1 = getCurrentScrimAlpha(scrim); if (scrim instanceof ScrimView) { float alpha2 = getDozeAlpha(scrim); float alpha = 1 - (1 - alpha1) * (1 - alpha2); ScrimView scrimView = (ScrimView) scrim; float dozeAlpha = getDozeAlpha(scrim); float alpha = 1 - (1 - alpha1) * (1 - dozeAlpha); alpha = Math.max(0, Math.min(1.0f, alpha)); int baseColor = ((ScrimView) scrim).getScrimColor(); ((ScrimView) scrim).setScrimColor( ColorUtils.setAlphaComponent(baseColor, (int) (alpha * 255))); scrimView.setViewAlpha(alpha); int dozeTint = Color.TRANSPARENT; if (mAnimatingDozeUnlock || mDozing) { dozeTint = Color.BLACK; } scrimView.setTint(dozeTint); } else { scrim.setAlpha(alpha1); } Loading @@ -363,13 +402,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private void startScrimAnimation(final View scrim, float target) { float current = getCurrentScrimAlpha(scrim); ValueAnimator anim = ValueAnimator.ofFloat(current, target); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { anim.addUpdateListener(animation -> { float alpha = (float) animation.getAnimatedValue(); setCurrentScrimAlpha(scrim, alpha); updateScrimColor(scrim); } }); anim.setInterpolator(getInterpolator()); anim.setStartDelay(mAnimationDelay); Loading @@ -384,6 +420,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, if (mKeyguardFadingOutInProgress) { mKeyguardFadeoutAnimation = null; mKeyguardFadingOutInProgress = false; mAnimatingDozeUnlock = false; } scrim.setTag(TAG_KEY_ANIM, null); scrim.setTag(TAG_KEY_ANIM_TARGET, null); Loading Loading @@ -436,6 +473,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mOnAnimationFinished = null; } mKeyguardFadingOutInProgress = false; if (!mWakeAndUnlocking || force) mAnimatingDozeUnlock = false; } } Loading Loading @@ -559,8 +598,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mScrimBehind.setExcludedArea(area); } public int getScrimBehindColor() { return mScrimBehind.getScrimColorWithAlpha(); public int getBackgroundColor() { int color = mLockColors.getMainColor(); return Color.argb((int) (mScrimBehind.getAlpha() * Color.alpha(color)), Color.red(color), Color.green(color), Color.blue(color)); } public void setScrimBehindChangeRunnable(Runnable changeRunnable) { Loading @@ -577,4 +618,18 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public void setCurrentUser(int currentUser) { // Don't care in the base class. } @Override public void onColorsChanged(ColorExtractor.GradientColors colors, int which) { if ((which & WallpaperManager.FLAG_LOCK) != 0) { mLockColors = colors; mNeedsDrawableColorUpdate = true; scheduleUpdate(); } if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { mSystemColors = colors; mNeedsDrawableColorUpdate = true; scheduleUpdate(); } } } packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +6 −16 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.OverScroller; import android.widget.ScrollView; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.ExpandHelper; Loading Loading @@ -87,6 +88,8 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ScrollAdapter; import android.support.v4.graphics.ColorUtils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; Loading Loading @@ -477,16 +480,8 @@ public class NotificationStackScrollLayout extends ViewGroup float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount); alpha *= mBackgroundFadeAmount; // We need to manually blend in the background color int scrimColor = mScrimController.getScrimBehindColor(); // SRC_OVER blending Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc float alphaInv = 1 - alpha; int color = Color.argb((int) (alpha * 255 + alphaInv * Color.alpha(scrimColor)), (int) (mBackgroundFadeAmount * Color.red(mBgColor) + alphaInv * Color.red(scrimColor)), (int) (mBackgroundFadeAmount * Color.green(mBgColor) + alphaInv * Color.green(scrimColor)), (int) (mBackgroundFadeAmount * Color.blue(mBgColor) + alphaInv * Color.blue(scrimColor))); int scrimColor = mScrimController.getBackgroundColor(); int color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha); if (mCachedBackgroundColor != color) { mCachedBackgroundColor = color; mBackgroundPaint.setColor(color); Loading Loading @@ -4059,12 +4054,7 @@ public class NotificationStackScrollLayout extends ViewGroup public void setScrimController(ScrimController scrimController) { mScrimController = scrimController; mScrimController.setScrimBehindChangeRunnable(new Runnable() { @Override public void run() { updateBackgroundDimming(); } }); mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming); } public void forceNoOverlappingRendering(boolean force) { Loading packages/SystemUI/tests/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -44,7 +44,8 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ android-support-v7-mediarouter \ android-support-v7-palette \ android-support-v14-preference \ android-support-v17-leanback android-support-v17-leanback \ colorextraction LOCAL_STATIC_JAVA_LIBRARIES := \ metrics-helper-lib \ Loading Loading
packages/SystemUI/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ android-support-v7-mediarouter \ android-support-v7-palette \ android-support-v14-preference \ android-support-v17-leanback android-support-v17-leanback \ colorextraction LOCAL_STATIC_JAVA_LIBRARIES := \ SystemUI-tags \ Loading
packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +158 −55 Original line number Diff line number Diff line Loading @@ -19,39 +19,52 @@ package com.android.systemui.statusbar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; import android.view.animation.Interpolator; import com.android.systemui.R; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.statusbar.policy.ConfigurationController; import com.google.android.colorextraction.ColorExtractor; import com.google.android.colorextraction.drawable.GradientDrawable; /** * A view which can draw a scrim */ public class ScrimView extends View { private final Paint mPaint = new Paint(); private int mScrimColor; private boolean mIsEmpty = true; public class ScrimView extends View implements ConfigurationController.ConfigurationListener { private static final String TAG = "ScrimView"; private final ColorExtractor.GradientColors mColors; private boolean mDrawAsSrc; private float mViewAlpha = 1.0f; private ValueAnimator mAlphaAnimator; private Rect mExcludedRect = new Rect(); private boolean mHasExcludedArea; private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mViewAlpha = (float) animation.getAnimatedValue(); invalidate(); private Drawable mDrawable; private PorterDuffColorFilter mColorFilter; private int mTintColor; private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener = animation -> { if (mDrawable == null) { Log.w(TAG, "Trying to animate null drawable"); return; } mDrawable.setAlpha((int) (255 * (float) animation.getAnimatedValue())); }; private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() { @Override Loading @@ -76,72 +89,134 @@ public class ScrimView extends View public ScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ScrimView); mDrawable = new GradientDrawable(context); mDrawable.setCallback(this); mColors = new ColorExtractor.GradientColors(); updateScreenSize(); try { mScrimColor = ta.getColor(R.styleable.ScrimView_scrimColor, Color.BLACK); } finally { ta.recycle(); } // We need to know about configuration changes to update the gradient size // since it's independent from view bounds. ConfigurationController config = Dependency.get(ConfigurationController.class); config.addCallback(this); } @Override protected void onDraw(Canvas canvas) { if (mDrawAsSrc || (!mIsEmpty && mViewAlpha > 0f)) { PorterDuff.Mode mode = mDrawAsSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER; int color = getScrimColorWithAlpha(); if (mDrawAsSrc || mDrawable.getAlpha() > 0) { if (!mHasExcludedArea) { canvas.drawColor(color, mode); mDrawable.draw(canvas); } else { mPaint.setColor(color); if (mExcludedRect.top > 0) { canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint); canvas.save(); canvas.clipRect(0, 0, getWidth(), mExcludedRect.top); mDrawable.draw(canvas); canvas.restore(); } if (mExcludedRect.left > 0) { canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom, mPaint); canvas.save(); canvas.clipRect(0, mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom); mDrawable.draw(canvas); canvas.restore(); } if (mExcludedRect.right < getWidth()) { canvas.drawRect(mExcludedRect.right, mExcludedRect.top, getWidth(), mExcludedRect.bottom, mPaint); canvas.save(); canvas.clipRect(mExcludedRect.right, mExcludedRect.top, getWidth(), mExcludedRect.bottom); mDrawable.draw(canvas); canvas.restore(); } if (mExcludedRect.bottom < getHeight()) { canvas.drawRect(0, mExcludedRect.bottom, getWidth(), getHeight(), mPaint); canvas.save(); canvas.clipRect(0, mExcludedRect.bottom, getWidth(), getHeight()); mDrawable.draw(canvas); canvas.restore(); } } } } public int getScrimColorWithAlpha() { int color = mScrimColor; color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color), Color.green(color), Color.blue(color)); return color; public void setDrawable(Drawable drawable) { mDrawable = drawable; mDrawable.setCallback(this); mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom()); invalidate(); } @Override public void invalidateDrawable(@NonNull Drawable drawable) { super.invalidateDrawable(drawable); if (drawable == mDrawable) { invalidate(); } } public void setDrawAsSrc(boolean asSrc) { mDrawAsSrc = asSrc; mPaint.setXfermode(new PorterDuffXfermode(mDrawAsSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER)); invalidate(); PorterDuff.Mode mode = asSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER; mDrawable.setXfermode(new PorterDuffXfermode(mode)); } public void setScrimColor(int color) { if (color != mScrimColor) { mIsEmpty = Color.alpha(color) == 0; mScrimColor = color; @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (changed) { mDrawable.setBounds(left, top, right, bottom); invalidate(); } } public void setColors(@NonNull ColorExtractor.GradientColors colors) { if (colors == null) { throw new IllegalArgumentException("Colors cannot be null"); } if (mColors.equals(colors)) { return; } mColors.set(colors); updateColorWithTint(); } @VisibleForTesting Drawable getDrawable() { return mDrawable; } public void setTint(int color) { if (mTintColor == color) { return; } mTintColor = color; updateColorWithTint(); } private void updateColorWithTint() { if (mDrawable instanceof GradientDrawable) { // Optimization to blend colors and avoid a color filter GradientDrawable drawable = (GradientDrawable) mDrawable; float tintAmount = Color.alpha(mTintColor) / 255f; int mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount); int secondaryTinted = ColorUtils.blendARGB(mColors.getSecondaryColor(), mTintColor, tintAmount); drawable.setColors(mainTinted, secondaryTinted, false); } else { if (mColorFilter == null) { mColorFilter = new PorterDuffColorFilter(mTintColor, PorterDuff.Mode.SRC_OVER); } else { mColorFilter.setColor(mTintColor); } mDrawable.setColorFilter(Color.alpha(mTintColor) == 0 ? null : mColorFilter); mDrawable.invalidateSelf(); } if (mChangeRunnable != null) { mChangeRunnable.run(); } } } public int getScrimColor() { return mScrimColor; public int getTint() { return mTintColor; } @Override Loading @@ -150,23 +225,30 @@ public class ScrimView extends View } public void setViewAlpha(float alpha) { if (alpha != mViewAlpha) { mViewAlpha = alpha; if (mAlphaAnimator != null) { mAlphaAnimator.cancel(); } if (alpha != mViewAlpha) { mViewAlpha = alpha; invalidate(); mDrawable.setAlpha((int) (255 * alpha)); if (mChangeRunnable != null) { mChangeRunnable.run(); } } } @VisibleForTesting float getViewAlpha() { return mViewAlpha; } public void animateViewAlpha(float alpha, long durationOut, Interpolator interpolator) { if (mAlphaAnimator != null) { mAlphaAnimator.cancel(); } mAlphaAnimator = ValueAnimator.ofFloat(mViewAlpha, alpha); mAlphaAnimator = ValueAnimator.ofFloat(getViewAlpha(), alpha); mAlphaAnimator.addUpdateListener(mAlphaUpdateListener); mAlphaAnimator.addListener(mClearAnimatorListener); mAlphaAnimator.setInterpolator(interpolator); Loading @@ -193,4 +275,25 @@ public class ScrimView extends View public void setChangeRunnable(Runnable changeRunnable) { mChangeRunnable = changeRunnable; } @Override public void onConfigChanged(Configuration newConfig) { updateScreenSize(); } private void updateScreenSize() { if (mDrawable instanceof GradientDrawable) { WindowManager wm = mContext.getSystemService(WindowManager.class); if (wm == null) { Log.w(TAG, "Can't resize gradient drawable to fit the screen"); return; } Display display = wm.getDefaultDisplay(); if (display != null) { Point size = new Point(); display.getRealSize(size); ((GradientDrawable) mDrawable).setScreenSize(size.x, size.y); } } } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +96 −41 Original line number Diff line number Diff line Loading @@ -20,9 +20,12 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.app.WallpaperManager; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.support.v4.graphics.ColorUtils; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; Loading @@ -30,6 +33,7 @@ import android.view.ViewTreeObserver; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; Loading @@ -38,12 +42,14 @@ import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.stack.ViewState; import com.google.android.colorextraction.ColorExtractor; /** * Controls both the scrim behind the notifications and in front of the notifications (when a * security method gets shown). */ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnHeadsUpChangedListener { OnHeadsUpChangedListener, ColorExtractor.OnColorsChangedListener { public static final long ANIMATION_DURATION = 220; public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR = new PathInterpolator(0f, 0, 0.7f, 1f); Loading @@ -61,11 +67,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private final LightBarController mLightBarController; protected final ScrimView mScrimBehind; private final ScrimView mScrimInFront; protected final ScrimView mScrimInFront; private final UnlockMethodCache mUnlockMethodCache; private final View mHeadsUpScrim; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final ColorExtractor mColorExtractor; private ColorExtractor.GradientColors mLockColors; private ColorExtractor.GradientColors mSystemColors; private boolean mNeedsDrawableColorUpdate; protected float mScrimBehindAlpha; protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD; protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING; Loading Loading @@ -98,6 +109,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private boolean mSkipFirstFrame; private boolean mDontAnimateBouncerChanges; private boolean mKeyguardFadingOutInProgress; private boolean mAnimatingDozeUnlock; private ValueAnimator mKeyguardFadeoutAnimation; public ScrimController(LightBarController lightBarController, ScrimView scrimBehind, Loading @@ -111,12 +123,22 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mLightBarController = lightBarController; mScrimBehindAlpha = context.getResources().getFloat(R.dimen.scrim_behind_alpha); mColorExtractor = new ColorExtractor(context); mColorExtractor.setListener(this); mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK); mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM); mNeedsDrawableColorUpdate = true; updateHeadsUpScrim(false); updateScrims(); } public void setKeyguardShowing(boolean showing) { mKeyguardShowing = showing; // Showing/hiding the keyguard means that scrim colors // will probably have to be switched mNeedsDrawableColorUpdate = true; scheduleUpdate(); } Loading Loading @@ -157,6 +179,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public void setWakeAndUnlocking() { mWakeAndUnlocking = true; mAnimatingDozeUnlock = true; scheduleUpdate(); } Loading Loading @@ -192,7 +215,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public void animateKeyguardUnoccluding(long duration) { mAnimateChange = false; setScrimBehindColor(0f); setScrimBehindAlpha(0f); mAnimateChange = true; scheduleUpdate(); mDurationOverride = duration; Loading Loading @@ -246,23 +269,35 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } protected void updateScrims() { // Make sure we have the right gradients if (mNeedsDrawableColorUpdate) { mNeedsDrawableColorUpdate = false; if (mKeyguardShowing) { mScrimInFront.setColors(mLockColors); mScrimBehind.setColors(mLockColors); } else { mScrimInFront.setColors(mSystemColors); mScrimBehind.setColors(mSystemColors); } } if (mAnimateKeyguardFadingOut || mForceHideScrims) { setScrimInFrontColor(0f); setScrimBehindColor(0f); setScrimInFrontAlpha(0f); setScrimBehindAlpha(0f); } else if (mWakeAndUnlocking) { // During wake and unlock, we first hide everything behind a black scrim, which then // gets faded out from animateKeyguardFadingOut. if (mDozing) { setScrimInFrontColor(0f); setScrimBehindColor(1f); setScrimInFrontAlpha(0f); setScrimBehindAlpha(1f); } else { setScrimInFrontColor(1f); setScrimBehindColor(0f); setScrimInFrontAlpha(1f); setScrimBehindAlpha(0f); } } else if (!mKeyguardShowing && !mBouncerShowing) { updateScrimNormal(); setScrimInFrontColor(0); setScrimInFrontAlpha(0); } else { updateScrimKeyguard(); } Loading @@ -275,18 +310,18 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, float fraction = 1 - behindFraction; fraction = (float) Math.pow(fraction, 0.8f); behindFraction = (float) Math.pow(behindFraction, 0.8f); setScrimInFrontColor(fraction * getScrimInFrontAlpha()); setScrimBehindColor(behindFraction * mScrimBehindAlphaKeyguard); setScrimInFrontAlpha(fraction * getScrimInFrontAlpha()); setScrimBehindAlpha(behindFraction * mScrimBehindAlphaKeyguard); } else if (mBouncerShowing && !mBouncerIsKeyguard) { setScrimInFrontColor(getScrimInFrontAlpha()); setScrimInFrontAlpha(getScrimInFrontAlpha()); updateScrimNormal(); } else if (mBouncerShowing) { setScrimInFrontColor(0f); setScrimBehindColor(mScrimBehindAlpha); setScrimInFrontAlpha(0f); setScrimBehindAlpha(mScrimBehindAlpha); } else { float fraction = Math.max(0, Math.min(mFraction, 1)); setScrimInFrontColor(0f); setScrimBehindColor(fraction setScrimInFrontAlpha(0f); setScrimBehindAlpha(fraction * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking) + mScrimBehindAlphaUnlocking); } Loading @@ -297,30 +332,29 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, // let's start this 20% of the way down the screen frac = frac * 1.2f - 0.2f; if (frac <= 0) { setScrimBehindColor(0); setScrimBehindAlpha(0); } else { // woo, special effects final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); setScrimBehindColor(k * mScrimBehindAlpha); setScrimBehindAlpha(k * mScrimBehindAlpha); } } private void setScrimBehindColor(float alpha) { setScrimColor(mScrimBehind, alpha); private void setScrimBehindAlpha(float alpha) { setScrimAlpha(mScrimBehind, alpha); } private void setScrimInFrontColor(float alpha) { setScrimColor(mScrimInFront, alpha); private void setScrimInFrontAlpha(float alpha) { setScrimAlpha(mScrimInFront, alpha); if (alpha == 0f) { mScrimInFront.setClickable(false); } else { // Eat touch events (unless dozing). mScrimInFront.setClickable(!mDozing); } } private void setScrimColor(View scrim, float alpha) { private void setScrimAlpha(View scrim, float alpha) { updateScrim(mAnimateChange, scrim, alpha, getCurrentScrimAlpha(scrim)); } Loading @@ -346,15 +380,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } } protected void updateScrimColor(View scrim) { private void updateScrimColor(View scrim) { float alpha1 = getCurrentScrimAlpha(scrim); if (scrim instanceof ScrimView) { float alpha2 = getDozeAlpha(scrim); float alpha = 1 - (1 - alpha1) * (1 - alpha2); ScrimView scrimView = (ScrimView) scrim; float dozeAlpha = getDozeAlpha(scrim); float alpha = 1 - (1 - alpha1) * (1 - dozeAlpha); alpha = Math.max(0, Math.min(1.0f, alpha)); int baseColor = ((ScrimView) scrim).getScrimColor(); ((ScrimView) scrim).setScrimColor( ColorUtils.setAlphaComponent(baseColor, (int) (alpha * 255))); scrimView.setViewAlpha(alpha); int dozeTint = Color.TRANSPARENT; if (mAnimatingDozeUnlock || mDozing) { dozeTint = Color.BLACK; } scrimView.setTint(dozeTint); } else { scrim.setAlpha(alpha1); } Loading @@ -363,13 +402,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private void startScrimAnimation(final View scrim, float target) { float current = getCurrentScrimAlpha(scrim); ValueAnimator anim = ValueAnimator.ofFloat(current, target); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { anim.addUpdateListener(animation -> { float alpha = (float) animation.getAnimatedValue(); setCurrentScrimAlpha(scrim, alpha); updateScrimColor(scrim); } }); anim.setInterpolator(getInterpolator()); anim.setStartDelay(mAnimationDelay); Loading @@ -384,6 +420,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, if (mKeyguardFadingOutInProgress) { mKeyguardFadeoutAnimation = null; mKeyguardFadingOutInProgress = false; mAnimatingDozeUnlock = false; } scrim.setTag(TAG_KEY_ANIM, null); scrim.setTag(TAG_KEY_ANIM_TARGET, null); Loading Loading @@ -436,6 +473,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mOnAnimationFinished = null; } mKeyguardFadingOutInProgress = false; if (!mWakeAndUnlocking || force) mAnimatingDozeUnlock = false; } } Loading Loading @@ -559,8 +598,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mScrimBehind.setExcludedArea(area); } public int getScrimBehindColor() { return mScrimBehind.getScrimColorWithAlpha(); public int getBackgroundColor() { int color = mLockColors.getMainColor(); return Color.argb((int) (mScrimBehind.getAlpha() * Color.alpha(color)), Color.red(color), Color.green(color), Color.blue(color)); } public void setScrimBehindChangeRunnable(Runnable changeRunnable) { Loading @@ -577,4 +618,18 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public void setCurrentUser(int currentUser) { // Don't care in the base class. } @Override public void onColorsChanged(ColorExtractor.GradientColors colors, int which) { if ((which & WallpaperManager.FLAG_LOCK) != 0) { mLockColors = colors; mNeedsDrawableColorUpdate = true; scheduleUpdate(); } if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { mSystemColors = colors; mNeedsDrawableColorUpdate = true; scheduleUpdate(); } } }
packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +6 −16 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.OverScroller; import android.widget.ScrollView; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.ExpandHelper; Loading Loading @@ -87,6 +88,8 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ScrollAdapter; import android.support.v4.graphics.ColorUtils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; Loading Loading @@ -477,16 +480,8 @@ public class NotificationStackScrollLayout extends ViewGroup float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount); alpha *= mBackgroundFadeAmount; // We need to manually blend in the background color int scrimColor = mScrimController.getScrimBehindColor(); // SRC_OVER blending Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc float alphaInv = 1 - alpha; int color = Color.argb((int) (alpha * 255 + alphaInv * Color.alpha(scrimColor)), (int) (mBackgroundFadeAmount * Color.red(mBgColor) + alphaInv * Color.red(scrimColor)), (int) (mBackgroundFadeAmount * Color.green(mBgColor) + alphaInv * Color.green(scrimColor)), (int) (mBackgroundFadeAmount * Color.blue(mBgColor) + alphaInv * Color.blue(scrimColor))); int scrimColor = mScrimController.getBackgroundColor(); int color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha); if (mCachedBackgroundColor != color) { mCachedBackgroundColor = color; mBackgroundPaint.setColor(color); Loading Loading @@ -4059,12 +4054,7 @@ public class NotificationStackScrollLayout extends ViewGroup public void setScrimController(ScrimController scrimController) { mScrimController = scrimController; mScrimController.setScrimBehindChangeRunnable(new Runnable() { @Override public void run() { updateBackgroundDimming(); } }); mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming); } public void forceNoOverlappingRendering(boolean force) { Loading
packages/SystemUI/tests/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -44,7 +44,8 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ android-support-v7-mediarouter \ android-support-v7-palette \ android-support-v14-preference \ android-support-v17-leanback android-support-v17-leanback \ colorextraction LOCAL_STATIC_JAVA_LIBRARIES := \ metrics-helper-lib \ Loading