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