StatusBarController.java revision 3a49ba2e402701170a61f18632652d1a9379cf15
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
17package com.android.server.policy;
18
19import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
20import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
21import static android.view.WindowManagerInternal.AppTransitionListener;
22
23import android.app.StatusBarManager;
24import android.os.IBinder;
25import android.os.SystemClock;
26import android.view.View;
27import android.view.animation.Animation;
28import android.view.animation.AnimationSet;
29import android.view.animation.Interpolator;
30import android.view.animation.TranslateAnimation;
31
32import com.android.server.LocalServices;
33import com.android.server.statusbar.StatusBarManagerInternal;
34
35/**
36 * Implements status bar specific behavior.
37 */
38public 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