• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
StatusBarController()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
skipAnimation()115     protected boolean skipAnimation() {
116         return mWin.getAttrs().height == MATCH_PARENT;
117     }
118 
getAppTransitionListener()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      */
calculateStatusBarTransitionStartTime(Animation openAnimation, Animation closeAnimation)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      */
findTranslateAnimation(Animation animation)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      */
findAlmostThereFraction(Interpolator interpolator)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