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.animation.Interpolator;
     27 
     28 import com.android.systemui.Interpolators;
     29 import com.android.systemui.doze.DozeHost;
     30 import com.android.systemui.doze.DozeLog;
     31 
     32 /**
     33  * Controller which handles all the doze animations of the scrims.
     34  */
     35 public class DozeScrimController {
     36     private static final String TAG = "DozeScrimController";
     37     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     38 
     39     private final DozeParameters mDozeParameters;
     40     private final Handler mHandler = new Handler();
     41     private final ScrimController mScrimController;
     42 
     43     private boolean mDozing;
     44     private DozeHost.PulseCallback mPulseCallback;
     45     private int mPulseReason;
     46     private Animator mInFrontAnimator;
     47     private Animator mBehindAnimator;
     48     private float mInFrontTarget;
     49     private float mBehindTarget;
     50 
     51     public DozeScrimController(ScrimController scrimController, Context context) {
     52         mScrimController = scrimController;
     53         mDozeParameters = new DozeParameters(context);
     54     }
     55 
     56     public void setDozing(boolean dozing, boolean animate) {
     57         if (mDozing == dozing) return;
     58         mDozing = dozing;
     59         if (mDozing) {
     60             abortAnimations();
     61             mScrimController.setDozeBehindAlpha(1f);
     62             mScrimController.setDozeInFrontAlpha(1f);
     63         } else {
     64             cancelPulsing();
     65             if (animate) {
     66                 startScrimAnimation(false /* inFront */, 0f /* target */,
     67                         NotificationPanelView.DOZE_ANIMATION_DURATION,
     68                         Interpolators.LINEAR_OUT_SLOW_IN);
     69                 startScrimAnimation(true /* inFront */, 0f /* target */,
     70                         NotificationPanelView.DOZE_ANIMATION_DURATION,
     71                         Interpolators.LINEAR_OUT_SLOW_IN);
     72             } else {
     73                 abortAnimations();
     74                 mScrimController.setDozeBehindAlpha(0f);
     75                 mScrimController.setDozeInFrontAlpha(0f);
     76             }
     77         }
     78     }
     79 
     80     /** When dozing, fade screen contents in and out using the front scrim. */
     81     public void pulse(@NonNull DozeHost.PulseCallback callback, int reason) {
     82         if (callback == null) {
     83             throw new IllegalArgumentException("callback must not be null");
     84         }
     85 
     86         if (!mDozing || mPulseCallback != null) {
     87             // Pulse suppressed.
     88             callback.onPulseFinished();
     89             return;
     90         }
     91 
     92         // Begin pulse.  Note that it's very important that the pulse finished callback
     93         // be invoked when we're done so that the caller can drop the pulse wakelock.
     94         mPulseCallback = callback;
     95         mPulseReason = reason;
     96         mHandler.post(mPulseIn);
     97     }
     98 
     99     /**
    100      * Aborts pulsing immediately.
    101      */
    102     public void abortPulsing() {
    103         cancelPulsing();
    104         if (mDozing) {
    105             mScrimController.setDozeBehindAlpha(1f);
    106             mScrimController.setDozeInFrontAlpha(1f);
    107         }
    108     }
    109 
    110     public void onScreenTurnedOn() {
    111         if (isPulsing()) {
    112             final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
    113                     || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
    114             startScrimAnimation(true /* inFront */, 0f,
    115                     mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
    116                     pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
    117                     mPulseInFinished);
    118         }
    119     }
    120 
    121     public boolean isPulsing() {
    122         return mPulseCallback != null;
    123     }
    124 
    125     public boolean isDozing() {
    126         return mDozing;
    127     }
    128 
    129     private void cancelPulsing() {
    130         if (DEBUG) Log.d(TAG, "Cancel pulsing");
    131 
    132         if (mPulseCallback != null) {
    133             mHandler.removeCallbacks(mPulseIn);
    134             mHandler.removeCallbacks(mPulseOut);
    135             pulseFinished();
    136         }
    137     }
    138 
    139     private void pulseStarted() {
    140         if (mPulseCallback != null) {
    141             mPulseCallback.onPulseStarted();
    142         }
    143     }
    144 
    145     private void pulseFinished() {
    146         if (mPulseCallback != null) {
    147             mPulseCallback.onPulseFinished();
    148             mPulseCallback = null;
    149         }
    150     }
    151 
    152     private void abortAnimations() {
    153         if (mInFrontAnimator != null) {
    154             mInFrontAnimator.cancel();
    155         }
    156         if (mBehindAnimator != null) {
    157             mBehindAnimator.cancel();
    158         }
    159     }
    160 
    161     private void startScrimAnimation(final boolean inFront, float target, long duration,
    162             Interpolator interpolator) {
    163         startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */);
    164     }
    165 
    166     private void startScrimAnimation(final boolean inFront, float target, long duration,
    167             Interpolator interpolator, final Runnable endRunnable) {
    168         Animator current = getCurrentAnimator(inFront);
    169         if (current != null) {
    170             float currentTarget = getCurrentTarget(inFront);
    171             if (currentTarget == target) {
    172                 return;
    173             }
    174             current.cancel();
    175         }
    176         ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target);
    177         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    178             @Override
    179             public void onAnimationUpdate(ValueAnimator animation) {
    180                 float value = (float) animation.getAnimatedValue();
    181                 setDozeAlpha(inFront, value);
    182             }
    183         });
    184         anim.setInterpolator(interpolator);
    185         anim.setDuration(duration);
    186         anim.addListener(new AnimatorListenerAdapter() {
    187             @Override
    188             public void onAnimationEnd(Animator animation) {
    189                 setCurrentAnimator(inFront, null);
    190                 if (endRunnable != null) {
    191                     endRunnable.run();
    192                 }
    193             }
    194         });
    195         anim.start();
    196         setCurrentAnimator(inFront, anim);
    197         setCurrentTarget(inFront, target);
    198     }
    199 
    200     private float getCurrentTarget(boolean inFront) {
    201         return inFront ? mInFrontTarget : mBehindTarget;
    202     }
    203 
    204     private void setCurrentTarget(boolean inFront, float target) {
    205         if (inFront) {
    206             mInFrontTarget = target;
    207         } else {
    208             mBehindTarget = target;
    209         }
    210     }
    211 
    212     private Animator getCurrentAnimator(boolean inFront) {
    213         return inFront ? mInFrontAnimator : mBehindAnimator;
    214     }
    215 
    216     private void setCurrentAnimator(boolean inFront, Animator animator) {
    217         if (inFront) {
    218             mInFrontAnimator = animator;
    219         } else {
    220             mBehindAnimator = animator;
    221         }
    222     }
    223 
    224     private void setDozeAlpha(boolean inFront, float alpha) {
    225         if (inFront) {
    226             mScrimController.setDozeInFrontAlpha(alpha);
    227         } else {
    228             mScrimController.setDozeBehindAlpha(alpha);
    229         }
    230     }
    231 
    232     private float getDozeAlpha(boolean inFront) {
    233         return inFront
    234                 ? mScrimController.getDozeInFrontAlpha()
    235                 : mScrimController.getDozeBehindAlpha();
    236     }
    237 
    238     private final Runnable mPulseIn = new Runnable() {
    239         @Override
    240         public void run() {
    241             if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
    242                     + DozeLog.pulseReasonToString(mPulseReason));
    243             if (!mDozing) return;
    244             DozeLog.tracePulseStart(mPulseReason);
    245 
    246             // Signal that the pulse is ready to turn the screen on and draw.
    247             pulseStarted();
    248         }
    249     };
    250 
    251     private final Runnable mPulseInFinished = new Runnable() {
    252         @Override
    253         public void run() {
    254             if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
    255             if (!mDozing) return;
    256             mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
    257         }
    258     };
    259 
    260     private final Runnable mPulseOut = new Runnable() {
    261         @Override
    262         public void run() {
    263             if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
    264             if (!mDozing) return;
    265             startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(),
    266                     Interpolators.ALPHA_IN, mPulseOutFinished);
    267         }
    268     };
    269 
    270     private final Runnable mPulseOutFinished = new Runnable() {
    271         @Override
    272         public void run() {
    273             if (DEBUG) Log.d(TAG, "Pulse out finished");
    274             DozeLog.tracePulseFinish();
    275 
    276             // Signal that the pulse is all finished so we can turn the screen off now.
    277             pulseFinished();
    278         }
    279     };
    280 }
    281