StatusBarController.java revision fe762344f4475a3a336bb46aef2d59c1fabf32ab
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    @Override
116    protected boolean skipAnimation() {
117        return mWin.getAttrs().height == MATCH_PARENT;
118    }
119
120    public AppTransitionListener getAppTransitionListener() {
121        return mAppTransitionListener;
122    }
123
124    /**
125     * For a given app transition with {@code openAnimation} and {@code closeAnimation}, this
126     * calculates the timings for the corresponding status bar transition.
127     *
128     * @return the desired start time of the status bar transition, in uptime millis
129     */
130    private static long calculateStatusBarTransitionStartTime(Animation openAnimation,
131            Animation closeAnimation) {
132        if (openAnimation != null && closeAnimation != null) {
133            TranslateAnimation openTranslateAnimation = findTranslateAnimation(openAnimation);
134            TranslateAnimation closeTranslateAnimation = findTranslateAnimation(closeAnimation);
135            if (openTranslateAnimation != null) {
136
137                // Some interpolators are extremely quickly mostly finished, but not completely. For
138                // our purposes, we need to find the fraction for which ther interpolator is mostly
139                // there, and use that value for the calculation.
140                float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
141                return SystemClock.uptimeMillis()
142                        + openTranslateAnimation.getStartOffset()
143                        + (long)(openTranslateAnimation.getDuration()*t) - TRANSITION_DURATION;
144            } else if (closeTranslateAnimation != null) {
145                return SystemClock.uptimeMillis();
146            } else {
147                return SystemClock.uptimeMillis();
148            }
149        } else {
150            return SystemClock.uptimeMillis();
151        }
152    }
153
154    /**
155     * Tries to find a {@link TranslateAnimation} inside the {@code animation}.
156     *
157     * @return the found animation, {@code null} otherwise
158     */
159    private static TranslateAnimation findTranslateAnimation(Animation animation) {
160        if (animation instanceof TranslateAnimation) {
161            return (TranslateAnimation) animation;
162        } else if (animation instanceof AnimationSet) {
163            AnimationSet set = (AnimationSet) animation;
164            for (int i = 0; i < set.getAnimations().size(); i++) {
165                Animation a = set.getAnimations().get(i);
166                if (a instanceof TranslateAnimation) {
167                    return (TranslateAnimation) a;
168                }
169            }
170        }
171        return null;
172    }
173
174    /**
175     * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which
176     * {@code interpolator(t + eps) > 0.99}.
177     */
178    private static float findAlmostThereFraction(Interpolator interpolator) {
179        float val = 0.5f;
180        float adj = 0.25f;
181        while (adj >= 0.01f) {
182            if (interpolator.getInterpolation(val) < 0.99f) {
183                val += adj;
184            } else {
185                val -= adj;
186            }
187            adj /= 2;
188        }
189        return val;
190    }
191}
192