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