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.content.ComponentName;
20import android.graphics.Rect;
21import android.os.Bundle;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.Message;
25import android.util.Pair;
26
27import com.android.internal.os.SomeArgs;
28import com.android.internal.statusbar.IStatusBar;
29import com.android.internal.statusbar.StatusBarIcon;
30
31/**
32 * This class takes the functions from IStatusBar that come in on
33 * binder pool threads and posts messages to get them onto the main
34 * thread, and calls onto Callbacks.  It also takes care of
35 * coalescing these calls so they don't stack up.  For the calls
36 * are coalesced, note that they are all idempotent.
37 */
38public class CommandQueue extends IStatusBar.Stub {
39    private static final int INDEX_MASK = 0xffff;
40    private static final int MSG_SHIFT  = 16;
41    private static final int MSG_MASK   = 0xffff << MSG_SHIFT;
42
43    private static final int OP_SET_ICON    = 1;
44    private static final int OP_REMOVE_ICON = 2;
45
46    private static final int MSG_ICON                          = 1 << MSG_SHIFT;
47    private static final int MSG_DISABLE                       = 2 << MSG_SHIFT;
48    private static final int MSG_EXPAND_NOTIFICATIONS          = 3 << MSG_SHIFT;
49    private static final int MSG_COLLAPSE_PANELS               = 4 << MSG_SHIFT;
50    private static final int MSG_EXPAND_SETTINGS               = 5 << MSG_SHIFT;
51    private static final int MSG_SET_SYSTEMUI_VISIBILITY       = 6 << MSG_SHIFT;
52    private static final int MSG_TOP_APP_WINDOW_CHANGED        = 7 << MSG_SHIFT;
53    private static final int MSG_SHOW_IME_BUTTON               = 8 << MSG_SHIFT;
54    private static final int MSG_TOGGLE_RECENT_APPS            = 9 << MSG_SHIFT;
55    private static final int MSG_PRELOAD_RECENT_APPS           = 10 << MSG_SHIFT;
56    private static final int MSG_CANCEL_PRELOAD_RECENT_APPS    = 11 << MSG_SHIFT;
57    private static final int MSG_SET_WINDOW_STATE              = 12 << MSG_SHIFT;
58    private static final int MSG_SHOW_RECENT_APPS              = 13 << MSG_SHIFT;
59    private static final int MSG_HIDE_RECENT_APPS              = 14 << MSG_SHIFT;
60    private static final int MSG_BUZZ_BEEP_BLINKED             = 15 << MSG_SHIFT;
61    private static final int MSG_NOTIFICATION_LIGHT_OFF        = 16 << MSG_SHIFT;
62    private static final int MSG_NOTIFICATION_LIGHT_PULSE      = 17 << MSG_SHIFT;
63    private static final int MSG_SHOW_SCREEN_PIN_REQUEST       = 18 << MSG_SHIFT;
64    private static final int MSG_APP_TRANSITION_PENDING        = 19 << MSG_SHIFT;
65    private static final int MSG_APP_TRANSITION_CANCELLED      = 20 << MSG_SHIFT;
66    private static final int MSG_APP_TRANSITION_STARTING       = 21 << MSG_SHIFT;
67    private static final int MSG_ASSIST_DISCLOSURE             = 22 << MSG_SHIFT;
68    private static final int MSG_START_ASSIST                  = 23 << MSG_SHIFT;
69    private static final int MSG_CAMERA_LAUNCH_GESTURE         = 24 << MSG_SHIFT;
70    private static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS     = 25 << MSG_SHIFT;
71    private static final int MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU = 26 << MSG_SHIFT;
72    private static final int MSG_ADD_QS_TILE                   = 27 << MSG_SHIFT;
73    private static final int MSG_REMOVE_QS_TILE                = 28 << MSG_SHIFT;
74    private static final int MSG_CLICK_QS_TILE                 = 29 << MSG_SHIFT;
75    private static final int MSG_TOGGLE_APP_SPLIT_SCREEN       = 30 << MSG_SHIFT;
76    private static final int MSG_APP_TRANSITION_FINISHED       = 31 << MSG_SHIFT;
77    private static final int MSG_DISMISS_KEYBOARD_SHORTCUTS    = 32 << MSG_SHIFT;
78
79    public static final int FLAG_EXCLUDE_NONE = 0;
80    public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
81    public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
82    public static final int FLAG_EXCLUDE_NOTIFICATION_PANEL = 1 << 2;
83    public static final int FLAG_EXCLUDE_INPUT_METHODS_PANEL = 1 << 3;
84    public static final int FLAG_EXCLUDE_COMPAT_MODE_PANEL = 1 << 4;
85
86    private static final String SHOW_IME_SWITCHER_KEY = "showImeSwitcherKey";
87
88    private final Object mLock = new Object();
89    private Callbacks mCallbacks;
90    private Handler mHandler = new H();
91
92    /**
93     * These methods are called back on the main thread.
94     */
95    public interface Callbacks {
96        void setIcon(String slot, StatusBarIcon icon);
97        void removeIcon(String slot);
98        void disable(int state1, int state2, boolean animate);
99        void animateExpandNotificationsPanel();
100        void animateCollapsePanels(int flags);
101        void animateExpandSettingsPanel(String obj);
102        void setSystemUiVisibility(int vis, int fullscreenStackVis,
103                int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds);
104        void topAppWindowChanged(boolean visible);
105        void setImeWindowStatus(IBinder token, int vis, int backDisposition,
106                boolean showImeSwitcher);
107        void showRecentApps(boolean triggeredFromAltTab, boolean fromHome);
108        void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
109        void toggleRecentApps();
110        void toggleSplitScreen();
111        void preloadRecentApps();
112        void dismissKeyboardShortcutsMenu();
113        void toggleKeyboardShortcutsMenu(int deviceId);
114        void cancelPreloadRecentApps();
115        void setWindowState(int window, int state);
116        void buzzBeepBlinked();
117        void notificationLightOff();
118        void notificationLightPulse(int argb, int onMillis, int offMillis);
119        void showScreenPinningRequest(int taskId);
120        void appTransitionPending();
121        void appTransitionCancelled();
122        void appTransitionStarting(long startTime, long duration);
123        void appTransitionFinished();
124        void showAssistDisclosure();
125        void startAssist(Bundle args);
126        void onCameraLaunchGestureDetected(int source);
127        void showTvPictureInPictureMenu();
128
129        void addQsTile(ComponentName tile);
130        void remQsTile(ComponentName tile);
131        void clickTile(ComponentName tile);
132    }
133
134    public CommandQueue(Callbacks callbacks) {
135        mCallbacks = callbacks;
136    }
137
138    public void setIcon(String slot, StatusBarIcon icon) {
139        synchronized (mLock) {
140            // don't coalesce these
141            mHandler.obtainMessage(MSG_ICON, OP_SET_ICON, 0,
142                    new Pair<String, StatusBarIcon>(slot, icon)).sendToTarget();
143        }
144    }
145
146    public void removeIcon(String slot) {
147        synchronized (mLock) {
148            // don't coalesce these
149            mHandler.obtainMessage(MSG_ICON, OP_REMOVE_ICON, 0, slot).sendToTarget();
150        }
151    }
152
153    public void disable(int state1, int state2) {
154        synchronized (mLock) {
155            mHandler.removeMessages(MSG_DISABLE);
156            mHandler.obtainMessage(MSG_DISABLE, state1, state2, null).sendToTarget();
157        }
158    }
159
160    public void animateExpandNotificationsPanel() {
161        synchronized (mLock) {
162            mHandler.removeMessages(MSG_EXPAND_NOTIFICATIONS);
163            mHandler.sendEmptyMessage(MSG_EXPAND_NOTIFICATIONS);
164        }
165    }
166
167    public void animateCollapsePanels() {
168        synchronized (mLock) {
169            mHandler.removeMessages(MSG_COLLAPSE_PANELS);
170            mHandler.sendEmptyMessage(MSG_COLLAPSE_PANELS);
171        }
172    }
173
174    public void animateExpandSettingsPanel(String subPanel) {
175        synchronized (mLock) {
176            mHandler.removeMessages(MSG_EXPAND_SETTINGS);
177            mHandler.obtainMessage(MSG_EXPAND_SETTINGS, subPanel).sendToTarget();
178        }
179    }
180
181    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
182            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
183        synchronized (mLock) {
184            // Don't coalesce these, since it might have one time flags set such as
185            // STATUS_BAR_UNHIDE which might get lost.
186            SomeArgs args = SomeArgs.obtain();
187            args.argi1 = vis;
188            args.argi2 = fullscreenStackVis;
189            args.argi3 = dockedStackVis;
190            args.argi4 = mask;
191            args.arg1 = fullscreenStackBounds;
192            args.arg2 = dockedStackBounds;
193            mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
194        }
195    }
196
197    public void topAppWindowChanged(boolean menuVisible) {
198        synchronized (mLock) {
199            mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
200            mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
201                    null).sendToTarget();
202        }
203    }
204
205    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
206            boolean showImeSwitcher) {
207        synchronized (mLock) {
208            mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
209            Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token);
210            m.getData().putBoolean(SHOW_IME_SWITCHER_KEY, showImeSwitcher);
211            m.sendToTarget();
212        }
213    }
214
215    public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
216        synchronized (mLock) {
217            mHandler.removeMessages(MSG_SHOW_RECENT_APPS);
218            mHandler.obtainMessage(MSG_SHOW_RECENT_APPS,
219                    triggeredFromAltTab ? 1 : 0, fromHome ? 1 : 0, null).sendToTarget();
220        }
221    }
222
223    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
224        synchronized (mLock) {
225            mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
226            mHandler.obtainMessage(MSG_HIDE_RECENT_APPS,
227                    triggeredFromAltTab ? 1 : 0, triggeredFromHomeKey ? 1 : 0,
228                    null).sendToTarget();
229        }
230    }
231
232    public void toggleSplitScreen() {
233        synchronized (mLock) {
234            mHandler.removeMessages(MSG_TOGGLE_APP_SPLIT_SCREEN);
235            mHandler.obtainMessage(MSG_TOGGLE_APP_SPLIT_SCREEN, 0, 0, null).sendToTarget();
236        }
237    }
238
239    public void toggleRecentApps() {
240        synchronized (mLock) {
241            mHandler.removeMessages(MSG_TOGGLE_RECENT_APPS);
242            mHandler.obtainMessage(MSG_TOGGLE_RECENT_APPS, 0, 0, null).sendToTarget();
243        }
244    }
245
246    public void preloadRecentApps() {
247        synchronized (mLock) {
248            mHandler.removeMessages(MSG_PRELOAD_RECENT_APPS);
249            mHandler.obtainMessage(MSG_PRELOAD_RECENT_APPS, 0, 0, null).sendToTarget();
250        }
251    }
252
253    public void cancelPreloadRecentApps() {
254        synchronized (mLock) {
255            mHandler.removeMessages(MSG_CANCEL_PRELOAD_RECENT_APPS);
256            mHandler.obtainMessage(MSG_CANCEL_PRELOAD_RECENT_APPS, 0, 0, null).sendToTarget();
257        }
258    }
259
260    @Override
261    public void dismissKeyboardShortcutsMenu() {
262        synchronized (mLock) {
263            mHandler.removeMessages(MSG_DISMISS_KEYBOARD_SHORTCUTS);
264            mHandler.obtainMessage(MSG_DISMISS_KEYBOARD_SHORTCUTS).sendToTarget();
265        }
266    }
267
268    @Override
269    public void toggleKeyboardShortcutsMenu(int deviceId) {
270        synchronized (mLock) {
271            mHandler.removeMessages(MSG_TOGGLE_KEYBOARD_SHORTCUTS);
272            mHandler.obtainMessage(MSG_TOGGLE_KEYBOARD_SHORTCUTS, deviceId, 0).sendToTarget();
273        }
274    }
275
276    @Override
277    public void showTvPictureInPictureMenu() {
278        synchronized (mLock) {
279            mHandler.removeMessages(MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU);
280            mHandler.obtainMessage(MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU).sendToTarget();
281        }
282    }
283
284    public void setWindowState(int window, int state) {
285        synchronized (mLock) {
286            // don't coalesce these
287            mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
288        }
289    }
290
291    public void buzzBeepBlinked() {
292        synchronized (mLock) {
293            mHandler.removeMessages(MSG_BUZZ_BEEP_BLINKED);
294            mHandler.sendEmptyMessage(MSG_BUZZ_BEEP_BLINKED);
295        }
296    }
297
298    public void notificationLightOff() {
299        synchronized (mLock) {
300            mHandler.sendEmptyMessage(MSG_NOTIFICATION_LIGHT_OFF);
301        }
302    }
303
304    public void notificationLightPulse(int argb, int onMillis, int offMillis) {
305        synchronized (mLock) {
306            mHandler.obtainMessage(MSG_NOTIFICATION_LIGHT_PULSE, onMillis, offMillis, argb)
307                    .sendToTarget();
308        }
309    }
310
311    public void showScreenPinningRequest(int taskId) {
312        synchronized (mLock) {
313            mHandler.obtainMessage(MSG_SHOW_SCREEN_PIN_REQUEST, taskId, 0, null)
314                    .sendToTarget();
315        }
316    }
317
318    public void appTransitionPending() {
319        synchronized (mLock) {
320            mHandler.removeMessages(MSG_APP_TRANSITION_PENDING);
321            mHandler.sendEmptyMessage(MSG_APP_TRANSITION_PENDING);
322        }
323    }
324
325    public void appTransitionCancelled() {
326        synchronized (mLock) {
327            mHandler.removeMessages(MSG_APP_TRANSITION_PENDING);
328            mHandler.sendEmptyMessage(MSG_APP_TRANSITION_PENDING);
329        }
330    }
331
332    public void appTransitionStarting(long startTime, long duration) {
333        synchronized (mLock) {
334            mHandler.removeMessages(MSG_APP_TRANSITION_STARTING);
335            mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, Pair.create(startTime, duration))
336                    .sendToTarget();
337        }
338    }
339
340    @Override
341    public void appTransitionFinished() {
342        synchronized (mLock) {
343            mHandler.removeMessages(MSG_APP_TRANSITION_FINISHED);
344            mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
345        }
346    }
347
348    public void showAssistDisclosure() {
349        synchronized (mLock) {
350            mHandler.removeMessages(MSG_ASSIST_DISCLOSURE);
351            mHandler.obtainMessage(MSG_ASSIST_DISCLOSURE).sendToTarget();
352        }
353    }
354
355    public void startAssist(Bundle args) {
356        synchronized (mLock) {
357            mHandler.removeMessages(MSG_START_ASSIST);
358            mHandler.obtainMessage(MSG_START_ASSIST, args).sendToTarget();
359        }
360    }
361
362    @Override
363    public void onCameraLaunchGestureDetected(int source) {
364        synchronized (mLock) {
365            mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
366            mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE, source, 0).sendToTarget();
367        }
368    }
369
370    @Override
371    public void addQsTile(ComponentName tile) {
372        synchronized (mLock) {
373            mHandler.obtainMessage(MSG_ADD_QS_TILE, tile).sendToTarget();
374        }
375    }
376
377    @Override
378    public void remQsTile(ComponentName tile) {
379        synchronized (mLock) {
380            mHandler.obtainMessage(MSG_REMOVE_QS_TILE, tile).sendToTarget();
381        }
382    }
383
384    @Override
385    public void clickQsTile(ComponentName tile) {
386        synchronized (mLock) {
387            mHandler.obtainMessage(MSG_CLICK_QS_TILE, tile).sendToTarget();
388        }
389    }
390
391    private final class H extends Handler {
392        public void handleMessage(Message msg) {
393            final int what = msg.what & MSG_MASK;
394            switch (what) {
395                case MSG_ICON: {
396                    switch (msg.arg1) {
397                        case OP_SET_ICON: {
398                            Pair<String, StatusBarIcon> p = (Pair<String, StatusBarIcon>) msg.obj;
399                            mCallbacks.setIcon(p.first, p.second);
400                            break;
401                        }
402                        case OP_REMOVE_ICON:
403                            mCallbacks.removeIcon((String) msg.obj);
404                            break;
405                    }
406                    break;
407                }
408                case MSG_DISABLE:
409                    mCallbacks.disable(msg.arg1, msg.arg2, true /* animate */);
410                    break;
411                case MSG_EXPAND_NOTIFICATIONS:
412                    mCallbacks.animateExpandNotificationsPanel();
413                    break;
414                case MSG_COLLAPSE_PANELS:
415                    mCallbacks.animateCollapsePanels(0);
416                    break;
417                case MSG_EXPAND_SETTINGS:
418                    mCallbacks.animateExpandSettingsPanel((String) msg.obj);
419                    break;
420                case MSG_SET_SYSTEMUI_VISIBILITY:
421                    SomeArgs args = (SomeArgs) msg.obj;
422                    mCallbacks.setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
423                            args.argi4, (Rect) args.arg1, (Rect) args.arg2);
424                    args.recycle();
425                    break;
426                case MSG_TOP_APP_WINDOW_CHANGED:
427                    mCallbacks.topAppWindowChanged(msg.arg1 != 0);
428                    break;
429                case MSG_SHOW_IME_BUTTON:
430                    mCallbacks.setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2,
431                            msg.getData().getBoolean(SHOW_IME_SWITCHER_KEY, false));
432                    break;
433                case MSG_SHOW_RECENT_APPS:
434                    mCallbacks.showRecentApps(msg.arg1 != 0, msg.arg2 != 0);
435                    break;
436                case MSG_HIDE_RECENT_APPS:
437                    mCallbacks.hideRecentApps(msg.arg1 != 0, msg.arg2 != 0);
438                    break;
439                case MSG_TOGGLE_RECENT_APPS:
440                    mCallbacks.toggleRecentApps();
441                    break;
442                case MSG_PRELOAD_RECENT_APPS:
443                    mCallbacks.preloadRecentApps();
444                    break;
445                case MSG_CANCEL_PRELOAD_RECENT_APPS:
446                    mCallbacks.cancelPreloadRecentApps();
447                    break;
448                case MSG_DISMISS_KEYBOARD_SHORTCUTS:
449                    mCallbacks.dismissKeyboardShortcutsMenu();
450                    break;
451                case MSG_TOGGLE_KEYBOARD_SHORTCUTS:
452                    mCallbacks.toggleKeyboardShortcutsMenu(msg.arg1);
453                    break;
454                case MSG_SET_WINDOW_STATE:
455                    mCallbacks.setWindowState(msg.arg1, msg.arg2);
456                    break;
457                case MSG_BUZZ_BEEP_BLINKED:
458                    mCallbacks.buzzBeepBlinked();
459                    break;
460                case MSG_NOTIFICATION_LIGHT_OFF:
461                    mCallbacks.notificationLightOff();
462                    break;
463                case MSG_NOTIFICATION_LIGHT_PULSE:
464                    mCallbacks.notificationLightPulse((Integer) msg.obj, msg.arg1, msg.arg2);
465                    break;
466                case MSG_SHOW_SCREEN_PIN_REQUEST:
467                    mCallbacks.showScreenPinningRequest(msg.arg1);
468                    break;
469                case MSG_APP_TRANSITION_PENDING:
470                    mCallbacks.appTransitionPending();
471                    break;
472                case MSG_APP_TRANSITION_CANCELLED:
473                    mCallbacks.appTransitionCancelled();
474                    break;
475                case MSG_APP_TRANSITION_STARTING:
476                    Pair<Long, Long> data = (Pair<Long, Long>) msg.obj;
477                    mCallbacks.appTransitionStarting(data.first, data.second);
478                    break;
479                case MSG_APP_TRANSITION_FINISHED:
480                    mCallbacks.appTransitionFinished();
481                    break;
482                case MSG_ASSIST_DISCLOSURE:
483                    mCallbacks.showAssistDisclosure();
484                    break;
485                case MSG_START_ASSIST:
486                    mCallbacks.startAssist((Bundle) msg.obj);
487                    break;
488                case MSG_CAMERA_LAUNCH_GESTURE:
489                    mCallbacks.onCameraLaunchGestureDetected(msg.arg1);
490                    break;
491                case MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU:
492                    mCallbacks.showTvPictureInPictureMenu();
493                    break;
494                case MSG_ADD_QS_TILE:
495                    mCallbacks.addQsTile((ComponentName) msg.obj);
496                    break;
497                case MSG_REMOVE_QS_TILE:
498                    mCallbacks.remQsTile((ComponentName) msg.obj);
499                    break;
500                case MSG_CLICK_QS_TILE:
501                    mCallbacks.clickTile((ComponentName) msg.obj);
502                    break;
503                case MSG_TOGGLE_APP_SPLIT_SCREEN:
504                    mCallbacks.toggleSplitScreen();
505                    break;
506            }
507        }
508    }
509}
510
511