1/*
2 * Copyright (C) 2010 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.systemui.statusbar;
18
19import android.os.Handler;
20import android.os.IBinder;
21import android.os.Message;
22
23import com.android.internal.statusbar.IStatusBar;
24import com.android.internal.statusbar.StatusBarIcon;
25import com.android.internal.statusbar.StatusBarIconList;
26
27/**
28 * This class takes the functions from IStatusBar that come in on
29 * binder pool threads and posts messages to get them onto the main
30 * thread, and calls onto Callbacks.  It also takes care of
31 * coalescing these calls so they don't stack up.  For the calls
32 * are coalesced, note that they are all idempotent.
33 */
34public class CommandQueue extends IStatusBar.Stub {
35    private static final int INDEX_MASK = 0xffff;
36    private static final int MSG_SHIFT  = 16;
37    private static final int MSG_MASK   = 0xffff << MSG_SHIFT;
38
39    private static final int OP_SET_ICON    = 1;
40    private static final int OP_REMOVE_ICON = 2;
41
42    private static final int MSG_ICON                       = 1 << MSG_SHIFT;
43    private static final int MSG_DISABLE                    = 2 << MSG_SHIFT;
44    private static final int MSG_EXPAND_NOTIFICATIONS       = 3 << MSG_SHIFT;
45    private static final int MSG_COLLAPSE_PANELS            = 4 << MSG_SHIFT;
46    private static final int MSG_EXPAND_SETTINGS            = 5 << MSG_SHIFT;
47    private static final int MSG_SET_SYSTEMUI_VISIBILITY    = 6 << MSG_SHIFT;
48    private static final int MSG_TOP_APP_WINDOW_CHANGED     = 7 << MSG_SHIFT;
49    private static final int MSG_SHOW_IME_BUTTON            = 8 << MSG_SHIFT;
50    private static final int MSG_TOGGLE_RECENT_APPS         = 9 << MSG_SHIFT;
51    private static final int MSG_PRELOAD_RECENT_APPS        = 10 << MSG_SHIFT;
52    private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 11 << MSG_SHIFT;
53    private static final int MSG_SET_WINDOW_STATE           = 12 << MSG_SHIFT;
54    private static final int MSG_SHOW_RECENT_APPS           = 13 << MSG_SHIFT;
55    private static final int MSG_HIDE_RECENT_APPS           = 14 << MSG_SHIFT;
56    private static final int MSG_BUZZ_BEEP_BLINKED          = 15 << MSG_SHIFT;
57    private static final int MSG_NOTIFICATION_LIGHT_OFF     = 16 << MSG_SHIFT;
58    private static final int MSG_NOTIFICATION_LIGHT_PULSE   = 17 << MSG_SHIFT;
59    private static final int MSG_SHOW_SCREEN_PIN_REQUEST    = 18 << MSG_SHIFT;
60
61    public static final int FLAG_EXCLUDE_NONE = 0;
62    public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
63    public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
64    public static final int FLAG_EXCLUDE_NOTIFICATION_PANEL = 1 << 2;
65    public static final int FLAG_EXCLUDE_INPUT_METHODS_PANEL = 1 << 3;
66    public static final int FLAG_EXCLUDE_COMPAT_MODE_PANEL = 1 << 4;
67
68    private static final String SHOW_IME_SWITCHER_KEY = "showImeSwitcherKey";
69
70    private StatusBarIconList mList;
71    private Callbacks mCallbacks;
72    private Handler mHandler = new H();
73
74    /**
75     * These methods are called back on the main thread.
76     */
77    public interface Callbacks {
78        public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon);
79        public void updateIcon(String slot, int index, int viewIndex,
80                StatusBarIcon old, StatusBarIcon icon);
81        public void removeIcon(String slot, int index, int viewIndex);
82        public void disable(int state, boolean animate);
83        public void animateExpandNotificationsPanel();
84        public void animateCollapsePanels(int flags);
85        public void animateExpandSettingsPanel();
86        public void setSystemUiVisibility(int vis, int mask);
87        public void topAppWindowChanged(boolean visible);
88        public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
89                boolean showImeSwitcher);
90        public void showRecentApps(boolean triggeredFromAltTab);
91        public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
92        public void toggleRecentApps();
93        public void preloadRecentApps();
94        public void cancelPreloadRecentApps();
95        public void showSearchPanel();
96        public void hideSearchPanel();
97        public void setWindowState(int window, int state);
98        public void buzzBeepBlinked();
99        public void notificationLightOff();
100        public void notificationLightPulse(int argb, int onMillis, int offMillis);
101        public void showScreenPinningRequest();
102    }
103
104    public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
105        mCallbacks = callbacks;
106        mList = list;
107    }
108
109    public void setIcon(int index, StatusBarIcon icon) {
110        synchronized (mList) {
111            int what = MSG_ICON | index;
112            mHandler.removeMessages(what);
113            mHandler.obtainMessage(what, OP_SET_ICON, 0, icon.clone()).sendToTarget();
114        }
115    }
116
117    public void removeIcon(int index) {
118        synchronized (mList) {
119            int what = MSG_ICON | index;
120            mHandler.removeMessages(what);
121            mHandler.obtainMessage(what, OP_REMOVE_ICON, 0, null).sendToTarget();
122        }
123    }
124
125    public void disable(int state) {
126        synchronized (mList) {
127            mHandler.removeMessages(MSG_DISABLE);
128            mHandler.obtainMessage(MSG_DISABLE, state, 0, null).sendToTarget();
129        }
130    }
131
132    public void animateExpandNotificationsPanel() {
133        synchronized (mList) {
134            mHandler.removeMessages(MSG_EXPAND_NOTIFICATIONS);
135            mHandler.sendEmptyMessage(MSG_EXPAND_NOTIFICATIONS);
136        }
137    }
138
139    public void animateCollapsePanels() {
140        synchronized (mList) {
141            mHandler.removeMessages(MSG_COLLAPSE_PANELS);
142            mHandler.sendEmptyMessage(MSG_COLLAPSE_PANELS);
143        }
144    }
145
146    public void animateExpandSettingsPanel() {
147        synchronized (mList) {
148            mHandler.removeMessages(MSG_EXPAND_SETTINGS);
149            mHandler.sendEmptyMessage(MSG_EXPAND_SETTINGS);
150        }
151    }
152
153    public void setSystemUiVisibility(int vis, int mask) {
154        synchronized (mList) {
155            mHandler.removeMessages(MSG_SET_SYSTEMUI_VISIBILITY);
156            mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget();
157        }
158    }
159
160    public void topAppWindowChanged(boolean menuVisible) {
161        synchronized (mList) {
162            mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
163            mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
164                    null).sendToTarget();
165        }
166    }
167
168    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
169            boolean showImeSwitcher) {
170        synchronized (mList) {
171            mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
172            Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token);
173            m.getData().putBoolean(SHOW_IME_SWITCHER_KEY, showImeSwitcher);
174            m.sendToTarget();
175        }
176    }
177
178    public void showRecentApps(boolean triggeredFromAltTab) {
179        synchronized (mList) {
180            mHandler.removeMessages(MSG_SHOW_RECENT_APPS);
181            mHandler.obtainMessage(MSG_SHOW_RECENT_APPS,
182                    triggeredFromAltTab ? 1 : 0, 0, null).sendToTarget();
183        }
184    }
185
186    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
187        synchronized (mList) {
188            mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
189            mHandler.obtainMessage(MSG_HIDE_RECENT_APPS,
190                    triggeredFromAltTab ? 1 : 0, triggeredFromHomeKey ? 1 : 0,
191                    null).sendToTarget();
192        }
193    }
194
195    public void toggleRecentApps() {
196        synchronized (mList) {
197            mHandler.removeMessages(MSG_TOGGLE_RECENT_APPS);
198            mHandler.obtainMessage(MSG_TOGGLE_RECENT_APPS, 0, 0, null).sendToTarget();
199        }
200    }
201
202    public void preloadRecentApps() {
203        synchronized (mList) {
204            mHandler.removeMessages(MSG_PRELOAD_RECENT_APPS);
205            mHandler.obtainMessage(MSG_PRELOAD_RECENT_APPS, 0, 0, null).sendToTarget();
206        }
207    }
208
209    public void cancelPreloadRecentApps() {
210        synchronized (mList) {
211            mHandler.removeMessages(MSG_CANCEL_PRELOAD_RECENT_APPS);
212            mHandler.obtainMessage(MSG_CANCEL_PRELOAD_RECENT_APPS, 0, 0, null).sendToTarget();
213        }
214    }
215
216    public void setWindowState(int window, int state) {
217        synchronized (mList) {
218            // don't coalesce these
219            mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
220        }
221    }
222
223    public void buzzBeepBlinked() {
224        synchronized (mList) {
225            mHandler.removeMessages(MSG_BUZZ_BEEP_BLINKED);
226            mHandler.sendEmptyMessage(MSG_BUZZ_BEEP_BLINKED);
227        }
228    }
229
230    public void notificationLightOff() {
231        synchronized (mList) {
232            mHandler.sendEmptyMessage(MSG_NOTIFICATION_LIGHT_OFF);
233        }
234    }
235
236    public void notificationLightPulse(int argb, int onMillis, int offMillis) {
237        synchronized (mList) {
238            mHandler.obtainMessage(MSG_NOTIFICATION_LIGHT_PULSE, onMillis, offMillis, argb)
239                    .sendToTarget();
240        }
241    }
242
243    public void showScreenPinningRequest() {
244        synchronized (mList) {
245            mHandler.sendEmptyMessage(MSG_SHOW_SCREEN_PIN_REQUEST);
246        }
247    }
248
249    private final class H extends Handler {
250        public void handleMessage(Message msg) {
251            final int what = msg.what & MSG_MASK;
252            switch (what) {
253                case MSG_ICON: {
254                    final int index = msg.what & INDEX_MASK;
255                    final int viewIndex = mList.getViewIndex(index);
256                    switch (msg.arg1) {
257                        case OP_SET_ICON: {
258                            StatusBarIcon icon = (StatusBarIcon)msg.obj;
259                            StatusBarIcon old = mList.getIcon(index);
260                            if (old == null) {
261                                mList.setIcon(index, icon);
262                                mCallbacks.addIcon(mList.getSlot(index), index, viewIndex, icon);
263                            } else {
264                                mList.setIcon(index, icon);
265                                mCallbacks.updateIcon(mList.getSlot(index), index, viewIndex,
266                                        old, icon);
267                            }
268                            break;
269                        }
270                        case OP_REMOVE_ICON:
271                            if (mList.getIcon(index) != null) {
272                                mList.removeIcon(index);
273                                mCallbacks.removeIcon(mList.getSlot(index), index, viewIndex);
274                            }
275                            break;
276                    }
277                    break;
278                }
279                case MSG_DISABLE:
280                    mCallbacks.disable(msg.arg1, true /* animate */);
281                    break;
282                case MSG_EXPAND_NOTIFICATIONS:
283                    mCallbacks.animateExpandNotificationsPanel();
284                    break;
285                case MSG_COLLAPSE_PANELS:
286                    mCallbacks.animateCollapsePanels(0);
287                    break;
288                case MSG_EXPAND_SETTINGS:
289                    mCallbacks.animateExpandSettingsPanel();
290                    break;
291                case MSG_SET_SYSTEMUI_VISIBILITY:
292                    mCallbacks.setSystemUiVisibility(msg.arg1, msg.arg2);
293                    break;
294                case MSG_TOP_APP_WINDOW_CHANGED:
295                    mCallbacks.topAppWindowChanged(msg.arg1 != 0);
296                    break;
297                case MSG_SHOW_IME_BUTTON:
298                    mCallbacks.setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2,
299                            msg.getData().getBoolean(SHOW_IME_SWITCHER_KEY, false));
300                    break;
301                case MSG_SHOW_RECENT_APPS:
302                    mCallbacks.showRecentApps(msg.arg1 != 0);
303                    break;
304                case MSG_HIDE_RECENT_APPS:
305                    mCallbacks.hideRecentApps(msg.arg1 != 0, msg.arg2 != 0);
306                    break;
307                case MSG_TOGGLE_RECENT_APPS:
308                    mCallbacks.toggleRecentApps();
309                    break;
310                case MSG_PRELOAD_RECENT_APPS:
311                    mCallbacks.preloadRecentApps();
312                    break;
313                case MSG_CANCEL_PRELOAD_RECENT_APPS:
314                    mCallbacks.cancelPreloadRecentApps();
315                    break;
316                case MSG_SET_WINDOW_STATE:
317                    mCallbacks.setWindowState(msg.arg1, msg.arg2);
318                    break;
319                case MSG_BUZZ_BEEP_BLINKED:
320                    mCallbacks.buzzBeepBlinked();
321                    break;
322                case MSG_NOTIFICATION_LIGHT_OFF:
323                    mCallbacks.notificationLightOff();
324                    break;
325                case MSG_NOTIFICATION_LIGHT_PULSE:
326                    mCallbacks.notificationLightPulse((Integer) msg.obj, msg.arg1, msg.arg2);
327                    break;
328                case MSG_SHOW_SCREEN_PIN_REQUEST:
329                    mCallbacks.showScreenPinningRequest();
330                    break;
331            }
332        }
333    }
334}
335
336