| /* |
| * 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.phone; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.animation.ValueAnimator; |
| import android.annotation.NonNull; |
| import android.content.Context; |
| import android.graphics.Color; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.ViewTreeObserver; |
| import android.view.animation.AnimationUtils; |
| import android.view.animation.DecelerateInterpolator; |
| import android.view.animation.Interpolator; |
| |
| import com.android.systemui.R; |
| import com.android.systemui.doze.DozeHost; |
| import com.android.systemui.doze.DozeLog; |
| import com.android.systemui.statusbar.BackDropView; |
| import com.android.systemui.statusbar.ScrimView; |
| |
| /** |
| * 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 { |
| private static final String TAG = "ScrimController"; |
| private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); |
| |
| public static final long ANIMATION_DURATION = 220; |
| |
| private static final float SCRIM_BEHIND_ALPHA = 0.62f; |
| private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.55f; |
| private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f; |
| private static final float SCRIM_IN_FRONT_ALPHA = 0.75f; |
| private static final int TAG_KEY_ANIM = R.id.scrim; |
| |
| private final ScrimView mScrimBehind; |
| private final ScrimView mScrimInFront; |
| private final UnlockMethodCache mUnlockMethodCache; |
| private final DozeParameters mDozeParameters; |
| |
| private boolean mKeyguardShowing; |
| private float mFraction; |
| |
| private boolean mDarkenWhileDragging; |
| private boolean mBouncerShowing; |
| private boolean mAnimateChange; |
| private boolean mUpdatePending; |
| private boolean mExpanding; |
| private boolean mAnimateKeyguardFadingOut; |
| private long mDurationOverride = -1; |
| private long mAnimationDelay; |
| private Runnable mOnAnimationFinished; |
| private boolean mAnimationStarted; |
| private boolean mDozing; |
| private DozeHost.PulseCallback mPulseCallback; |
| private final Interpolator mInterpolator = new DecelerateInterpolator(); |
| private final Interpolator mLinearOutSlowInInterpolator; |
| private BackDropView mBackDropView; |
| private boolean mScrimSrcEnabled; |
| |
| public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, boolean scrimSrcEnabled) { |
| mScrimBehind = scrimBehind; |
| mScrimInFront = scrimInFront; |
| final Context context = scrimBehind.getContext(); |
| mUnlockMethodCache = UnlockMethodCache.getInstance(context); |
| mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, |
| android.R.interpolator.linear_out_slow_in); |
| mDozeParameters = new DozeParameters(context); |
| mScrimSrcEnabled = scrimSrcEnabled; |
| } |
| |
| public void setKeyguardShowing(boolean showing) { |
| mKeyguardShowing = showing; |
| scheduleUpdate(); |
| } |
| |
| public void onTrackingStarted() { |
| mExpanding = true; |
| mDarkenWhileDragging = !mUnlockMethodCache.isMethodInsecure(); |
| } |
| |
| public void onExpandingFinished() { |
| mExpanding = false; |
| } |
| |
| public void setPanelExpansion(float fraction) { |
| if (mFraction != fraction) { |
| mFraction = fraction; |
| scheduleUpdate(); |
| } |
| } |
| |
| public void setBouncerShowing(boolean showing) { |
| mBouncerShowing = showing; |
| mAnimateChange = !mExpanding; |
| scheduleUpdate(); |
| } |
| |
| public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) { |
| mAnimateKeyguardFadingOut = true; |
| mDurationOverride = duration; |
| mAnimationDelay = delay; |
| mAnimateChange = true; |
| mOnAnimationFinished = onAnimationFinished; |
| scheduleUpdate(); |
| } |
| |
| public void animateGoingToFullShade(long delay, long duration) { |
| mDurationOverride = duration; |
| mAnimationDelay = delay; |
| mAnimateChange = true; |
| scheduleUpdate(); |
| } |
| |
| public void setDozing(boolean dozing) { |
| if (mDozing == dozing) return; |
| mDozing = dozing; |
| if (!mDozing) { |
| cancelPulsing(); |
| mAnimateChange = true; |
| } else { |
| mAnimateChange = false; |
| } |
| scheduleUpdate(); |
| } |
| |
| /** When dozing, fade screen contents in and out using the front scrim. */ |
| public void pulse(@NonNull DozeHost.PulseCallback callback) { |
| if (callback == null) { |
| throw new IllegalArgumentException("callback must not be null"); |
| } |
| |
| if (!mDozing || mPulseCallback != null) { |
| // Pulse suppressed. |
| callback.onPulseFinished(); |
| return; |
| } |
| |
| // Begin pulse. Note that it's very important that the pulse finished callback |
| // be invoked when we're done so that the caller can drop the pulse wakelock. |
| mPulseCallback = callback; |
| mScrimInFront.post(mPulseIn); |
| } |
| |
| public boolean isPulsing() { |
| return mPulseCallback != null; |
| } |
| |
| private void cancelPulsing() { |
| if (DEBUG) Log.d(TAG, "Cancel pulsing"); |
| |
| if (mPulseCallback != null) { |
| mScrimInFront.removeCallbacks(mPulseIn); |
| mScrimInFront.removeCallbacks(mPulseOut); |
| pulseFinished(); |
| } |
| } |
| |
| private void pulseStarted() { |
| if (mPulseCallback != null) { |
| mPulseCallback.onPulseStarted(); |
| } |
| } |
| |
| private void pulseFinished() { |
| if (mPulseCallback != null) { |
| mPulseCallback.onPulseFinished(); |
| mPulseCallback = null; |
| } |
| } |
| |
| private void scheduleUpdate() { |
| if (mUpdatePending) return; |
| |
| // Make sure that a frame gets scheduled. |
| mScrimBehind.invalidate(); |
| mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this); |
| mUpdatePending = true; |
| } |
| |
| private void updateScrims() { |
| if (mAnimateKeyguardFadingOut) { |
| setScrimInFrontColor(0f); |
| setScrimBehindColor(0f); |
| } else if (!mKeyguardShowing && !mBouncerShowing) { |
| updateScrimNormal(); |
| setScrimInFrontColor(0); |
| } else { |
| updateScrimKeyguard(); |
| } |
| mAnimateChange = false; |
| } |
| |
| private void updateScrimKeyguard() { |
| if (mExpanding && mDarkenWhileDragging) { |
| float behindFraction = Math.max(0, Math.min(mFraction, 1)); |
| float fraction = 1 - behindFraction; |
| fraction = (float) Math.pow(fraction, 0.8f); |
| behindFraction = (float) Math.pow(behindFraction, 0.8f); |
| setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA); |
| setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD); |
| } else if (mBouncerShowing) { |
| setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA); |
| setScrimBehindColor(0f); |
| } else if (mDozing) { |
| setScrimInFrontColor(1); |
| } else { |
| float fraction = Math.max(0, Math.min(mFraction, 1)); |
| setScrimInFrontColor(0f); |
| setScrimBehindColor(fraction |
| * (SCRIM_BEHIND_ALPHA_KEYGUARD - SCRIM_BEHIND_ALPHA_UNLOCKING) |
| + SCRIM_BEHIND_ALPHA_UNLOCKING); |
| } |
| } |
| |
| private void updateScrimNormal() { |
| float frac = mFraction; |
| // let's start this 20% of the way down the screen |
| frac = frac * 1.2f - 0.2f; |
| if (frac <= 0) { |
| setScrimBehindColor(0); |
| } else { |
| // woo, special effects |
| final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f)))); |
| setScrimBehindColor(k * SCRIM_BEHIND_ALPHA); |
| } |
| } |
| |
| private void setScrimBehindColor(float alpha) { |
| setScrimColor(mScrimBehind, alpha); |
| } |
| |
| private void setScrimInFrontColor(float alpha) { |
| setScrimColor(mScrimInFront, alpha); |
| if (alpha == 0f) { |
| mScrimInFront.setClickable(false); |
| } else { |
| |
| // Eat touch events (unless dozing). |
| mScrimInFront.setClickable(!mDozing); |
| } |
| } |
| |
| private void setScrimColor(ScrimView scrim, float alpha) { |
| Object runningAnim = scrim.getTag(TAG_KEY_ANIM); |
| if (runningAnim instanceof ValueAnimator) { |
| ((ValueAnimator) runningAnim).cancel(); |
| scrim.setTag(TAG_KEY_ANIM, null); |
| } |
| int color = Color.argb((int) (alpha * 255), 0, 0, 0); |
| if (mAnimateChange) { |
| startScrimAnimation(scrim, color); |
| } else { |
| scrim.setScrimColor(color); |
| } |
| } |
| |
| private void startScrimAnimation(final ScrimView scrim, int targetColor) { |
| int current = Color.alpha(scrim.getScrimColor()); |
| int target = Color.alpha(targetColor); |
| if (current == targetColor) { |
| return; |
| } |
| ValueAnimator anim = ValueAnimator.ofInt(current, target); |
| anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
| @Override |
| public void onAnimationUpdate(ValueAnimator animation) { |
| int value = (int) animation.getAnimatedValue(); |
| scrim.setScrimColor(Color.argb(value, 0, 0, 0)); |
| } |
| }); |
| anim.setInterpolator(mAnimateKeyguardFadingOut |
| ? mLinearOutSlowInInterpolator |
| : mInterpolator); |
| anim.setStartDelay(mAnimationDelay); |
| anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION); |
| anim.addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| if (mOnAnimationFinished != null) { |
| mOnAnimationFinished.run(); |
| mOnAnimationFinished = null; |
| } |
| scrim.setTag(TAG_KEY_ANIM, null); |
| } |
| }); |
| anim.start(); |
| scrim.setTag(TAG_KEY_ANIM, anim); |
| mAnimationStarted = true; |
| } |
| |
| @Override |
| public boolean onPreDraw() { |
| mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this); |
| mUpdatePending = false; |
| updateScrims(); |
| mAnimateKeyguardFadingOut = false; |
| mDurationOverride = -1; |
| mAnimationDelay = 0; |
| |
| // Make sure that we always call the listener even if we didn't start an animation. |
| if (!mAnimationStarted && mOnAnimationFinished != null) { |
| mOnAnimationFinished.run(); |
| mOnAnimationFinished = null; |
| } |
| mAnimationStarted = false; |
| return true; |
| } |
| |
| private final Runnable mPulseIn = new Runnable() { |
| @Override |
| public void run() { |
| if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing); |
| if (!mDozing) return; |
| DozeLog.tracePulseStart(); |
| mDurationOverride = mDozeParameters.getPulseInDuration(); |
| mAnimationDelay = 0; |
| mAnimateChange = true; |
| mOnAnimationFinished = mPulseInFinished; |
| setScrimColor(mScrimInFront, 0); |
| |
| // Signal that the pulse is ready to turn the screen on and draw. |
| pulseStarted(); |
| } |
| }; |
| |
| private final Runnable mPulseInFinished = new Runnable() { |
| @Override |
| public void run() { |
| if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing); |
| if (!mDozing) return; |
| mScrimInFront.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); |
| } |
| }; |
| |
| private final Runnable mPulseOut = new Runnable() { |
| @Override |
| public void run() { |
| if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing); |
| if (!mDozing) return; |
| mDurationOverride = mDozeParameters.getPulseOutDuration(); |
| mAnimationDelay = 0; |
| mAnimateChange = true; |
| mOnAnimationFinished = mPulseOutFinished; |
| setScrimColor(mScrimInFront, 1); |
| } |
| }; |
| |
| private final Runnable mPulseOutFinished = new Runnable() { |
| @Override |
| public void run() { |
| if (DEBUG) Log.d(TAG, "Pulse out finished"); |
| DozeLog.tracePulseFinish(); |
| |
| // Signal that the pulse is all finished so we can turn the screen off now. |
| pulseFinished(); |
| } |
| }; |
| |
| public void setBackDropView(BackDropView backDropView) { |
| mBackDropView = backDropView; |
| mBackDropView.setOnVisibilityChangedRunnable(new Runnable() { |
| @Override |
| public void run() { |
| updateScrimBehindDrawingMode(); |
| } |
| }); |
| updateScrimBehindDrawingMode(); |
| } |
| |
| private void updateScrimBehindDrawingMode() { |
| boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE && mScrimSrcEnabled; |
| mScrimBehind.setDrawAsSrc(asSrc); |
| } |
| } |