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 int onAppTransitionStartingLocked(int transit, IBinder openToken,
     60                 IBinder closeToken, 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             return 0;
     75         }
     76 
     77         @Override
     78         public void onAppTransitionCancelledLocked(int transit) {
     79             mHandler.post(new Runnable() {
     80                 @Override
     81                 public void run() {
     82                     StatusBarManagerInternal statusbar = getStatusBarInternal();
     83                     if (statusbar != null) {
     84                         statusbar.appTransitionCancelled();
     85                     }
     86                 }
     87             });
     88         }
     89 
     90         @Override
     91         public void onAppTransitionFinishedLocked(IBinder token) {
     92             mHandler.post(new Runnable() {
     93                 @Override
     94                 public void run() {
     95                     StatusBarManagerInternal statusbar = LocalServices.getService(
     96                             StatusBarManagerInternal.class);
     97                     if (statusbar != null) {
     98                         statusbar.appTransitionFinished();
     99                     }
    100                 }
    101             });
    102         }
    103     };
    104 
    105     public StatusBarController() {
    106         super("StatusBar",
    107                 View.STATUS_BAR_TRANSIENT,
    108                 View.STATUS_BAR_UNHIDE,
    109                 View.STATUS_BAR_TRANSLUCENT,
    110                 StatusBarManager.WINDOW_STATUS_BAR,
    111                 FLAG_TRANSLUCENT_STATUS,
    112                 View.STATUS_BAR_TRANSPARENT);
    113     }
    114 
    115 
    116     public void setTopAppHidesStatusBar(boolean hidesStatusBar) {
    117         StatusBarManagerInternal statusbar = getStatusBarInternal();
    118         if (statusbar != null) {
    119             statusbar.setTopAppHidesStatusBar(hidesStatusBar);
    120         }
    121     }
    122 
    123     @Override
    124     protected boolean skipAnimation() {
    125         return mWin.getAttrs().height == MATCH_PARENT;
    126     }
    127 
    128     public AppTransitionListener getAppTransitionListener() {
    129         return mAppTransitionListener;
    130     }
    131 
    132     /**
    133      * For a given app transition with {@code openAnimation} and {@code closeAnimation}, this
    134      * calculates the timings for the corresponding status bar transition.
    135      *
    136      * @return the desired start time of the status bar transition, in uptime millis
    137      */
    138     private static long calculateStatusBarTransitionStartTime(Animation openAnimation,
    139             Animation closeAnimation) {
    140         if (openAnimation != null && closeAnimation != null) {
    141             TranslateAnimation openTranslateAnimation = findTranslateAnimation(openAnimation);
    142             TranslateAnimation closeTranslateAnimation = findTranslateAnimation(closeAnimation);
    143             if (openTranslateAnimation != null) {
    144 
    145                 // Some interpolators are extremely quickly mostly finished, but not completely. For
    146                 // our purposes, we need to find the fraction for which ther interpolator is mostly
    147                 // there, and use that value for the calculation.
    148                 float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
    149                 return SystemClock.uptimeMillis()
    150                         + openTranslateAnimation.getStartOffset()
    151                         + (long)(openTranslateAnimation.getDuration()*t) - TRANSITION_DURATION;
    152             } else if (closeTranslateAnimation != null) {
    153                 return SystemClock.uptimeMillis();
    154             } else {
    155                 return SystemClock.uptimeMillis();
    156             }
    157         } else {
    158             return SystemClock.uptimeMillis();
    159         }
    160     }
    161 
    162     /**
    163      * Tries to find a {@link TranslateAnimation} inside the {@code animation}.
    164      *
    165      * @return the found animation, {@code null} otherwise
    166      */
    167     private static TranslateAnimation findTranslateAnimation(Animation animation) {
    168         if (animation instanceof TranslateAnimation) {
    169             return (TranslateAnimation) animation;
    170         } else if (animation instanceof AnimationSet) {
    171             AnimationSet set = (AnimationSet) animation;
    172             for (int i = 0; i < set.getAnimations().size(); i++) {
    173                 Animation a = set.getAnimations().get(i);
    174                 if (a instanceof TranslateAnimation) {
    175                     return (TranslateAnimation) a;
    176                 }
    177             }
    178         }
    179         return null;
    180     }
    181 
    182     /**
    183      * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which
    184      * {@code interpolator(t + eps) > 0.99}.
    185      */
    186     private static float findAlmostThereFraction(Interpolator interpolator) {
    187         float val = 0.5f;
    188         float adj = 0.25f;
    189         while (adj >= 0.01f) {
    190             if (interpolator.getInterpolation(val) < 0.99f) {
    191                 val += adj;
    192             } else {
    193                 val -= adj;
    194             }
    195             adj /= 2;
    196         }
    197         return val;
    198     }
    199 }
    200