Home | History | Annotate | Download | only in policy
      1 /*
      2  * Copyright (C) 2015 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.server.policy;
     18 
     19 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
     20 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
     21 import static android.view.WindowManagerInternal.AppTransitionListener;
     22 
     23 import android.app.StatusBarManager;
     24 import android.os.IBinder;
     25 import android.os.SystemClock;
     26 import android.view.View;
     27 import android.view.animation.Animation;
     28 import android.view.animation.AnimationSet;
     29 import android.view.animation.Interpolator;
     30 import android.view.animation.TranslateAnimation;
     31 
     32 import com.android.server.LocalServices;
     33 import com.android.server.statusbar.StatusBarManagerInternal;
     34 
     35 /**
     36  * Implements status bar specific behavior.
     37  */
     38 public class StatusBarController extends BarController {
     39 
     40     private static final long TRANSITION_DURATION = 120L;
     41 
     42     private final AppTransitionListener mAppTransitionListener
     43             = new AppTransitionListener() {
     44 
     45         @Override
     46         public void onAppTransitionPendingLocked() {
     47             mHandler.post(new Runnable() {
     48                 @Override
     49                 public void run() {
     50                     StatusBarManagerInternal statusbar = getStatusBarInternal();
     51                     if (statusbar != null) {
     52                         statusbar.appTransitionPending();
     53                     }
     54                 }
     55             });
     56         }
     57 
     58         @Override
     59         public void onAppTransitionStartingLocked(IBinder openToken, IBinder closeToken,
     60                 final Animation openAnimation, final Animation closeAnimation) {
     61             mHandler.post(new Runnable() {
     62                 @Override
     63                 public void run() {
     64                     StatusBarManagerInternal statusbar = getStatusBarInternal();
     65                     if (statusbar != null) {
     66                         long startTime = calculateStatusBarTransitionStartTime(openAnimation,
     67                                 closeAnimation);
     68                         long duration = closeAnimation != null || openAnimation != null
     69                                 ? TRANSITION_DURATION : 0;
     70                         statusbar.appTransitionStarting(startTime, duration);
     71                     }
     72                 }
     73             });
     74         }
     75 
     76         @Override
     77         public void onAppTransitionCancelledLocked() {
     78             mHandler.post(new Runnable() {
     79                 @Override
     80                 public void run() {
     81                     StatusBarManagerInternal statusbar = getStatusBarInternal();
     82                     if (statusbar != null) {
     83                         statusbar.appTransitionCancelled();
     84                     }
     85                 }
     86             });
     87         }
     88 
     89         @Override
     90         public void onAppTransitionFinishedLocked(IBinder token) {
     91             mHandler.post(new Runnable() {
     92                 @Override
     93                 public void run() {
     94                     StatusBarManagerInternal statusbar = LocalServices.getService(
     95                             StatusBarManagerInternal.class);
     96                     if (statusbar != null) {
     97                         statusbar.appTransitionFinished();
     98                     }
     99                 }
    100             });
    101         }
    102     };
    103 
    104     public StatusBarController() {
    105         super("StatusBar",
    106                 View.STATUS_BAR_TRANSIENT,
    107                 View.STATUS_BAR_UNHIDE,
    108                 View.STATUS_BAR_TRANSLUCENT,
    109                 StatusBarManager.WINDOW_STATUS_BAR,
    110                 FLAG_TRANSLUCENT_STATUS,
    111                 View.STATUS_BAR_TRANSPARENT);
    112     }
    113 
    114     @Override
    115     protected boolean skipAnimation() {
    116         return mWin.getAttrs().height == MATCH_PARENT;
    117     }
    118 
    119     public AppTransitionListener getAppTransitionListener() {
    120         return mAppTransitionListener;
    121     }
    122 
    123     /**
    124      * For a given app transition with {@code openAnimation} and {@code closeAnimation}, this
    125      * calculates the timings for the corresponding status bar transition.
    126      *
    127      * @return the desired start time of the status bar transition, in uptime millis
    128      */
    129     private static long calculateStatusBarTransitionStartTime(Animation openAnimation,
    130             Animation closeAnimation) {
    131         if (openAnimation != null && closeAnimation != null) {
    132             TranslateAnimation openTranslateAnimation = findTranslateAnimation(openAnimation);
    133             TranslateAnimation closeTranslateAnimation = findTranslateAnimation(closeAnimation);
    134             if (openTranslateAnimation != null) {
    135 
    136                 // Some interpolators are extremely quickly mostly finished, but not completely. For
    137                 // our purposes, we need to find the fraction for which ther interpolator is mostly
    138                 // there, and use that value for the calculation.
    139                 float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
    140                 return SystemClock.uptimeMillis()
    141                         + openTranslateAnimation.getStartOffset()
    142                         + (long)(openTranslateAnimation.getDuration()*t) - TRANSITION_DURATION;
    143             } else if (closeTranslateAnimation != null) {
    144                 return SystemClock.uptimeMillis();
    145             } else {
    146                 return SystemClock.uptimeMillis();
    147             }
    148         } else {
    149             return SystemClock.uptimeMillis();
    150         }
    151     }
    152 
    153     /**
    154      * Tries to find a {@link TranslateAnimation} inside the {@code animation}.
    155      *
    156      * @return the found animation, {@code null} otherwise
    157      */
    158     private static TranslateAnimation findTranslateAnimation(Animation animation) {
    159         if (animation instanceof TranslateAnimation) {
    160             return (TranslateAnimation) animation;
    161         } else if (animation instanceof AnimationSet) {
    162             AnimationSet set = (AnimationSet) animation;
    163             for (int i = 0; i < set.getAnimations().size(); i++) {
    164                 Animation a = set.getAnimations().get(i);
    165                 if (a instanceof TranslateAnimation) {
    166                     return (TranslateAnimation) a;
    167                 }
    168             }
    169         }
    170         return null;
    171     }
    172 
    173     /**
    174      * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which
    175      * {@code interpolator(t + eps) > 0.99}.
    176      */
    177     private static float findAlmostThereFraction(Interpolator interpolator) {
    178         float val = 0.5f;
    179         float adj = 0.25f;
    180         while (adj >= 0.01f) {
    181             if (interpolator.getInterpolation(val) < 0.99f) {
    182                 val += adj;
    183             } else {
    184                 val -= adj;
    185             }
    186             adj /= 2;
    187         }
    188         return val;
    189     }
    190 }
    191