Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.systemui.statusbar.phone;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.ValueAnimator;
     22 import android.annotation.NonNull;
     23 import android.content.Context;
     24 import android.os.Handler;
     25 import android.util.Log;
     26 import android.view.View;
     27 import android.view.animation.Interpolator;
     28 
     29 import com.android.keyguard.KeyguardStatusView;
     30 import com.android.systemui.Interpolators;
     31 import com.android.systemui.doze.DozeHost;
     32 import com.android.systemui.doze.DozeLog;
     33 import com.android.systemui.doze.DozeTriggers;
     34 
     35 /**
     36  * Controller which handles all the doze animations of the scrims.
     37  */
     38 public class DozeScrimController {
     39     private static final String TAG = "DozeScrimController";
     40     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     41 
     42     private final DozeParameters mDozeParameters;
     43     private final Handler mHandler = new Handler();
     44     private final ScrimController mScrimController;
     45 
     46     private final Context mContext;
     47 
     48     private boolean mDozing;
     49     private DozeHost.PulseCallback mPulseCallback;
     50     private int mPulseReason;
     51     private Animator mInFrontAnimator;
     52     private Animator mBehindAnimator;
     53     private float mInFrontTarget;
     54     private float mBehindTarget;
     55     private boolean mDozingAborted;
     56     private boolean mWakeAndUnlocking;
     57 
     58     public DozeScrimController(ScrimController scrimController, Context context) {
     59         mContext = context;
     60         mScrimController = scrimController;
     61         mDozeParameters = new DozeParameters(context);
     62     }
     63 
     64     public void setDozing(boolean dozing, boolean animate) {
     65         if (mDozing == dozing) return;
     66         mDozing = dozing;
     67         mWakeAndUnlocking = false;
     68         if (mDozing) {
     69             mDozingAborted = false;
     70             abortAnimations();
     71             mScrimController.setDozeBehindAlpha(1f);
     72             mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
     73         } else {
     74             cancelPulsing();
     75             if (animate) {
     76                 startScrimAnimation(false /* inFront */, 0f /* target */,
     77                         NotificationPanelView.DOZE_ANIMATION_DURATION,
     78                         Interpolators.LINEAR_OUT_SLOW_IN);
     79                 startScrimAnimation(true /* inFront */, 0f /* target */,
     80                         NotificationPanelView.DOZE_ANIMATION_DURATION,
     81                         Interpolators.LINEAR_OUT_SLOW_IN);
     82             } else {
     83                 abortAnimations();
     84                 mScrimController.setDozeBehindAlpha(0f);
     85                 mScrimController.setDozeInFrontAlpha(0f);
     86             }
     87         }
     88     }
     89 
     90     public void setWakeAndUnlocking() {
     91         // Immediately abort the doze scrims in case of wake-and-unlock
     92         // for pulsing so the Keyguard fade-out animation scrim can take over.
     93         if (!mWakeAndUnlocking) {
     94             mWakeAndUnlocking = true;
     95             mScrimController.setDozeBehindAlpha(0f);
     96             mScrimController.setDozeInFrontAlpha(0f);
     97         }
     98     }
     99 
    100     /** When dozing, fade screen contents in and out using the front scrim. */
    101     public void pulse(@NonNull DozeHost.PulseCallback callback, int reason) {
    102         if (callback == null) {
    103             throw new IllegalArgumentException("callback must not be null");
    104         }
    105 
    106         if (!mDozing || mPulseCallback != null) {
    107             // Pulse suppressed.
    108             callback.onPulseFinished();
    109             return;
    110         }
    111 
    112         // Begin pulse.  Note that it's very important that the pulse finished callback
    113         // be invoked when we're done so that the caller can drop the pulse wakelock.
    114         mPulseCallback = callback;
    115         mPulseReason = reason;
    116         mHandler.post(mPulseIn);
    117     }
    118 
    119     /**
    120      * Aborts pulsing immediately.
    121      */
    122     public void abortPulsing() {
    123         cancelPulsing();
    124         if (mDozing && !mWakeAndUnlocking) {
    125             mScrimController.setDozeBehindAlpha(1f);
    126             mScrimController.setDozeInFrontAlpha(
    127                     mDozeParameters.getAlwaysOn() && !mDozingAborted ? 0f : 1f);
    128         }
    129     }
    130 
    131     /**
    132      * Aborts dozing immediately.
    133      */
    134     public void abortDoze() {
    135         mDozingAborted = true;
    136         abortPulsing();
    137     }
    138 
    139     public void onScreenTurnedOn() {
    140         if (isPulsing()) {
    141             final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
    142                     || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
    143             startScrimAnimation(true /* inFront */, 0f,
    144                     mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
    145                     pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
    146                     mPulseInFinished);
    147         }
    148     }
    149 
    150     public boolean isPulsing() {
    151         return mPulseCallback != null;
    152     }
    153 
    154     public boolean isDozing() {
    155         return mDozing;
    156     }
    157 
    158     public void extendPulse() {
    159         mHandler.removeCallbacks(mPulseOut);
    160     }
    161 
    162     private void cancelPulsing() {
    163         if (DEBUG) Log.d(TAG, "Cancel pulsing");
    164 
    165         if (mPulseCallback != null) {
    166             mHandler.removeCallbacks(mPulseIn);
    167             mHandler.removeCallbacks(mPulseOut);
    168             mHandler.removeCallbacks(mPulseOutExtended);
    169             pulseFinished();
    170         }
    171     }
    172 
    173     private void pulseStarted() {
    174         if (mPulseCallback != null) {
    175             mPulseCallback.onPulseStarted();
    176         }
    177     }
    178 
    179     private void pulseFinished() {
    180         if (mPulseCallback != null) {
    181             mPulseCallback.onPulseFinished();
    182             mPulseCallback = null;
    183         }
    184     }
    185 
    186     private void abortAnimations() {
    187         if (mInFrontAnimator != null) {
    188             mInFrontAnimator.cancel();
    189         }
    190         if (mBehindAnimator != null) {
    191             mBehindAnimator.cancel();
    192         }
    193     }
    194 
    195     private void startScrimAnimation(final boolean inFront, float target, long duration,
    196             Interpolator interpolator) {
    197         startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */);
    198     }
    199 
    200     private void startScrimAnimation(final boolean inFront, float target, long duration,
    201             Interpolator interpolator, final Runnable endRunnable) {
    202         Animator current = getCurrentAnimator(inFront);
    203         if (current != null) {
    204             float currentTarget = getCurrentTarget(inFront);
    205             if (currentTarget == target) {
    206                 return;
    207             }
    208             current.cancel();
    209         }
    210         ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target);
    211         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    212             @Override
    213             public void onAnimationUpdate(ValueAnimator animation) {
    214                 float value = (float) animation.getAnimatedValue();
    215                 setDozeAlpha(inFront, value);
    216             }
    217         });
    218         anim.setInterpolator(interpolator);
    219         anim.setDuration(duration);
    220         anim.addListener(new AnimatorListenerAdapter() {
    221             @Override
    222             public void onAnimationEnd(Animator animation) {
    223                 setCurrentAnimator(inFront, null);
    224                 if (endRunnable != null) {
    225                     endRunnable.run();
    226                 }
    227             }
    228         });
    229         anim.start();
    230         setCurrentAnimator(inFront, anim);
    231         setCurrentTarget(inFront, target);
    232     }
    233 
    234     private float getCurrentTarget(boolean inFront) {
    235         return inFront ? mInFrontTarget : mBehindTarget;
    236     }
    237 
    238     private void setCurrentTarget(boolean inFront, float target) {
    239         if (inFront) {
    240             mInFrontTarget = target;
    241         } else {
    242             mBehindTarget = target;
    243         }
    244     }
    245 
    246     private Animator getCurrentAnimator(boolean inFront) {
    247         return inFront ? mInFrontAnimator : mBehindAnimator;
    248     }
    249 
    250     private void setCurrentAnimator(boolean inFront, Animator animator) {
    251         if (inFront) {
    252             mInFrontAnimator = animator;
    253         } else {
    254             mBehindAnimator = animator;
    255         }
    256     }
    257 
    258     private void setDozeAlpha(boolean inFront, float alpha) {
    259         if (mWakeAndUnlocking) {
    260             return;
    261         }
    262         if (inFront) {
    263             mScrimController.setDozeInFrontAlpha(alpha);
    264         } else {
    265             mScrimController.setDozeBehindAlpha(alpha);
    266         }
    267     }
    268 
    269     private float getDozeAlpha(boolean inFront) {
    270         return inFront
    271                 ? mScrimController.getDozeInFrontAlpha()
    272                 : mScrimController.getDozeBehindAlpha();
    273     }
    274 
    275     private final Runnable mPulseIn = new Runnable() {
    276         @Override
    277         public void run() {
    278             if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
    279                     + DozeLog.pulseReasonToString(mPulseReason));
    280             if (!mDozing) return;
    281             DozeLog.tracePulseStart(mPulseReason);
    282 
    283             // Signal that the pulse is ready to turn the screen on and draw.
    284             pulseStarted();
    285 
    286             if (mDozeParameters.getAlwaysOn()) {
    287                 mHandler.post(DozeScrimController.this::onScreenTurnedOn);
    288             }
    289         }
    290     };
    291 
    292     private final Runnable mPulseInFinished = new Runnable() {
    293         @Override
    294         public void run() {
    295             if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
    296             if (!mDozing) return;
    297             mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
    298             mHandler.postDelayed(mPulseOutExtended,
    299                     mDozeParameters.getPulseVisibleDurationExtended());
    300         }
    301     };
    302 
    303     private final Runnable mPulseOutExtended = new Runnable() {
    304         @Override
    305         public void run() {
    306             mHandler.removeCallbacks(mPulseOut);
    307             mPulseOut.run();
    308         }
    309     };
    310 
    311     private final Runnable mPulseOut = new Runnable() {
    312         @Override
    313         public void run() {
    314             mHandler.removeCallbacks(mPulseOutExtended);
    315             if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
    316             if (!mDozing) return;
    317             startScrimAnimation(true /* inFront */, mDozeParameters.getAlwaysOn() ? 0 : 1,
    318                     mDozeParameters.getPulseOutDuration(),
    319                     Interpolators.ALPHA_IN, mPulseOutFinished);
    320         }
    321     };
    322 
    323     private final Runnable mPulseOutFinished = new Runnable() {
    324         @Override
    325         public void run() {
    326             if (DEBUG) Log.d(TAG, "Pulse out finished");
    327             DozeLog.tracePulseFinish();
    328 
    329             // Signal that the pulse is all finished so we can turn the screen off now.
    330             pulseFinished();
    331         }
    332     };
    333 }
    334