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