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