PhoneWindow.java revision 63042d6a0347017fcc4cd98560ca3407044c165c
1/*
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16package com.android.internal.policy.impl;
17
18import static android.view.View.MeasureSpec.AT_MOST;
19import static android.view.View.MeasureSpec.EXACTLY;
20import static android.view.View.MeasureSpec.getMode;
21import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
22import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
23import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
24import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
25import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
26import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
27import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
28
29import com.android.internal.view.BaseSurfaceHolder;
30import com.android.internal.view.RootViewSurfaceTaker;
31import com.android.internal.view.StandaloneActionMode;
32import com.android.internal.view.menu.ActionMenuView;
33import com.android.internal.view.menu.ContextMenuBuilder;
34import com.android.internal.view.menu.MenuBuilder;
35import com.android.internal.view.menu.MenuDialogHelper;
36import com.android.internal.view.menu.MenuItemImpl;
37import com.android.internal.view.menu.MenuPopupHelper;
38import com.android.internal.view.menu.MenuView;
39import com.android.internal.view.menu.SubMenuBuilder;
40import com.android.internal.widget.ActionBarContextView;
41import com.android.internal.widget.ActionBarView;
42
43import android.app.KeyguardManager;
44import android.content.Context;
45import android.content.res.Configuration;
46import android.content.res.TypedArray;
47import android.graphics.Canvas;
48import android.graphics.PixelFormat;
49import android.graphics.Rect;
50import android.graphics.drawable.Drawable;
51import android.media.AudioManager;
52import android.net.Uri;
53import android.os.Bundle;
54import android.os.Parcel;
55import android.os.Parcelable;
56import android.util.AndroidRuntimeException;
57import android.util.Config;
58import android.util.DisplayMetrics;
59import android.util.EventLog;
60import android.util.Log;
61import android.util.SparseArray;
62import android.util.TypedValue;
63import android.view.ActionMode;
64import android.view.Gravity;
65import android.view.InputQueue;
66import android.view.KeyCharacterMap;
67import android.view.KeyEvent;
68import android.view.LayoutInflater;
69import android.view.Menu;
70import android.view.MenuItem;
71import android.view.MotionEvent;
72import android.view.SurfaceHolder;
73import android.view.View;
74import android.view.ViewGroup;
75import android.view.ViewManager;
76import android.view.ViewStub;
77import android.view.Window;
78import android.view.WindowManager;
79import android.view.View.MeasureSpec;
80import android.view.accessibility.AccessibilityEvent;
81import android.view.accessibility.AccessibilityManager;
82import android.view.animation.Animation;
83import android.view.animation.AnimationUtils;
84import android.widget.FrameLayout;
85import android.widget.ImageView;
86import android.widget.PopupWindow;
87import android.widget.ProgressBar;
88import android.widget.TextView;
89
90/**
91 * Android-specific Window.
92 * <p>
93 * todo: need to pull the generic functionality out into a base class
94 * in android.widget.
95 */
96public class PhoneWindow extends Window implements MenuBuilder.Callback {
97
98    private final static String TAG = "PhoneWindow";
99
100    private final static boolean SWEEP_OPEN_MENU = false;
101
102    /**
103     * Simple callback used by the context menu and its submenus. The options
104     * menu submenus do not use this (their behavior is more complex).
105     */
106    final DialogMenuCallback mContextMenuCallback = new DialogMenuCallback(FEATURE_CONTEXT_MENU);
107
108    final TypedValue mMinWidthMajor = new TypedValue();
109    final TypedValue mMinWidthMinor = new TypedValue();
110
111    // This is the top-level view of the window, containing the window decor.
112    private DecorView mDecor;
113
114    // This is the view in which the window contents are placed. It is either
115    // mDecor itself, or a child of mDecor where the contents go.
116    private ViewGroup mContentParent;
117
118    SurfaceHolder.Callback2 mTakeSurfaceCallback;
119
120    InputQueue.Callback mTakeInputQueueCallback;
121
122    private boolean mIsFloating;
123
124    private LayoutInflater mLayoutInflater;
125
126    private TextView mTitleView;
127
128    private ActionBarView mActionBar;
129
130    private DrawableFeatureState[] mDrawables;
131
132    private PanelFeatureState[] mPanels;
133
134    /**
135     * The panel that is prepared or opened (the most recent one if there are
136     * multiple panels). Shortcuts will go to this panel. It gets set in
137     * {@link #preparePanel} and cleared in {@link #closePanel}.
138     */
139    private PanelFeatureState mPreparedPanel;
140
141    /**
142     * The keycode that is currently held down (as a modifier) for chording. If
143     * this is 0, there is no key held down.
144     */
145    private int mPanelChordingKey;
146    private boolean mPanelMayLongPress;
147
148    private ImageView mLeftIconView;
149
150    private ImageView mRightIconView;
151
152    private ProgressBar mCircularProgressBar;
153
154    private ProgressBar mHorizontalProgressBar;
155
156    private int mBackgroundResource = 0;
157
158    private Drawable mBackgroundDrawable;
159
160    private int mFrameResource = 0;
161
162    private int mTextColor = 0;
163
164    private CharSequence mTitle = null;
165
166    private int mTitleColor = 0;
167
168    private boolean mAlwaysReadCloseOnTouchAttr = false;
169
170    private ContextMenuBuilder mContextMenu;
171    private MenuDialogHelper mContextMenuHelper;
172    private ActionButtonSubmenu mActionButtonPopup;
173    private boolean mClosingActionMenu;
174
175    private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
176
177    private AudioManager mAudioManager;
178    private KeyguardManager mKeyguardManager;
179
180    public PhoneWindow(Context context) {
181        super(context);
182        mLayoutInflater = LayoutInflater.from(context);
183    }
184
185    @Override
186    public final void setContainer(Window container) {
187        super.setContainer(container);
188    }
189
190    @Override
191    public boolean requestFeature(int featureId) {
192        if (mContentParent != null) {
193            throw new AndroidRuntimeException("requestFeature() must be called before adding content");
194        }
195        final int features = getFeatures();
196        if ((features != DEFAULT_FEATURES) && (featureId == FEATURE_CUSTOM_TITLE)) {
197
198            /* Another feature is enabled and the user is trying to enable the custom title feature */
199            throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
200        }
201        if (((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) &&
202                (featureId != FEATURE_CUSTOM_TITLE) && (featureId != FEATURE_ACTION_MODE_OVERLAY)) {
203
204            /* Custom title feature is enabled and the user is trying to enable another feature */
205            throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
206        }
207        if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) {
208            return false; // Ignore. No title dominates.
209        }
210        if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_NO_TITLE) {
211            // Remove the action bar feature if we have no title. No title dominates.
212            removeFeature(FEATURE_ACTION_BAR);
213        }
214        return super.requestFeature(featureId);
215    }
216
217    @Override
218    public void setContentView(int layoutResID) {
219        if (mContentParent == null) {
220            installDecor();
221        } else {
222            mContentParent.removeAllViews();
223        }
224        mLayoutInflater.inflate(layoutResID, mContentParent);
225        final Callback cb = getCallback();
226        if (cb != null) {
227            cb.onContentChanged();
228        }
229    }
230
231    @Override
232    public void setContentView(View view) {
233        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
234    }
235
236    @Override
237    public void setContentView(View view, ViewGroup.LayoutParams params) {
238        if (mContentParent == null) {
239            installDecor();
240        } else {
241            mContentParent.removeAllViews();
242        }
243        mContentParent.addView(view, params);
244        final Callback cb = getCallback();
245        if (cb != null) {
246            cb.onContentChanged();
247        }
248    }
249
250    @Override
251    public void addContentView(View view, ViewGroup.LayoutParams params) {
252        if (mContentParent == null) {
253            installDecor();
254        }
255        mContentParent.addView(view, params);
256        final Callback cb = getCallback();
257        if (cb != null) {
258            cb.onContentChanged();
259        }
260    }
261
262    @Override
263    public View getCurrentFocus() {
264        return mDecor != null ? mDecor.findFocus() : null;
265    }
266
267    @Override
268    public void takeSurface(SurfaceHolder.Callback2 callback) {
269        mTakeSurfaceCallback = callback;
270    }
271
272    public void takeInputQueue(InputQueue.Callback callback) {
273        mTakeInputQueueCallback = callback;
274    }
275
276    @Override
277    public boolean isFloating() {
278        return mIsFloating;
279    }
280
281    /**
282     * Return a LayoutInflater instance that can be used to inflate XML view layout
283     * resources for use in this Window.
284     *
285     * @return LayoutInflater The shared LayoutInflater.
286     */
287    @Override
288    public LayoutInflater getLayoutInflater() {
289        return mLayoutInflater;
290    }
291
292    @Override
293    public void setTitle(CharSequence title) {
294        if (mTitleView != null) {
295            mTitleView.setText(title);
296        } else if (mActionBar != null) {
297            mActionBar.setWindowTitle(title);
298        }
299        mTitle = title;
300    }
301
302    @Override
303    public void setTitleColor(int textColor) {
304        if (mTitleView != null) {
305            mTitleView.setTextColor(textColor);
306        }
307        mTitleColor = textColor;
308    }
309
310    /**
311     * Prepares the panel to either be opened or chorded. This creates the Menu
312     * instance for the panel and populates it via the Activity callbacks.
313     *
314     * @param st The panel state to prepare.
315     * @param event The event that triggered the preparing of the panel.
316     * @return Whether the panel was prepared. If the panel should not be shown,
317     *         returns false.
318     */
319    public final boolean preparePanel(PanelFeatureState st, KeyEvent event) {
320        // Already prepared (isPrepared will be reset to false later)
321        if (st.isPrepared)
322            return true;
323
324        if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
325            // Another Panel is prepared and possibly open, so close it
326            closePanel(mPreparedPanel, false);
327        }
328
329        final Callback cb = getCallback();
330
331        if (cb != null) {
332            st.createdPanelView = cb.onCreatePanelView(st.featureId);
333        }
334
335        if (st.createdPanelView == null) {
336            // Init the panel state's menu--return false if init failed
337            if (st.menu == null || st.refreshMenuContent) {
338                if (st.menu == null) {
339                    if (!initializePanelMenu(st) || (st.menu == null)) {
340                        return false;
341                    }
342                }
343                // Call callback, and return if it doesn't want to display menu
344                if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) {
345                    // Ditch the menu created above
346                    st.menu = null;
347
348                    return false;
349                }
350
351                st.refreshMenuContent = false;
352
353                if (mActionBar != null) {
354                    mActionBar.setMenu(st.menu);
355                }
356            }
357
358            // Callback and return if the callback does not want to show the menu
359            if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) {
360                return false;
361            }
362
363            // Set the proper keymap
364            KeyCharacterMap kmap = KeyCharacterMap.load(
365                    event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
366            st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;
367            st.menu.setQwertyMode(st.qwertyMode);
368        }
369
370        // Set other state
371        st.isPrepared = true;
372        st.isHandled = false;
373        mPreparedPanel = st;
374
375        return true;
376    }
377
378    @Override
379    public void onConfigurationChanged(Configuration newConfig) {
380        // Action bars handle their own menu state
381        if (mActionBar == null) {
382            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
383            if ((st != null) && (st.menu != null)) {
384                final MenuBuilder menuBuilder = (MenuBuilder) st.menu;
385
386                if (st.isOpen) {
387                    // Freeze state
388                    final Bundle state = new Bundle();
389                    menuBuilder.saveHierarchyState(state);
390
391                    // Remove the menu views since they need to be recreated
392                    // according to the new configuration
393                    clearMenuViews(st);
394
395                    // Re-open the same menu
396                    reopenMenu(false);
397
398                    // Restore state
399                    menuBuilder.restoreHierarchyState(state);
400
401                } else {
402                    // Clear menu views so on next menu opening, it will use
403                    // the proper layout
404                    clearMenuViews(st);
405                }
406            }
407        }
408    }
409
410    private static void clearMenuViews(PanelFeatureState st) {
411        // This can be called on config changes, so we should make sure
412        // the views will be reconstructed based on the new orientation, etc.
413
414        // Allow the callback to create a new panel view
415        st.createdPanelView = null;
416
417        // Causes the decor view to be recreated
418        st.refreshDecorView = true;
419
420        ((MenuBuilder) st.menu).clearMenuViews();
421    }
422
423    @Override
424    public final void openPanel(int featureId, KeyEvent event) {
425        if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
426                mActionBar.isOverflowReserved()) {
427            // Invalidate the options menu, we want a prepare event that the app can respond to.
428            invalidatePanelMenu(FEATURE_OPTIONS_PANEL);
429            mActionBar.showOverflowMenu();
430        } else {
431            openPanel(getPanelState(featureId, true), event);
432        }
433    }
434
435    private void openPanel(PanelFeatureState st, KeyEvent event) {
436        // System.out.println("Open panel: isOpen=" + st.isOpen);
437
438        // Already open, return
439        if (st.isOpen) {
440            return;
441        }
442
443        // Don't open an options panel for honeycomb apps on xlarge devices.
444        // (The app should be using an action bar for menu items.)
445        if (st.featureId == FEATURE_OPTIONS_PANEL) {
446            Context context = getContext();
447            Configuration config = context.getResources().getConfiguration();
448            boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) ==
449                    Configuration.SCREENLAYOUT_SIZE_XLARGE;
450            boolean isHoneycombApp = context.getApplicationInfo().targetSdkVersion >=
451                    android.os.Build.VERSION_CODES.HONEYCOMB;
452
453            if (isXLarge && isHoneycombApp) {
454                return;
455            }
456        }
457
458        Callback cb = getCallback();
459        if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) {
460            // Callback doesn't want the menu to open, reset any state
461            closePanel(st, true);
462            return;
463        }
464
465        final WindowManager wm = getWindowManager();
466        if (wm == null) {
467            return;
468        }
469
470        // Prepare panel (should have been done before, but just in case)
471        if (!preparePanel(st, event)) {
472            return;
473        }
474
475        if (st.decorView == null || st.refreshDecorView) {
476            if (st.decorView == null) {
477                // Initialize the panel decor, this will populate st.decorView
478                if (!initializePanelDecor(st) || (st.decorView == null))
479                    return;
480            } else if (st.refreshDecorView && (st.decorView.getChildCount() > 0)) {
481                // Decor needs refreshing, so remove its views
482                st.decorView.removeAllViews();
483            }
484
485            // This will populate st.shownPanelView
486            if (!initializePanelContent(st) || (st.shownPanelView == null)) {
487                return;
488            }
489
490            ViewGroup.LayoutParams lp = st.shownPanelView.getLayoutParams();
491            if (lp == null) {
492                lp = new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
493            }
494
495            int backgroundResId;
496            if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
497                // If the contents is fill parent for the width, set the
498                // corresponding background
499                backgroundResId = st.fullBackground;
500            } else {
501                // Otherwise, set the normal panel background
502                backgroundResId = st.background;
503            }
504            st.decorView.setWindowBackground(getContext().getResources().getDrawable(
505                    backgroundResId));
506
507
508            st.decorView.addView(st.shownPanelView, lp);
509
510            /*
511             * Give focus to the view, if it or one of its children does not
512             * already have it.
513             */
514            if (!st.shownPanelView.hasFocus()) {
515                st.shownPanelView.requestFocus();
516            }
517        }
518
519        st.isOpen = true;
520        st.isHandled = false;
521
522        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
523                WRAP_CONTENT, WRAP_CONTENT,
524                st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG,
525                WindowManager.LayoutParams.FLAG_DITHER
526                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
527                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
528                st.decorView.mDefaultOpacity);
529
530        lp.gravity = st.gravity;
531        lp.windowAnimations = st.windowAnimations;
532
533        wm.addView(st.decorView, lp);
534        // Log.v(TAG, "Adding main menu to window manager.");
535    }
536
537    @Override
538    public final void closePanel(int featureId) {
539        if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
540                mActionBar.isOverflowReserved()) {
541            mActionBar.hideOverflowMenu();
542        } else if (featureId == FEATURE_CONTEXT_MENU) {
543            closeContextMenu();
544        } else {
545            closePanel(getPanelState(featureId, true), true);
546        }
547    }
548
549    /**
550     * Closes the given panel.
551     *
552     * @param st The panel to be closed.
553     * @param doCallback Whether to notify the callback that the panel was
554     *            closed. If the panel is in the process of re-opening or
555     *            opening another panel (e.g., menu opening a sub menu), the
556     *            callback should not happen and this variable should be false.
557     *            In addition, this method internally will only perform the
558     *            callback if the panel is open.
559     */
560    public final void closePanel(PanelFeatureState st, boolean doCallback) {
561        // System.out.println("Close panel: isOpen=" + st.isOpen);
562        final ViewManager wm = getWindowManager();
563        if ((wm != null) && st.isOpen) {
564            if (st.decorView != null) {
565                wm.removeView(st.decorView);
566                // Log.v(TAG, "Removing main menu from window manager.");
567            }
568
569            if (doCallback) {
570                callOnPanelClosed(st.featureId, st, null);
571            }
572        } else if (st.featureId == FEATURE_OPTIONS_PANEL && doCallback &&
573                mActionBar != null) {
574            checkCloseActionMenu(st.menu);
575        }
576        st.isPrepared = false;
577        st.isHandled = false;
578        st.isOpen = false;
579
580        // This view is no longer shown, so null it out
581        st.shownPanelView = null;
582
583        if (st.isInExpandedMode) {
584            // Next time the menu opens, it should not be in expanded mode, so
585            // force a refresh of the decor
586            st.refreshDecorView = true;
587            st.isInExpandedMode = false;
588        }
589
590        if (mPreparedPanel == st) {
591            mPreparedPanel = null;
592            mPanelChordingKey = 0;
593        }
594    }
595
596    private void checkCloseActionMenu(Menu menu) {
597        if (mClosingActionMenu) {
598            return;
599        }
600
601        boolean closed = false;
602        mClosingActionMenu = true;
603        if (mActionBar.isOverflowMenuOpen() && mActionBar.hideOverflowMenu()) {
604            closed = true;
605        }
606        if (mActionButtonPopup != null) {
607            mActionButtonPopup.dismiss();
608            closed = true;
609        }
610        Callback cb = getCallback();
611        if (cb != null && closed) {
612            cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
613        }
614        mClosingActionMenu = false;
615    }
616
617    @Override
618    public final void togglePanel(int featureId, KeyEvent event) {
619        PanelFeatureState st = getPanelState(featureId, true);
620        if (st.isOpen) {
621            closePanel(st, true);
622        } else {
623            openPanel(st, event);
624        }
625    }
626
627    @Override
628    public void invalidatePanelMenu(int featureId) {
629        PanelFeatureState st = getPanelState(featureId, true);
630        if (st.menu != null) {
631            st.menu.clear();
632        }
633        st.refreshMenuContent = true;
634        st.refreshDecorView = true;
635
636        // Prepare the options panel if we have an action bar
637        if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)
638                && mActionBar != null) {
639            st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
640            if (st != null) {
641                st.isPrepared = false;
642                preparePanel(st, null);
643            }
644        }
645    }
646
647    /**
648     * Called when the panel key is pushed down.
649     * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.
650     * @param event The key event.
651     * @return Whether the key was handled.
652     */
653    public final boolean onKeyDownPanel(int featureId, KeyEvent event) {
654        final int keyCode = event.getKeyCode();
655
656        if (event.getRepeatCount() == 0) {
657            // The panel key was pushed, so set the chording key
658            mPanelChordingKey = keyCode;
659            mPanelMayLongPress = false;
660
661            PanelFeatureState st = getPanelState(featureId, true);
662            if (!st.isOpen) {
663                if (getContext().getResources().getConfiguration().keyboard
664                        == Configuration.KEYBOARD_NOKEYS) {
665                    mPanelMayLongPress = true;
666                }
667                return preparePanel(st, event);
668            }
669        } else if (mPanelMayLongPress && mPanelChordingKey == keyCode
670                && (event.getFlags()&KeyEvent.FLAG_LONG_PRESS) != 0) {
671            // We have had a long press while in a state where this
672            // should be executed...  do it!
673            mPanelChordingKey = 0;
674            mPanelMayLongPress = false;
675        }
676
677        return false;
678    }
679
680    /**
681     * Called when the panel key is released.
682     * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.
683     * @param event The key event.
684     */
685    public final void onKeyUpPanel(int featureId, KeyEvent event) {
686        // The panel key was released, so clear the chording key
687        if (mPanelChordingKey != 0) {
688            mPanelChordingKey = 0;
689            mPanelMayLongPress = false;
690
691            if (event.isCanceled()) {
692                return;
693            }
694
695            boolean playSoundEffect = false;
696            final PanelFeatureState st = getPanelState(featureId, true);
697            if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
698                    mActionBar.isOverflowReserved()) {
699                if (!mActionBar.isOverflowMenuShowing()) {
700                    final Callback cb = getCallback();
701                    if (cb != null &&
702                            cb.onPreparePanel(featureId, st.createdPanelView, st.menu)) {
703                        playSoundEffect = mActionBar.showOverflowMenu();
704                    }
705                } else {
706                    playSoundEffect = mActionBar.hideOverflowMenu();
707                }
708            } else {
709                if (st.isOpen || st.isHandled) {
710
711                    // Play the sound effect if the user closed an open menu (and not if
712                    // they just released a menu shortcut)
713                    playSoundEffect = st.isOpen;
714
715                    // Close menu
716                    closePanel(st, true);
717
718                } else if (st.isPrepared) {
719
720                    // Write 'menu opened' to event log
721                    EventLog.writeEvent(50001, 0);
722
723                    // Show menu
724                    openPanel(st, event);
725
726                    playSoundEffect = true;
727                }
728            }
729
730            if (playSoundEffect) {
731                AudioManager audioManager = (AudioManager) getContext().getSystemService(
732                        Context.AUDIO_SERVICE);
733                if (audioManager != null) {
734                    audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
735                } else {
736                    Log.w(TAG, "Couldn't get audio manager");
737                }
738            }
739        }
740    }
741
742    @Override
743    public final void closeAllPanels() {
744        final ViewManager wm = getWindowManager();
745        if (wm == null) {
746            return;
747        }
748
749        final PanelFeatureState[] panels = mPanels;
750        final int N = panels != null ? panels.length : 0;
751        for (int i = 0; i < N; i++) {
752            final PanelFeatureState panel = panels[i];
753            if (panel != null) {
754                closePanel(panel, true);
755            }
756        }
757
758        closeContextMenu();
759    }
760
761    /**
762     * Closes the context menu. This notifies the menu logic of the close, along
763     * with dismissing it from the UI.
764     */
765    private synchronized void closeContextMenu() {
766        if (mContextMenu != null) {
767            mContextMenu.close();
768            dismissContextMenu();
769        }
770    }
771
772    /**
773     * Dismisses just the context menu UI. To close the context menu, use
774     * {@link #closeContextMenu()}.
775     */
776    private synchronized void dismissContextMenu() {
777        mContextMenu = null;
778
779        if (mContextMenuHelper != null) {
780            mContextMenuHelper.dismiss();
781            mContextMenuHelper = null;
782        }
783    }
784
785    @Override
786    public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
787        return performPanelShortcut(getPanelState(featureId, true), keyCode, event, flags);
788    }
789
790    private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
791            int flags) {
792        if (event.isSystem() || (st == null)) {
793            return false;
794        }
795
796        boolean handled = false;
797
798        // Only try to perform menu shortcuts if preparePanel returned true (possible false
799        // return value from application not wanting to show the menu).
800        if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) {
801            // The menu is prepared now, perform the shortcut on it
802            handled = st.menu.performShortcut(keyCode, event, flags);
803        }
804
805        if (handled) {
806            // Mark as handled
807            st.isHandled = true;
808
809            if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0) {
810                closePanel(st, true);
811            }
812        }
813
814        return handled;
815    }
816
817    @Override
818    public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
819
820        PanelFeatureState st = getPanelState(featureId, true);
821        if (!preparePanel(st, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU))) {
822            return false;
823        }
824        if (st.menu == null) {
825            return false;
826        }
827
828        boolean res = st.menu.performIdentifierAction(id, flags);
829
830        closePanel(st, true);
831
832        return res;
833    }
834
835    public PanelFeatureState findMenuPanel(Menu menu) {
836        final PanelFeatureState[] panels = mPanels;
837        final int N = panels != null ? panels.length : 0;
838        for (int i = 0; i < N; i++) {
839            final PanelFeatureState panel = panels[i];
840            if (panel != null && panel.menu == menu) {
841                return panel;
842            }
843        }
844        return null;
845    }
846
847    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
848        final Callback cb = getCallback();
849        if (cb != null) {
850            final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
851            if (panel != null) {
852                return cb.onMenuItemSelected(panel.featureId, item);
853            }
854        }
855        return false;
856    }
857
858    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
859        final PanelFeatureState panel = findMenuPanel(menu);
860        if (panel != null) {
861            // Close the panel and only do the callback if the menu is being
862            // closed
863            // completely, not if opening a sub menu
864            closePanel(panel, allMenusAreClosing);
865        }
866    }
867
868    public void onCloseSubMenu(SubMenuBuilder subMenu) {
869        final Menu parentMenu = subMenu.getRootMenu();
870        final PanelFeatureState panel = findMenuPanel(parentMenu);
871
872        // Callback
873        if (panel != null) {
874            callOnPanelClosed(panel.featureId, panel, parentMenu);
875            closePanel(panel, true);
876        }
877    }
878
879    public boolean onSubMenuSelected(final SubMenuBuilder subMenu) {
880        if (!subMenu.hasVisibleItems()) {
881            return true;
882        }
883
884        final Menu parentMenu = subMenu.getRootMenu();
885        final PanelFeatureState panel = findMenuPanel(parentMenu);
886
887        if (hasFeature(FEATURE_ACTION_BAR) && panel.featureId == FEATURE_OPTIONS_PANEL) {
888            mDecor.post(new Runnable() {
889                public void run() {
890                    mActionButtonPopup = new ActionButtonSubmenu(getContext(), subMenu);
891                    mActionButtonPopup.show();
892                    Callback cb = getCallback();
893                    if (cb != null) {
894                        cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
895                    }
896                }
897            });
898        } else {
899            // The window manager will give us a valid window token
900            new MenuDialogHelper(subMenu).show(null);
901        }
902
903        return true;
904    }
905
906    public void onMenuModeChange(MenuBuilder menu) {
907        reopenMenu(true);
908    }
909
910    private void reopenMenu(boolean toggleMenuMode) {
911        if (mActionBar != null) {
912            final Callback cb = getCallback();
913            if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
914                if (cb != null) {
915                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
916                    if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
917                        cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);
918                        mActionBar.openOverflowMenu();
919                    }
920                }
921            } else {
922                mActionBar.hideOverflowMenu();
923                if (cb != null) {
924                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
925                    cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu);
926                }
927            }
928            return;
929        }
930
931        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
932
933        // Save the future expanded mode state since closePanel will reset it
934        boolean newExpandedMode = toggleMenuMode ? !st.isInExpandedMode : st.isInExpandedMode;
935
936        st.refreshDecorView = true;
937        closePanel(st, false);
938
939        // Set the expanded mode state
940        st.isInExpandedMode = newExpandedMode;
941
942        openPanel(st, null);
943    }
944
945    /**
946     * Initializes the menu associated with the given panel feature state. You
947     * must at the very least set PanelFeatureState.menu to the Menu to be
948     * associated with the given panel state. The default implementation creates
949     * a new menu for the panel state.
950     *
951     * @param st The panel whose menu is being initialized.
952     * @return Whether the initialization was successful.
953     */
954    protected boolean initializePanelMenu(final PanelFeatureState st) {
955        final MenuBuilder menu = new MenuBuilder(getContext());
956
957        menu.setCallback(this);
958        st.setMenu(menu);
959
960        return true;
961    }
962
963    /**
964     * Perform initial setup of a panel. This should at the very least set the
965     * style information in the PanelFeatureState and must set
966     * PanelFeatureState.decor to the panel's window decor view.
967     *
968     * @param st The panel being initialized.
969     */
970    protected boolean initializePanelDecor(PanelFeatureState st) {
971        st.decorView = new DecorView(getContext(), st.featureId);
972        st.gravity = Gravity.CENTER | Gravity.BOTTOM;
973        st.setStyle(getContext());
974
975        return true;
976    }
977
978    /**
979     * Initializes the panel associated with the panel feature state. You must
980     * at the very least set PanelFeatureState.panel to the View implementing
981     * its contents. The default implementation gets the panel from the menu.
982     *
983     * @param st The panel state being initialized.
984     * @return Whether the initialization was successful.
985     */
986    protected boolean initializePanelContent(PanelFeatureState st) {
987
988        if (st.createdPanelView != null) {
989            st.shownPanelView = st.createdPanelView;
990            return true;
991        }
992
993        final MenuBuilder menu = (MenuBuilder)st.menu;
994        if (menu == null) {
995            return false;
996        }
997
998        st.shownPanelView = menu.getMenuView((st.isInExpandedMode) ? MenuBuilder.TYPE_EXPANDED
999                : MenuBuilder.TYPE_ICON, st.decorView);
1000
1001        if (st.shownPanelView != null) {
1002            // Use the menu View's default animations if it has any
1003            final int defaultAnimations = ((MenuView) st.shownPanelView).getWindowAnimations();
1004            if (defaultAnimations != 0) {
1005                st.windowAnimations = defaultAnimations;
1006            }
1007            return true;
1008        } else {
1009            return false;
1010        }
1011    }
1012
1013    @Override
1014    public boolean performContextMenuIdentifierAction(int id, int flags) {
1015        return (mContextMenu != null) ? mContextMenu.performIdentifierAction(id, flags) : false;
1016    }
1017
1018    @Override
1019    public final void setBackgroundDrawable(Drawable drawable) {
1020        if (drawable != mBackgroundDrawable || mBackgroundResource != 0) {
1021            mBackgroundResource = 0;
1022            mBackgroundDrawable = drawable;
1023            if (mDecor != null) {
1024                mDecor.setWindowBackground(drawable);
1025            }
1026        }
1027    }
1028
1029    @Override
1030    public final void setFeatureDrawableResource(int featureId, int resId) {
1031        if (resId != 0) {
1032            DrawableFeatureState st = getDrawableState(featureId, true);
1033            if (st.resid != resId) {
1034                st.resid = resId;
1035                st.uri = null;
1036                st.local = getContext().getResources().getDrawable(resId);
1037                updateDrawable(featureId, st, false);
1038            }
1039        } else {
1040            setFeatureDrawable(featureId, null);
1041        }
1042    }
1043
1044    @Override
1045    public final void setFeatureDrawableUri(int featureId, Uri uri) {
1046        if (uri != null) {
1047            DrawableFeatureState st = getDrawableState(featureId, true);
1048            if (st.uri == null || !st.uri.equals(uri)) {
1049                st.resid = 0;
1050                st.uri = uri;
1051                st.local = loadImageURI(uri);
1052                updateDrawable(featureId, st, false);
1053            }
1054        } else {
1055            setFeatureDrawable(featureId, null);
1056        }
1057    }
1058
1059    @Override
1060    public final void setFeatureDrawable(int featureId, Drawable drawable) {
1061        DrawableFeatureState st = getDrawableState(featureId, true);
1062        st.resid = 0;
1063        st.uri = null;
1064        if (st.local != drawable) {
1065            st.local = drawable;
1066            updateDrawable(featureId, st, false);
1067        }
1068    }
1069
1070    @Override
1071    public void setFeatureDrawableAlpha(int featureId, int alpha) {
1072        DrawableFeatureState st = getDrawableState(featureId, true);
1073        if (st.alpha != alpha) {
1074            st.alpha = alpha;
1075            updateDrawable(featureId, st, false);
1076        }
1077    }
1078
1079    protected final void setFeatureDefaultDrawable(int featureId, Drawable drawable) {
1080        DrawableFeatureState st = getDrawableState(featureId, true);
1081        if (st.def != drawable) {
1082            st.def = drawable;
1083            updateDrawable(featureId, st, false);
1084        }
1085    }
1086
1087    @Override
1088    public final void setFeatureInt(int featureId, int value) {
1089        // XXX Should do more management (as with drawable features) to
1090        // deal with interactions between multiple window policies.
1091        updateInt(featureId, value, false);
1092    }
1093
1094    /**
1095     * Update the state of a drawable feature. This should be called, for every
1096     * drawable feature supported, as part of onActive(), to make sure that the
1097     * contents of a containing window is properly updated.
1098     *
1099     * @see #onActive
1100     * @param featureId The desired drawable feature to change.
1101     * @param fromActive Always true when called from onActive().
1102     */
1103    protected final void updateDrawable(int featureId, boolean fromActive) {
1104        final DrawableFeatureState st = getDrawableState(featureId, false);
1105        if (st != null) {
1106            updateDrawable(featureId, st, fromActive);
1107        }
1108    }
1109
1110    /**
1111     * Called when a Drawable feature changes, for the window to update its
1112     * graphics.
1113     *
1114     * @param featureId The feature being changed.
1115     * @param drawable The new Drawable to show, or null if none.
1116     * @param alpha The new alpha blending of the Drawable.
1117     */
1118    protected void onDrawableChanged(int featureId, Drawable drawable, int alpha) {
1119        ImageView view;
1120        if (featureId == FEATURE_LEFT_ICON) {
1121            view = getLeftIconView();
1122        } else if (featureId == FEATURE_RIGHT_ICON) {
1123            view = getRightIconView();
1124        } else {
1125            return;
1126        }
1127
1128        if (drawable != null) {
1129            drawable.setAlpha(alpha);
1130            view.setImageDrawable(drawable);
1131            view.setVisibility(View.VISIBLE);
1132        } else {
1133            view.setVisibility(View.GONE);
1134        }
1135    }
1136
1137    /**
1138     * Called when an int feature changes, for the window to update its
1139     * graphics.
1140     *
1141     * @param featureId The feature being changed.
1142     * @param value The new integer value.
1143     */
1144    protected void onIntChanged(int featureId, int value) {
1145        if (featureId == FEATURE_PROGRESS || featureId == FEATURE_INDETERMINATE_PROGRESS) {
1146            updateProgressBars(value);
1147        } else if (featureId == FEATURE_CUSTOM_TITLE) {
1148            FrameLayout titleContainer = (FrameLayout) findViewById(com.android.internal.R.id.title_container);
1149            if (titleContainer != null) {
1150                mLayoutInflater.inflate(value, titleContainer);
1151            }
1152        }
1153    }
1154
1155    /**
1156     * Updates the progress bars that are shown in the title bar.
1157     *
1158     * @param value Can be one of {@link Window#PROGRESS_VISIBILITY_ON},
1159     *            {@link Window#PROGRESS_VISIBILITY_OFF},
1160     *            {@link Window#PROGRESS_INDETERMINATE_ON},
1161     *            {@link Window#PROGRESS_INDETERMINATE_OFF}, or a value
1162     *            starting at {@link Window#PROGRESS_START} through
1163     *            {@link Window#PROGRESS_END} for setting the default
1164     *            progress (if {@link Window#PROGRESS_END} is given,
1165     *            the progress bar widgets in the title will be hidden after an
1166     *            animation), a value between
1167     *            {@link Window#PROGRESS_SECONDARY_START} -
1168     *            {@link Window#PROGRESS_SECONDARY_END} for the
1169     *            secondary progress (if
1170     *            {@link Window#PROGRESS_SECONDARY_END} is given, the
1171     *            progress bar widgets will still be shown with the secondary
1172     *            progress bar will be completely filled in.)
1173     */
1174    private void updateProgressBars(int value) {
1175        ProgressBar circularProgressBar = getCircularProgressBar(true);
1176        ProgressBar horizontalProgressBar = getHorizontalProgressBar(true);
1177
1178        final int features = getLocalFeatures();
1179        if (value == PROGRESS_VISIBILITY_ON) {
1180            if ((features & (1 << FEATURE_PROGRESS)) != 0) {
1181                int level = horizontalProgressBar.getProgress();
1182                int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
1183                        View.VISIBLE : View.INVISIBLE;
1184                horizontalProgressBar.setVisibility(visibility);
1185            }
1186            if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
1187                circularProgressBar.setVisibility(View.VISIBLE);
1188            }
1189        } else if (value == PROGRESS_VISIBILITY_OFF) {
1190            if ((features & (1 << FEATURE_PROGRESS)) != 0) {
1191                horizontalProgressBar.setVisibility(View.GONE);
1192            }
1193            if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
1194                circularProgressBar.setVisibility(View.GONE);
1195            }
1196        } else if (value == PROGRESS_INDETERMINATE_ON) {
1197            horizontalProgressBar.setIndeterminate(true);
1198        } else if (value == PROGRESS_INDETERMINATE_OFF) {
1199            horizontalProgressBar.setIndeterminate(false);
1200        } else if (PROGRESS_START <= value && value <= PROGRESS_END) {
1201            // We want to set the progress value before testing for visibility
1202            // so that when the progress bar becomes visible again, it has the
1203            // correct level.
1204            horizontalProgressBar.setProgress(value - PROGRESS_START);
1205
1206            if (value < PROGRESS_END) {
1207                showProgressBars(horizontalProgressBar, circularProgressBar);
1208            } else {
1209                hideProgressBars(horizontalProgressBar, circularProgressBar);
1210            }
1211        } else if (PROGRESS_SECONDARY_START <= value && value <= PROGRESS_SECONDARY_END) {
1212            horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START);
1213
1214            showProgressBars(horizontalProgressBar, circularProgressBar);
1215        }
1216
1217    }
1218
1219    private void showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {
1220        final int features = getLocalFeatures();
1221        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
1222                spinnyProgressBar.getVisibility() == View.INVISIBLE) {
1223            spinnyProgressBar.setVisibility(View.VISIBLE);
1224        }
1225        // Only show the progress bars if the primary progress is not complete
1226        if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
1227                horizontalProgressBar.getProgress() < 10000) {
1228            horizontalProgressBar.setVisibility(View.VISIBLE);
1229        }
1230    }
1231
1232    private void hideProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {
1233        final int features = getLocalFeatures();
1234        Animation anim = AnimationUtils.loadAnimation(getContext(), com.android.internal.R.anim.fade_out);
1235        anim.setDuration(1000);
1236        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
1237                spinnyProgressBar.getVisibility() == View.VISIBLE) {
1238            spinnyProgressBar.startAnimation(anim);
1239            spinnyProgressBar.setVisibility(View.INVISIBLE);
1240        }
1241        if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
1242                horizontalProgressBar.getVisibility() == View.VISIBLE) {
1243            horizontalProgressBar.startAnimation(anim);
1244            horizontalProgressBar.setVisibility(View.INVISIBLE);
1245        }
1246    }
1247
1248    /**
1249     * Request that key events come to this activity. Use this if your activity
1250     * has no views with focus, but the activity still wants a chance to process
1251     * key events.
1252     */
1253    @Override
1254    public void takeKeyEvents(boolean get) {
1255        mDecor.setFocusable(get);
1256    }
1257
1258    @Override
1259    public boolean superDispatchKeyEvent(KeyEvent event) {
1260        return mDecor.superDispatchKeyEvent(event);
1261    }
1262
1263    @Override
1264    public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
1265        return mDecor.superDispatchKeyShortcutEvent(event);
1266    }
1267
1268    @Override
1269    public boolean superDispatchTouchEvent(MotionEvent event) {
1270        return mDecor.superDispatchTouchEvent(event);
1271    }
1272
1273    @Override
1274    public boolean superDispatchTrackballEvent(MotionEvent event) {
1275        return mDecor.superDispatchTrackballEvent(event);
1276    }
1277
1278    /**
1279     * A key was pressed down and not handled by anything else in the window.
1280     *
1281     * @see #onKeyUp
1282     * @see android.view.KeyEvent
1283     */
1284    protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) {
1285        /* ****************************************************************************
1286         * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES.
1287         *
1288         * If your key handling must happen before the app gets a crack at the event,
1289         * it goes in PhoneWindowManager.
1290         *
1291         * If your key handling should happen in all windows, and does not depend on
1292         * the state of the current application, other than that the current
1293         * application can override the behavior by handling the event itself, it
1294         * should go in PhoneFallbackEventHandler.
1295         *
1296         * Only if your handling depends on the window, and the fact that it has
1297         * a DecorView, should it go here.
1298         * ****************************************************************************/
1299
1300        final KeyEvent.DispatcherState dispatcher =
1301                mDecor != null ? mDecor.getKeyDispatcherState() : null;
1302        //Log.i(TAG, "Key down: repeat=" + event.getRepeatCount()
1303        //        + " flags=0x" + Integer.toHexString(event.getFlags()));
1304
1305        switch (keyCode) {
1306            case KeyEvent.KEYCODE_VOLUME_UP:
1307            case KeyEvent.KEYCODE_VOLUME_DOWN:
1308            case KeyEvent.KEYCODE_VOLUME_MUTE: {
1309                // Similar code is in PhoneFallbackEventHandler in case the window
1310                // doesn't have one of these.  In this case, we execute it here and
1311                // eat the event instead, because we have mVolumeControlStreamType
1312                // and they don't.
1313                getAudioManager().handleKeyDown(keyCode, mVolumeControlStreamType);
1314                return true;
1315            }
1316
1317            case KeyEvent.KEYCODE_MENU: {
1318                onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event);
1319                return true;
1320            }
1321
1322            case KeyEvent.KEYCODE_BACK: {
1323                if (event.getRepeatCount() > 0) break;
1324                if (featureId < 0) break;
1325                // Currently don't do anything with long press.
1326                dispatcher.startTracking(event, this);
1327                return true;
1328            }
1329
1330        }
1331
1332        return false;
1333    }
1334
1335    private KeyguardManager getKeyguardManager() {
1336        if (mKeyguardManager == null) {
1337            mKeyguardManager = (KeyguardManager) getContext().getSystemService(
1338                    Context.KEYGUARD_SERVICE);
1339        }
1340        return mKeyguardManager;
1341    }
1342
1343    AudioManager getAudioManager() {
1344        if (mAudioManager == null) {
1345            mAudioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
1346        }
1347        return mAudioManager;
1348    }
1349
1350    /**
1351     * A key was released and not handled by anything else in the window.
1352     *
1353     * @see #onKeyDown
1354     * @see android.view.KeyEvent
1355     */
1356    protected boolean onKeyUp(int featureId, int keyCode, KeyEvent event) {
1357        final KeyEvent.DispatcherState dispatcher =
1358                mDecor != null ? mDecor.getKeyDispatcherState() : null;
1359        if (dispatcher != null) {
1360            dispatcher.handleUpEvent(event);
1361        }
1362        //Log.i(TAG, "Key up: repeat=" + event.getRepeatCount()
1363        //        + " flags=0x" + Integer.toHexString(event.getFlags()));
1364
1365        switch (keyCode) {
1366            case KeyEvent.KEYCODE_VOLUME_UP:
1367            case KeyEvent.KEYCODE_VOLUME_DOWN:
1368            case KeyEvent.KEYCODE_VOLUME_MUTE: {
1369                // Similar code is in PhoneFallbackEventHandler in case the window
1370                // doesn't have one of these.  In this case, we execute it here and
1371                // eat the event instead, because we have mVolumeControlStreamType
1372                // and they don't.
1373                getAudioManager().handleKeyUp(keyCode, mVolumeControlStreamType);
1374                return true;
1375            }
1376
1377            case KeyEvent.KEYCODE_MENU: {
1378                onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
1379                        event);
1380                return true;
1381            }
1382
1383            case KeyEvent.KEYCODE_BACK: {
1384                if (featureId < 0) break;
1385                if (event.isTracking() && !event.isCanceled()) {
1386                    if (featureId == FEATURE_OPTIONS_PANEL) {
1387                        PanelFeatureState st = getPanelState(featureId, false);
1388                        if (st != null && st.isInExpandedMode) {
1389                            // If the user is in an expanded menu and hits back, it
1390                            // should go back to the icon menu
1391                            reopenMenu(true);
1392                            return true;
1393                        }
1394                    }
1395                    closePanel(featureId);
1396                    return true;
1397                }
1398                break;
1399            }
1400
1401            case KeyEvent.KEYCODE_SEARCH: {
1402                /*
1403                 * Do this in onKeyUp since the Search key is also used for
1404                 * chording quick launch shortcuts.
1405                 */
1406                if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
1407                    break;
1408                }
1409                if (event.isTracking() && !event.isCanceled()) {
1410                    launchDefaultSearch();
1411                }
1412                return true;
1413            }
1414        }
1415
1416        return false;
1417    }
1418
1419    @Override
1420    protected void onActive() {
1421    }
1422
1423    @Override
1424    public final View getDecorView() {
1425        if (mDecor == null) {
1426            installDecor();
1427        }
1428        return mDecor;
1429    }
1430
1431    @Override
1432    public final View peekDecorView() {
1433        return mDecor;
1434    }
1435
1436    static private final String FOCUSED_ID_TAG = "android:focusedViewId";
1437    static private final String VIEWS_TAG = "android:views";
1438    static private final String PANELS_TAG = "android:Panels";
1439    static private final String ACTION_BAR_TAG = "android:ActionBar";
1440
1441    /** {@inheritDoc} */
1442    @Override
1443    public Bundle saveHierarchyState() {
1444        Bundle outState = new Bundle();
1445        if (mContentParent == null) {
1446            return outState;
1447        }
1448
1449        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
1450        mContentParent.saveHierarchyState(states);
1451        outState.putSparseParcelableArray(VIEWS_TAG, states);
1452
1453        // save the focused view id
1454        View focusedView = mContentParent.findFocus();
1455        if (focusedView != null) {
1456            if (focusedView.getId() != View.NO_ID) {
1457                outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
1458            } else {
1459                if (Config.LOGD) {
1460                    Log.d(TAG, "couldn't save which view has focus because the focused view "
1461                            + focusedView + " has no id.");
1462                }
1463            }
1464        }
1465
1466        // save the panels
1467        SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
1468        savePanelState(panelStates);
1469        if (panelStates.size() > 0) {
1470            outState.putSparseParcelableArray(PANELS_TAG, panelStates);
1471        }
1472
1473        if (mActionBar != null) {
1474            outState.putBoolean(ACTION_BAR_TAG, mActionBar.isOverflowMenuShowing());
1475        }
1476
1477        return outState;
1478    }
1479
1480    /** {@inheritDoc} */
1481    @Override
1482    public void restoreHierarchyState(Bundle savedInstanceState) {
1483        if (mContentParent == null) {
1484            return;
1485        }
1486
1487        SparseArray<Parcelable> savedStates
1488                = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
1489        if (savedStates != null) {
1490            mContentParent.restoreHierarchyState(savedStates);
1491        }
1492
1493        // restore the focused view
1494        int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
1495        if (focusedViewId != View.NO_ID) {
1496            View needsFocus = mContentParent.findViewById(focusedViewId);
1497            if (needsFocus != null) {
1498                needsFocus.requestFocus();
1499            } else {
1500                Log.w(TAG,
1501                        "Previously focused view reported id " + focusedViewId
1502                                + " during save, but can't be found during restore.");
1503            }
1504        }
1505
1506        // restore the panels
1507        SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
1508        if (panelStates != null) {
1509            restorePanelState(panelStates);
1510        }
1511
1512        if (mActionBar != null && savedInstanceState.getBoolean(ACTION_BAR_TAG)) {
1513            mActionBar.postShowOverflowMenu();
1514        }
1515    }
1516
1517    /**
1518     * Invoked when the panels should freeze their state.
1519     *
1520     * @param icicles Save state into this. This is usually indexed by the
1521     *            featureId. This will be given to {@link #restorePanelState} in the
1522     *            future.
1523     */
1524    private void savePanelState(SparseArray<Parcelable> icicles) {
1525        PanelFeatureState[] panels = mPanels;
1526        if (panels == null) {
1527            return;
1528        }
1529
1530        for (int curFeatureId = panels.length - 1; curFeatureId >= 0; curFeatureId--) {
1531            if (panels[curFeatureId] != null) {
1532                icicles.put(curFeatureId, panels[curFeatureId].onSaveInstanceState());
1533            }
1534        }
1535    }
1536
1537    /**
1538     * Invoked when the panels should thaw their state from a previously frozen state.
1539     *
1540     * @param icicles The state saved by {@link #savePanelState} that needs to be thawed.
1541     */
1542    private void restorePanelState(SparseArray<Parcelable> icicles) {
1543        PanelFeatureState st;
1544        for (int curFeatureId = icicles.size() - 1; curFeatureId >= 0; curFeatureId--) {
1545            st = getPanelState(curFeatureId, false /* required */);
1546            if (st == null) {
1547                // The panel must not have been required, and is currently not around, skip it
1548                continue;
1549            }
1550
1551            st.onRestoreInstanceState(icicles.get(curFeatureId));
1552        }
1553
1554        /*
1555         * Implementation note: call openPanelsAfterRestore later to actually open the
1556         * restored panels.
1557         */
1558    }
1559
1560    /**
1561     * Opens the panels that have had their state restored. This should be
1562     * called sometime after {@link #restorePanelState} when it is safe to add
1563     * to the window manager.
1564     */
1565    private void openPanelsAfterRestore() {
1566        PanelFeatureState[] panels = mPanels;
1567
1568        if (panels == null) {
1569            return;
1570        }
1571
1572        PanelFeatureState st;
1573        for (int i = panels.length - 1; i >= 0; i--) {
1574            st = panels[i];
1575            // We restore the panel if it was last open; we skip it if it
1576            // now is open, to avoid a race condition if the user immediately
1577            // opens it when we are resuming.
1578            if ((st != null) && !st.isOpen && st.wasLastOpen) {
1579                st.isInExpandedMode = st.wasLastExpanded;
1580                openPanel(st, null);
1581            }
1582        }
1583    }
1584
1585    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
1586        /* package */int mDefaultOpacity = PixelFormat.OPAQUE;
1587
1588        /** The feature ID of the panel, or -1 if this is the application's DecorView */
1589        private final int mFeatureId;
1590
1591        private final Rect mDrawingBounds = new Rect();
1592
1593        private final Rect mBackgroundPadding = new Rect();
1594
1595        private final Rect mFramePadding = new Rect();
1596
1597        private final Rect mFrameOffsets = new Rect();
1598
1599        private boolean mChanging;
1600
1601        private Drawable mMenuBackground;
1602        private boolean mWatchingForMenu;
1603        private int mDownY;
1604
1605        private ActionMode mActionMode;
1606        private ActionBarContextView mActionModeView;
1607        private PopupWindow mActionModePopup;
1608
1609        public DecorView(Context context, int featureId) {
1610            super(context);
1611            mFeatureId = featureId;
1612        }
1613
1614        @Override
1615        public boolean dispatchKeyEvent(KeyEvent event) {
1616            final int keyCode = event.getKeyCode();
1617            final int action = event.getAction();
1618            final boolean isDown = action == KeyEvent.ACTION_DOWN;
1619
1620            if (isDown && (event.getRepeatCount() == 0)) {
1621                // First handle chording of panel key: if a panel key is held
1622                // but not released, try to execute a shortcut in it.
1623                if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
1624                    boolean handled = dispatchKeyShortcutEvent(event);
1625                    if (handled) {
1626                        return true;
1627                    }
1628                }
1629
1630                // If a panel is open, perform a shortcut on it without the
1631                // chorded panel key
1632                if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
1633                    if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
1634                        return true;
1635                    }
1636                }
1637            }
1638
1639            // Back cancels action modes first.
1640            if (mActionMode != null && keyCode == KeyEvent.KEYCODE_BACK) {
1641                if (action == KeyEvent.ACTION_UP) {
1642                    mActionMode.finish();
1643                }
1644                return true;
1645            }
1646
1647            final Callback cb = getCallback();
1648            final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
1649                    : super.dispatchKeyEvent(event);
1650            if (handled) {
1651                return true;
1652            }
1653            return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
1654                    : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
1655        }
1656
1657        @Override
1658        public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
1659            // Perform the shortcut (mPreparedPanel can be null since
1660            // global shortcuts (such as search) don't rely on a
1661            // prepared panel or menu).
1662            boolean handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev,
1663                    Menu.FLAG_PERFORM_NO_CLOSE);
1664            if (handled) {
1665                if (mPreparedPanel != null) {
1666                    mPreparedPanel.isHandled = true;
1667                }
1668                return true;
1669            }
1670
1671            // Shortcut not handled by the panel.  Dispatch to the view hierarchy.
1672            final Callback cb = getCallback();
1673            return cb != null && mFeatureId < 0 ? cb.dispatchKeyShortcutEvent(ev) : super
1674                    .dispatchKeyShortcutEvent(ev);
1675        }
1676
1677        @Override
1678        public boolean dispatchTouchEvent(MotionEvent ev) {
1679            final Callback cb = getCallback();
1680            return cb != null && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super
1681                    .dispatchTouchEvent(ev);
1682        }
1683
1684        @Override
1685        public boolean dispatchTrackballEvent(MotionEvent ev) {
1686            final Callback cb = getCallback();
1687            return cb != null && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev) : super
1688                    .dispatchTrackballEvent(ev);
1689        }
1690
1691        public boolean superDispatchKeyEvent(KeyEvent event) {
1692            return super.dispatchKeyEvent(event);
1693        }
1694
1695        public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
1696            return super.dispatchKeyShortcutEvent(event);
1697        }
1698
1699        public boolean superDispatchTouchEvent(MotionEvent event) {
1700            return super.dispatchTouchEvent(event);
1701        }
1702
1703        public boolean superDispatchTrackballEvent(MotionEvent event) {
1704            return super.dispatchTrackballEvent(event);
1705        }
1706
1707        @Override
1708        public boolean onTouchEvent(MotionEvent event) {
1709            return onInterceptTouchEvent(event);
1710        }
1711
1712        private boolean isOutOfBounds(int x, int y) {
1713            return x < -5 || y < -5 || x > (getWidth() + 5)
1714                    || y > (getHeight() + 5);
1715        }
1716
1717        @Override
1718        public boolean onInterceptTouchEvent(MotionEvent event) {
1719            int action = event.getAction();
1720            if (mFeatureId >= 0) {
1721                if (action == MotionEvent.ACTION_DOWN) {
1722                    int x = (int)event.getX();
1723                    int y = (int)event.getY();
1724                    if (isOutOfBounds(x, y)) {
1725                        closePanel(mFeatureId);
1726                        return true;
1727                    }
1728                }
1729            }
1730
1731            if (!SWEEP_OPEN_MENU) {
1732                return false;
1733            }
1734
1735            if (mFeatureId >= 0) {
1736                if (action == MotionEvent.ACTION_DOWN) {
1737                    Log.i(TAG, "Watchiing!");
1738                    mWatchingForMenu = true;
1739                    mDownY = (int) event.getY();
1740                    return false;
1741                }
1742
1743                if (!mWatchingForMenu) {
1744                    return false;
1745                }
1746
1747                int y = (int)event.getY();
1748                if (action == MotionEvent.ACTION_MOVE) {
1749                    if (y > (mDownY+30)) {
1750                        Log.i(TAG, "Closing!");
1751                        closePanel(mFeatureId);
1752                        mWatchingForMenu = false;
1753                        return true;
1754                    }
1755                } else if (action == MotionEvent.ACTION_UP) {
1756                    mWatchingForMenu = false;
1757                }
1758
1759                return false;
1760            }
1761
1762            //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY()
1763            //        + " (in " + getHeight() + ")");
1764
1765            if (action == MotionEvent.ACTION_DOWN) {
1766                int y = (int)event.getY();
1767                if (y >= (getHeight()-5) && !hasChildren()) {
1768                    Log.i(TAG, "Watchiing!");
1769                    mWatchingForMenu = true;
1770                }
1771                return false;
1772            }
1773
1774            if (!mWatchingForMenu) {
1775                return false;
1776            }
1777
1778            int y = (int)event.getY();
1779            if (action == MotionEvent.ACTION_MOVE) {
1780                if (y < (getHeight()-30)) {
1781                    Log.i(TAG, "Opening!");
1782                    openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
1783                            KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
1784                    mWatchingForMenu = false;
1785                    return true;
1786                }
1787            } else if (action == MotionEvent.ACTION_UP) {
1788                mWatchingForMenu = false;
1789            }
1790
1791            return false;
1792        }
1793
1794        @Override
1795        public void sendAccessibilityEvent(int eventType) {
1796            if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
1797                return;
1798            }
1799
1800            // if we are showing a feature that should be announced and one child
1801            // make this child the event source since this is the feature itself
1802            // otherwise the callback will take over and announce its client
1803            if ((mFeatureId == FEATURE_OPTIONS_PANEL ||
1804                    mFeatureId == FEATURE_CONTEXT_MENU ||
1805                    mFeatureId == FEATURE_PROGRESS ||
1806                    mFeatureId == FEATURE_INDETERMINATE_PROGRESS)
1807                    && getChildCount() == 1) {
1808                getChildAt(0).sendAccessibilityEvent(eventType);
1809            } else {
1810                super.sendAccessibilityEvent(eventType);
1811            }
1812        }
1813
1814        @Override
1815        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1816            final Callback cb = getCallback();
1817            if (cb != null) {
1818                if (cb.dispatchPopulateAccessibilityEvent(event)) {
1819                    return true;
1820                }
1821            }
1822            return super.dispatchPopulateAccessibilityEvent(event);
1823        }
1824
1825        @Override
1826        protected boolean setFrame(int l, int t, int r, int b) {
1827            boolean changed = super.setFrame(l, t, r, b);
1828            if (changed) {
1829                final Rect drawingBounds = mDrawingBounds;
1830                getDrawingRect(drawingBounds);
1831
1832                Drawable fg = getForeground();
1833                if (fg != null) {
1834                    final Rect frameOffsets = mFrameOffsets;
1835                    drawingBounds.left += frameOffsets.left;
1836                    drawingBounds.top += frameOffsets.top;
1837                    drawingBounds.right -= frameOffsets.right;
1838                    drawingBounds.bottom -= frameOffsets.bottom;
1839                    fg.setBounds(drawingBounds);
1840                    final Rect framePadding = mFramePadding;
1841                    drawingBounds.left += framePadding.left - frameOffsets.left;
1842                    drawingBounds.top += framePadding.top - frameOffsets.top;
1843                    drawingBounds.right -= framePadding.right - frameOffsets.right;
1844                    drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
1845                }
1846
1847                Drawable bg = getBackground();
1848                if (bg != null) {
1849                    bg.setBounds(drawingBounds);
1850                }
1851
1852                if (SWEEP_OPEN_MENU) {
1853                    if (mMenuBackground == null && mFeatureId < 0
1854                            && getAttributes().height
1855                            == WindowManager.LayoutParams.MATCH_PARENT) {
1856                        mMenuBackground = getContext().getResources().getDrawable(
1857                                com.android.internal.R.drawable.menu_background);
1858                    }
1859                    if (mMenuBackground != null) {
1860                        mMenuBackground.setBounds(drawingBounds.left,
1861                                drawingBounds.bottom-6, drawingBounds.right,
1862                                drawingBounds.bottom+20);
1863                    }
1864                }
1865            }
1866            return changed;
1867        }
1868
1869        @Override
1870        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1871            final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
1872            final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
1873
1874            final int widthMode = getMode(widthMeasureSpec);
1875
1876            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
1877
1878            int width = getMeasuredWidth();
1879            boolean measure = false;
1880
1881            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
1882
1883            final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
1884
1885            if (widthMode == AT_MOST && tv.type != TypedValue.TYPE_NULL) {
1886                final int min;
1887                if (tv.type == TypedValue.TYPE_DIMENSION) {
1888                    min = (int)tv.getDimension(metrics);
1889                } else if (tv.type == TypedValue.TYPE_FRACTION) {
1890                    min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
1891                } else {
1892                    min = 0;
1893                }
1894
1895                if (width < min) {
1896                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
1897                    measure = true;
1898                }
1899            }
1900
1901            // TODO: Support height?
1902
1903            if (measure) {
1904                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
1905            }
1906        }
1907
1908        @Override
1909        public void draw(Canvas canvas) {
1910            super.draw(canvas);
1911
1912            if (mMenuBackground != null) {
1913                mMenuBackground.draw(canvas);
1914            }
1915        }
1916
1917
1918        @Override
1919        public boolean showContextMenuForChild(View originalView) {
1920            // Reuse the context menu builder
1921            if (mContextMenu == null) {
1922                mContextMenu = new ContextMenuBuilder(getContext());
1923                mContextMenu.setCallback(mContextMenuCallback);
1924            } else {
1925                mContextMenu.clearAll();
1926            }
1927
1928            mContextMenuHelper = mContextMenu.show(originalView, originalView.getWindowToken());
1929            return mContextMenuHelper != null;
1930        }
1931
1932        @Override
1933        public ActionMode startActionModeForChild(View originalView,
1934                ActionMode.Callback callback) {
1935            // originalView can be used here to be sure that we don't obscure
1936            // relevant content with the context mode UI.
1937            return startActionMode(callback);
1938        }
1939
1940        @Override
1941        public ActionMode startActionMode(ActionMode.Callback callback) {
1942            if (mActionMode != null) {
1943                mActionMode.finish();
1944            }
1945
1946            final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
1947            ActionMode mode = null;
1948            try {
1949                mode = getCallback().onWindowStartingActionMode(wrappedCallback);
1950            } catch (AbstractMethodError ame) {
1951                // Older apps might not implement this callback method.
1952            }
1953            if (mode != null) {
1954                mActionMode = mode;
1955            } else {
1956                if (mActionModeView == null) {
1957                    if (hasFeature(FEATURE_ACTION_MODE_OVERLAY)) {
1958                        mActionModeView = new ActionBarContextView(mContext);
1959                        mActionModePopup = new PopupWindow(mContext, null,
1960                                com.android.internal.R.attr.actionModePopupWindowStyle);
1961                        mActionModePopup.setLayoutInScreenEnabled(true);
1962                        mActionModePopup.setClippingEnabled(false);
1963                        mActionModePopup.setContentView(mActionModeView);
1964                        mActionModePopup.setWidth(MATCH_PARENT);
1965
1966                        TypedValue heightValue = new TypedValue();
1967                        mContext.getTheme().resolveAttribute(
1968                                com.android.internal.R.attr.actionBarSize, heightValue, false);
1969                        final int height = TypedValue.complexToDimensionPixelSize(heightValue.data,
1970                                mContext.getResources().getDisplayMetrics());
1971                        mActionModePopup.setHeight(height);
1972                    } else {
1973                        ViewStub stub = (ViewStub) findViewById(
1974                                com.android.internal.R.id.action_mode_bar_stub);
1975                        if (stub != null) {
1976                            mActionModeView = (ActionBarContextView) stub.inflate();
1977                        }
1978                    }
1979                }
1980
1981                if (mActionModeView != null) {
1982                    mActionModeView.killMode();
1983                    mode = new StandaloneActionMode(getContext(), mActionModeView, wrappedCallback);
1984                    if (callback.onCreateActionMode(mode, mode.getMenu())) {
1985                        mode.invalidate();
1986                        mActionModeView.initForMode(mode);
1987                        mActionModeView.setVisibility(View.VISIBLE);
1988                        mActionMode = mode;
1989                        if (mActionModePopup != null) {
1990                            mActionModePopup.showAtLocation(this,
1991                                    Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1992                        }
1993                    } else {
1994                        mActionMode = null;
1995                    }
1996                }
1997            }
1998            if (mActionMode != null) {
1999                try {
2000                    getCallback().onActionModeStarted(mActionMode);
2001                } catch (AbstractMethodError ame) {
2002                    // Older apps might not implement this callback method.
2003                }
2004            }
2005            return mActionMode;
2006        }
2007
2008        public void startChanging() {
2009            mChanging = true;
2010        }
2011
2012        public void finishChanging() {
2013            mChanging = false;
2014            drawableChanged();
2015        }
2016
2017        public void setWindowBackground(Drawable drawable) {
2018            if (getBackground() != drawable) {
2019                setBackgroundDrawable(drawable);
2020                if (drawable != null) {
2021                    drawable.getPadding(mBackgroundPadding);
2022                } else {
2023                    mBackgroundPadding.setEmpty();
2024                }
2025                drawableChanged();
2026            }
2027        }
2028
2029        @Override
2030        public void setBackgroundDrawable(Drawable d) {
2031            super.setBackgroundDrawable(d);
2032            if (getWindowToken() != null) {
2033                updateWindowResizeState();
2034            }
2035        }
2036
2037        public void setWindowFrame(Drawable drawable) {
2038            if (getForeground() != drawable) {
2039                setForeground(drawable);
2040                if (drawable != null) {
2041                    drawable.getPadding(mFramePadding);
2042                } else {
2043                    mFramePadding.setEmpty();
2044                }
2045                drawableChanged();
2046            }
2047        }
2048
2049        @Override
2050        protected boolean fitSystemWindows(Rect insets) {
2051            mFrameOffsets.set(insets);
2052            if (getForeground() != null) {
2053                drawableChanged();
2054            }
2055            return super.fitSystemWindows(insets);
2056        }
2057
2058        private void drawableChanged() {
2059            if (mChanging) {
2060                return;
2061            }
2062
2063            setPadding(mFramePadding.left + mBackgroundPadding.left, mFramePadding.top
2064                    + mBackgroundPadding.top, mFramePadding.right + mBackgroundPadding.right,
2065                    mFramePadding.bottom + mBackgroundPadding.bottom);
2066            requestLayout();
2067            invalidate();
2068
2069            int opacity = PixelFormat.OPAQUE;
2070
2071            // Note: if there is no background, we will assume opaque. The
2072            // common case seems to be that an application sets there to be
2073            // no background so it can draw everything itself. For that,
2074            // we would like to assume OPAQUE and let the app force it to
2075            // the slower TRANSLUCENT mode if that is really what it wants.
2076            Drawable bg = getBackground();
2077            Drawable fg = getForeground();
2078            if (bg != null) {
2079                if (fg == null) {
2080                    opacity = bg.getOpacity();
2081                } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
2082                        && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
2083                    // If the frame padding is zero, then we can be opaque
2084                    // if either the frame -or- the background is opaque.
2085                    int fop = fg.getOpacity();
2086                    int bop = bg.getOpacity();
2087                    if (Config.LOGV)
2088                        Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop);
2089                    if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
2090                        opacity = PixelFormat.OPAQUE;
2091                    } else if (fop == PixelFormat.UNKNOWN) {
2092                        opacity = bop;
2093                    } else if (bop == PixelFormat.UNKNOWN) {
2094                        opacity = fop;
2095                    } else {
2096                        opacity = Drawable.resolveOpacity(fop, bop);
2097                    }
2098                } else {
2099                    // For now we have to assume translucent if there is a
2100                    // frame with padding... there is no way to tell if the
2101                    // frame and background together will draw all pixels.
2102                    if (Config.LOGV)
2103                        Log.v(TAG, "Padding: " + mFramePadding);
2104                    opacity = PixelFormat.TRANSLUCENT;
2105                }
2106            }
2107
2108            if (Config.LOGV)
2109                Log.v(TAG, "Background: " + bg + ", Frame: " + fg);
2110            if (Config.LOGV)
2111                Log.v(TAG, "Selected default opacity: " + opacity);
2112
2113            mDefaultOpacity = opacity;
2114            if (mFeatureId < 0) {
2115                setDefaultWindowFormat(opacity);
2116            }
2117        }
2118
2119        @Override
2120        public void onWindowFocusChanged(boolean hasWindowFocus) {
2121            super.onWindowFocusChanged(hasWindowFocus);
2122
2123            mPanelMayLongPress = false;
2124
2125            // If the user is chording a menu shortcut, release the chord since
2126            // this window lost focus
2127            if (!hasWindowFocus && mPanelChordingKey != 0) {
2128                closePanel(FEATURE_OPTIONS_PANEL);
2129            }
2130
2131            final Callback cb = getCallback();
2132            if (cb != null && mFeatureId < 0) {
2133                cb.onWindowFocusChanged(hasWindowFocus);
2134            }
2135        }
2136
2137        void updateWindowResizeState() {
2138            Drawable bg = getBackground();
2139            hackTurnOffWindowResizeAnim(bg == null || bg.getOpacity()
2140                    != PixelFormat.OPAQUE);
2141        }
2142
2143        @Override
2144        protected void onAttachedToWindow() {
2145            super.onAttachedToWindow();
2146
2147            updateWindowResizeState();
2148
2149            final Callback cb = getCallback();
2150            if (cb != null && mFeatureId < 0) {
2151                cb.onAttachedToWindow();
2152            }
2153
2154            if (mFeatureId == -1) {
2155                /*
2156                 * The main window has been attached, try to restore any panels
2157                 * that may have been open before. This is called in cases where
2158                 * an activity is being killed for configuration change and the
2159                 * menu was open. When the activity is recreated, the menu
2160                 * should be shown again.
2161                 */
2162                openPanelsAfterRestore();
2163            }
2164        }
2165
2166        @Override
2167        protected void onDetachedFromWindow() {
2168            super.onDetachedFromWindow();
2169
2170            final Callback cb = getCallback();
2171            if (cb != null && mFeatureId < 0) {
2172                cb.onDetachedFromWindow();
2173            }
2174
2175            if (mActionButtonPopup != null) {
2176                if (mActionButtonPopup.isShowing()) {
2177                    mActionButtonPopup.dismiss();
2178                }
2179                mActionButtonPopup = null;
2180            }
2181        }
2182
2183        @Override
2184        protected void onConfigurationChanged(Configuration newConfig) {
2185            if (mActionButtonPopup != null) {
2186                mActionButtonPopup.dismiss();
2187                post(mActionButtonPopup);
2188            }
2189        }
2190
2191        @Override
2192        public void onCloseSystemDialogs(String reason) {
2193            if (mFeatureId >= 0) {
2194                closeAllPanels();
2195            }
2196        }
2197
2198        public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
2199            return mFeatureId < 0 ? mTakeSurfaceCallback : null;
2200        }
2201
2202        public InputQueue.Callback willYouTakeTheInputQueue() {
2203            return mFeatureId < 0 ? mTakeInputQueueCallback : null;
2204        }
2205
2206        public void setSurfaceType(int type) {
2207            PhoneWindow.this.setType(type);
2208        }
2209
2210        public void setSurfaceFormat(int format) {
2211            PhoneWindow.this.setFormat(format);
2212        }
2213
2214        public void setSurfaceKeepScreenOn(boolean keepOn) {
2215            if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2216            else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2217        }
2218
2219        /**
2220         * Clears out internal reference when the action mode is destroyed.
2221         */
2222        private class ActionModeCallbackWrapper implements ActionMode.Callback {
2223            private ActionMode.Callback mWrapped;
2224
2225            public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
2226                mWrapped = wrapped;
2227            }
2228
2229            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2230                return mWrapped.onCreateActionMode(mode, menu);
2231            }
2232
2233            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2234                return mWrapped.onPrepareActionMode(mode, menu);
2235            }
2236
2237            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2238                return mWrapped.onActionItemClicked(mode, item);
2239            }
2240
2241            public void onDestroyActionMode(ActionMode mode) {
2242                mWrapped.onDestroyActionMode(mode);
2243                if (mActionModePopup != null) {
2244                    mActionModePopup.dismiss();
2245                } else if (mActionModeView != null) {
2246                    mActionModeView.setVisibility(GONE);
2247                }
2248                if (mActionModeView != null) {
2249                    mActionModeView.removeAllViews();
2250                }
2251                try {
2252                    getCallback().onActionModeFinished(mActionMode);
2253                } catch (AbstractMethodError ame) {
2254                    // Older apps might not implement this callback method.
2255                }
2256                mActionMode = null;
2257            }
2258        }
2259    }
2260
2261    protected DecorView generateDecor() {
2262        return new DecorView(getContext(), -1);
2263    }
2264
2265    protected void setFeatureFromAttrs(int featureId, TypedArray attrs,
2266            int drawableAttr, int alphaAttr) {
2267        Drawable d = attrs.getDrawable(drawableAttr);
2268        if (d != null) {
2269            requestFeature(featureId);
2270            setFeatureDefaultDrawable(featureId, d);
2271        }
2272        if ((getFeatures() & (1 << featureId)) != 0) {
2273            int alpha = attrs.getInt(alphaAttr, -1);
2274            if (alpha >= 0) {
2275                setFeatureDrawableAlpha(featureId, alpha);
2276            }
2277        }
2278    }
2279
2280    protected ViewGroup generateLayout(DecorView decor) {
2281        // Apply data from current theme.
2282
2283        TypedArray a = getWindowStyle();
2284
2285        if (false) {
2286            System.out.println("From style:");
2287            String s = "Attrs:";
2288            for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {
2289                s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
2290                        + a.getString(i);
2291            }
2292            System.out.println(s);
2293        }
2294
2295        mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
2296        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
2297                & (~getForcedWindowFlags());
2298        if (mIsFloating) {
2299            setLayout(WRAP_CONTENT, WRAP_CONTENT);
2300            setFlags(0, flagsToUpdate);
2301        } else {
2302            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
2303        }
2304
2305        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
2306            requestFeature(FEATURE_NO_TITLE);
2307        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
2308            // Don't allow an action bar if there is no title.
2309            requestFeature(FEATURE_ACTION_BAR);
2310        }
2311
2312        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
2313            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
2314        }
2315
2316        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
2317            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
2318        }
2319
2320        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
2321            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags()));
2322        }
2323
2324        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
2325            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
2326        }
2327
2328        if (a.getBoolean(com.android.internal.R.styleable.Window_windowEnableSplitTouch,
2329                getContext().getApplicationInfo().targetSdkVersion
2330                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
2331            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
2332        }
2333
2334        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
2335        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
2336
2337        if (getContext().getApplicationInfo().targetSdkVersion
2338                < android.os.Build.VERSION_CODES.HONEYCOMB) {
2339            addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
2340        }
2341
2342        if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
2343                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
2344            if (a.getBoolean(
2345                    com.android.internal.R.styleable.Window_windowCloseOnTouchOutside,
2346                    false)) {
2347                setCloseOnTouchOutsideIfNotSet(true);
2348            }
2349        }
2350
2351        WindowManager.LayoutParams params = getAttributes();
2352
2353        if (!hasSoftInputMode()) {
2354            params.softInputMode = a.getInt(
2355                    com.android.internal.R.styleable.Window_windowSoftInputMode,
2356                    params.softInputMode);
2357        }
2358
2359        if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,
2360                mIsFloating)) {
2361            /* All dialogs should have the window dimmed */
2362            if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
2363                params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
2364            }
2365            params.dimAmount = a.getFloat(
2366                    android.R.styleable.Window_backgroundDimAmount, 0.5f);
2367        }
2368
2369        if (params.windowAnimations == 0) {
2370            params.windowAnimations = a.getResourceId(
2371                    com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
2372        }
2373
2374        // The rest are only done if this window is not embedded; otherwise,
2375        // the values are inherited from our container.
2376        if (getContainer() == null) {
2377            if (mBackgroundDrawable == null) {
2378                if (mBackgroundResource == 0) {
2379                    mBackgroundResource = a.getResourceId(
2380                            com.android.internal.R.styleable.Window_windowBackground, 0);
2381                }
2382                if (mFrameResource == 0) {
2383                    mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0);
2384                }
2385                if (false) {
2386                    System.out.println("Background: "
2387                            + Integer.toHexString(mBackgroundResource) + " Frame: "
2388                            + Integer.toHexString(mFrameResource));
2389                }
2390            }
2391            mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);
2392        }
2393
2394        // Inflate the window decor.
2395
2396        int layoutResource;
2397        int features = getLocalFeatures();
2398        // System.out.println("Features: 0x" + Integer.toHexString(features));
2399        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
2400            if (mIsFloating) {
2401                TypedValue res = new TypedValue();
2402                getContext().getTheme().resolveAttribute(
2403                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
2404                layoutResource = res.resourceId;
2405            } else {
2406                layoutResource = com.android.internal.R.layout.screen_title_icons;
2407            }
2408            // XXX Remove this once action bar supports these features.
2409            removeFeature(FEATURE_ACTION_BAR);
2410            // System.out.println("Title Icons!");
2411        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
2412                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
2413            // Special case for a window with only a progress bar (and title).
2414            // XXX Need to have a no-title version of embedded windows.
2415            layoutResource = com.android.internal.R.layout.screen_progress;
2416            // System.out.println("Progress!");
2417        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
2418            // Special case for a window with a custom title.
2419            // If the window is floating, we need a dialog layout
2420            if (mIsFloating) {
2421                TypedValue res = new TypedValue();
2422                getContext().getTheme().resolveAttribute(
2423                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
2424                layoutResource = res.resourceId;
2425            } else {
2426                layoutResource = com.android.internal.R.layout.screen_custom_title;
2427            }
2428            // XXX Remove this once action bar supports these features.
2429            removeFeature(FEATURE_ACTION_BAR);
2430        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
2431            // If no other features and not embedded, only need a title.
2432            // If the window is floating, we need a dialog layout
2433            if (mIsFloating) {
2434                TypedValue res = new TypedValue();
2435                getContext().getTheme().resolveAttribute(
2436                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
2437                layoutResource = res.resourceId;
2438            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
2439                if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
2440                    layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
2441                } else {
2442                    layoutResource = com.android.internal.R.layout.screen_action_bar;
2443                }
2444            } else {
2445                layoutResource = com.android.internal.R.layout.screen_title;
2446            }
2447            // System.out.println("Title!");
2448        } else {
2449            // Embedded, so no decoration is needed.
2450            layoutResource = com.android.internal.R.layout.screen_simple;
2451            // System.out.println("Simple!");
2452        }
2453
2454        mDecor.startChanging();
2455
2456        View in = mLayoutInflater.inflate(layoutResource, null);
2457        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
2458
2459        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
2460        if (contentParent == null) {
2461            throw new RuntimeException("Window couldn't find content container view");
2462        }
2463
2464        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
2465            ProgressBar progress = getCircularProgressBar(false);
2466            if (progress != null) {
2467                progress.setIndeterminate(true);
2468            }
2469        }
2470
2471        // Remaining setup -- of background and title -- that only applies
2472        // to top-level windows.
2473        if (getContainer() == null) {
2474            Drawable drawable = mBackgroundDrawable;
2475            if (mBackgroundResource != 0) {
2476                drawable = getContext().getResources().getDrawable(mBackgroundResource);
2477            }
2478            mDecor.setWindowBackground(drawable);
2479            drawable = null;
2480            if (mFrameResource != 0) {
2481                drawable = getContext().getResources().getDrawable(mFrameResource);
2482            }
2483            mDecor.setWindowFrame(drawable);
2484
2485            // System.out.println("Text=" + Integer.toHexString(mTextColor) +
2486            // " Sel=" + Integer.toHexString(mTextSelectedColor) +
2487            // " Title=" + Integer.toHexString(mTitleColor));
2488
2489            if (mTitleColor == 0) {
2490                mTitleColor = mTextColor;
2491            }
2492
2493            if (mTitle != null) {
2494                setTitle(mTitle);
2495            }
2496            setTitleColor(mTitleColor);
2497        }
2498
2499        mDecor.finishChanging();
2500
2501        return contentParent;
2502    }
2503
2504    /** @hide */
2505    public void alwaysReadCloseOnTouchAttr() {
2506        mAlwaysReadCloseOnTouchAttr = true;
2507    }
2508
2509    private void installDecor() {
2510        if (mDecor == null) {
2511            mDecor = generateDecor();
2512            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
2513            mDecor.setIsRootNamespace(true);
2514        }
2515        if (mContentParent == null) {
2516            mContentParent = generateLayout(mDecor);
2517
2518            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
2519            if (mTitleView != null) {
2520                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
2521                    View titleContainer = findViewById(com.android.internal.R.id.title_container);
2522                    if (titleContainer != null) {
2523                        titleContainer.setVisibility(View.GONE);
2524                    } else {
2525                        mTitleView.setVisibility(View.GONE);
2526                    }
2527                    if (mContentParent instanceof FrameLayout) {
2528                        ((FrameLayout)mContentParent).setForeground(null);
2529                    }
2530                } else {
2531                    mTitleView.setText(mTitle);
2532                }
2533            } else {
2534                mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
2535                if (mActionBar != null) {
2536                    if (mActionBar.getTitle() == null) {
2537                        mActionBar.setWindowTitle(mTitle);
2538                    }
2539                    final int localFeatures = getLocalFeatures();
2540                    if ((localFeatures & (1 << FEATURE_PROGRESS)) != 0) {
2541                        mActionBar.initProgress();
2542                    }
2543                    if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
2544                        mActionBar.initIndeterminateProgress();
2545                    }
2546                    // Post the panel invalidate for later; avoid application onCreateOptionsMenu
2547                    // being called in the middle of onCreate or similar.
2548                    mDecor.post(new Runnable() {
2549                        public void run() {
2550                            if (!isDestroyed()) {
2551                                invalidatePanelMenu(FEATURE_ACTION_BAR);
2552                            }
2553                        }
2554                    });
2555                }
2556            }
2557        }
2558    }
2559
2560    private Drawable loadImageURI(Uri uri) {
2561        try {
2562            return Drawable.createFromStream(
2563                    getContext().getContentResolver().openInputStream(uri), null);
2564        } catch (Exception e) {
2565            Log.w(TAG, "Unable to open content: " + uri);
2566        }
2567        return null;
2568    }
2569
2570    private DrawableFeatureState getDrawableState(int featureId, boolean required) {
2571        if ((getFeatures() & (1 << featureId)) == 0) {
2572            if (!required) {
2573                return null;
2574            }
2575            throw new RuntimeException("The feature has not been requested");
2576        }
2577
2578        DrawableFeatureState[] ar;
2579        if ((ar = mDrawables) == null || ar.length <= featureId) {
2580            DrawableFeatureState[] nar = new DrawableFeatureState[featureId + 1];
2581            if (ar != null) {
2582                System.arraycopy(ar, 0, nar, 0, ar.length);
2583            }
2584            mDrawables = ar = nar;
2585        }
2586
2587        DrawableFeatureState st = ar[featureId];
2588        if (st == null) {
2589            ar[featureId] = st = new DrawableFeatureState(featureId);
2590        }
2591        return st;
2592    }
2593
2594    /**
2595     * Gets a panel's state based on its feature ID.
2596     *
2597     * @param featureId The feature ID of the panel.
2598     * @param required Whether the panel is required (if it is required and it
2599     *            isn't in our features, this throws an exception).
2600     * @return The panel state.
2601     */
2602    private PanelFeatureState getPanelState(int featureId, boolean required) {
2603        return getPanelState(featureId, required, null);
2604    }
2605
2606    /**
2607     * Gets a panel's state based on its feature ID.
2608     *
2609     * @param featureId The feature ID of the panel.
2610     * @param required Whether the panel is required (if it is required and it
2611     *            isn't in our features, this throws an exception).
2612     * @param convertPanelState Optional: If the panel state does not exist, use
2613     *            this as the panel state.
2614     * @return The panel state.
2615     */
2616    private PanelFeatureState getPanelState(int featureId, boolean required,
2617            PanelFeatureState convertPanelState) {
2618        if ((getFeatures() & (1 << featureId)) == 0) {
2619            if (!required) {
2620                return null;
2621            }
2622            throw new RuntimeException("The feature has not been requested");
2623        }
2624
2625        PanelFeatureState[] ar;
2626        if ((ar = mPanels) == null || ar.length <= featureId) {
2627            PanelFeatureState[] nar = new PanelFeatureState[featureId + 1];
2628            if (ar != null) {
2629                System.arraycopy(ar, 0, nar, 0, ar.length);
2630            }
2631            mPanels = ar = nar;
2632        }
2633
2634        PanelFeatureState st = ar[featureId];
2635        if (st == null) {
2636            ar[featureId] = st = (convertPanelState != null)
2637                    ? convertPanelState
2638                    : new PanelFeatureState(featureId);
2639        }
2640        return st;
2641    }
2642
2643    @Override
2644    public final void setChildDrawable(int featureId, Drawable drawable) {
2645        DrawableFeatureState st = getDrawableState(featureId, true);
2646        st.child = drawable;
2647        updateDrawable(featureId, st, false);
2648    }
2649
2650    @Override
2651    public final void setChildInt(int featureId, int value) {
2652        updateInt(featureId, value, false);
2653    }
2654
2655    @Override
2656    public boolean isShortcutKey(int keyCode, KeyEvent event) {
2657        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
2658        return st.menu != null && st.menu.isShortcutKey(keyCode, event);
2659    }
2660
2661    private void updateDrawable(int featureId, DrawableFeatureState st, boolean fromResume) {
2662        // Do nothing if the decor is not yet installed... an update will
2663        // need to be forced when we eventually become active.
2664        if (mContentParent == null) {
2665            return;
2666        }
2667
2668        final int featureMask = 1 << featureId;
2669
2670        if ((getFeatures() & featureMask) == 0 && !fromResume) {
2671            return;
2672        }
2673
2674        Drawable drawable = null;
2675        if (st != null) {
2676            drawable = st.child;
2677            if (drawable == null)
2678                drawable = st.local;
2679            if (drawable == null)
2680                drawable = st.def;
2681        }
2682        if ((getLocalFeatures() & featureMask) == 0) {
2683            if (getContainer() != null) {
2684                if (isActive() || fromResume) {
2685                    getContainer().setChildDrawable(featureId, drawable);
2686                }
2687            }
2688        } else if (st != null && (st.cur != drawable || st.curAlpha != st.alpha)) {
2689            // System.out.println("Drawable changed: old=" + st.cur
2690            // + ", new=" + drawable);
2691            st.cur = drawable;
2692            st.curAlpha = st.alpha;
2693            onDrawableChanged(featureId, drawable, st.alpha);
2694        }
2695    }
2696
2697    private void updateInt(int featureId, int value, boolean fromResume) {
2698
2699        // Do nothing if the decor is not yet installed... an update will
2700        // need to be forced when we eventually become active.
2701        if (mContentParent == null) {
2702            return;
2703        }
2704
2705        final int featureMask = 1 << featureId;
2706
2707        if ((getFeatures() & featureMask) == 0 && !fromResume) {
2708            return;
2709        }
2710
2711        if ((getLocalFeatures() & featureMask) == 0) {
2712            if (getContainer() != null) {
2713                getContainer().setChildInt(featureId, value);
2714            }
2715        } else {
2716            onIntChanged(featureId, value);
2717        }
2718    }
2719
2720    private ImageView getLeftIconView() {
2721        if (mLeftIconView != null) {
2722            return mLeftIconView;
2723        }
2724        if (mContentParent == null) {
2725            installDecor();
2726        }
2727        return (mLeftIconView = (ImageView)findViewById(com.android.internal.R.id.left_icon));
2728    }
2729
2730    private ProgressBar getCircularProgressBar(boolean shouldInstallDecor) {
2731        if (mCircularProgressBar != null) {
2732            return mCircularProgressBar;
2733        }
2734        if (mContentParent == null && shouldInstallDecor) {
2735            installDecor();
2736        }
2737        mCircularProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_circular);
2738        if (mCircularProgressBar != null) {
2739            mCircularProgressBar.setVisibility(View.INVISIBLE);
2740        }
2741        return mCircularProgressBar;
2742    }
2743
2744    private ProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) {
2745        if (mHorizontalProgressBar != null) {
2746            return mHorizontalProgressBar;
2747        }
2748        if (mContentParent == null && shouldInstallDecor) {
2749            installDecor();
2750        }
2751        mHorizontalProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_horizontal);
2752        if (mHorizontalProgressBar != null) {
2753            mHorizontalProgressBar.setVisibility(View.INVISIBLE);
2754        }
2755        return mHorizontalProgressBar;
2756    }
2757
2758    private ImageView getRightIconView() {
2759        if (mRightIconView != null) {
2760            return mRightIconView;
2761        }
2762        if (mContentParent == null) {
2763            installDecor();
2764        }
2765        return (mRightIconView = (ImageView)findViewById(com.android.internal.R.id.right_icon));
2766    }
2767
2768    /**
2769     * Helper method for calling the {@link Callback#onPanelClosed(int, Menu)}
2770     * callback. This method will grab whatever extra state is needed for the
2771     * callback that isn't given in the parameters. If the panel is not open,
2772     * this will not perform the callback.
2773     *
2774     * @param featureId Feature ID of the panel that was closed. Must be given.
2775     * @param panel Panel that was closed. Optional but useful if there is no
2776     *            menu given.
2777     * @param menu The menu that was closed. Optional, but give if you have.
2778     */
2779    private void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) {
2780        final Callback cb = getCallback();
2781        if (cb == null)
2782            return;
2783
2784        // Try to get a menu
2785        if (menu == null) {
2786            // Need a panel to grab the menu, so try to get that
2787            if (panel == null) {
2788                if ((featureId >= 0) && (featureId < mPanels.length)) {
2789                    panel = mPanels[featureId];
2790                }
2791            }
2792
2793            if (panel != null) {
2794                // menu still may be null, which is okay--we tried our best
2795                menu = panel.menu;
2796            }
2797        }
2798
2799        // If the panel is not open, do not callback
2800        if ((panel != null) && (!panel.isOpen))
2801            return;
2802
2803        cb.onPanelClosed(featureId, menu);
2804    }
2805
2806    /**
2807     * Helper method for adding launch-search to most applications. Opens the
2808     * search window using default settings.
2809     *
2810     * @return true if search window opened
2811     */
2812    private boolean launchDefaultSearch() {
2813        final Callback cb = getCallback();
2814        if (cb == null) {
2815            return false;
2816        } else {
2817            sendCloseSystemWindows("search");
2818            return cb.onSearchRequested();
2819        }
2820    }
2821
2822    @Override
2823    public void setVolumeControlStream(int streamType) {
2824        mVolumeControlStreamType = streamType;
2825    }
2826
2827    @Override
2828    public int getVolumeControlStream() {
2829        return mVolumeControlStreamType;
2830    }
2831
2832    private static final class DrawableFeatureState {
2833        DrawableFeatureState(int _featureId) {
2834            featureId = _featureId;
2835        }
2836
2837        final int featureId;
2838
2839        int resid;
2840
2841        Uri uri;
2842
2843        Drawable local;
2844
2845        Drawable child;
2846
2847        Drawable def;
2848
2849        Drawable cur;
2850
2851        int alpha = 255;
2852
2853        int curAlpha = 255;
2854    }
2855
2856    private static final class PanelFeatureState {
2857
2858        /** Feature ID for this panel. */
2859        int featureId;
2860
2861        // Information pulled from the style for this panel.
2862
2863        int background;
2864
2865        /** The background when the panel spans the entire available width. */
2866        int fullBackground;
2867
2868        int gravity;
2869
2870        int x;
2871
2872        int y;
2873
2874        int windowAnimations;
2875
2876        /** Dynamic state of the panel. */
2877        DecorView decorView;
2878
2879        /** The panel that was returned by onCreatePanelView(). */
2880        View createdPanelView;
2881
2882        /** The panel that we are actually showing. */
2883        View shownPanelView;
2884
2885        /** Use {@link #setMenu} to set this. */
2886        Menu menu;
2887
2888        /**
2889         * Whether the panel has been prepared (see
2890         * {@link PhoneWindow#preparePanel}).
2891         */
2892        boolean isPrepared;
2893
2894        /**
2895         * Whether an item's action has been performed. This happens in obvious
2896         * scenarios (user clicks on menu item), but can also happen with
2897         * chording menu+(shortcut key).
2898         */
2899        boolean isHandled;
2900
2901        boolean isOpen;
2902
2903        /**
2904         * True if the menu is in expanded mode, false if the menu is in icon
2905         * mode
2906         */
2907        boolean isInExpandedMode;
2908
2909        public boolean qwertyMode;
2910
2911        boolean refreshDecorView;
2912
2913        boolean refreshMenuContent;
2914
2915        boolean wasLastOpen;
2916
2917        boolean wasLastExpanded;
2918
2919        /**
2920         * Contains the state of the menu when told to freeze.
2921         */
2922        Bundle frozenMenuState;
2923
2924        PanelFeatureState(int featureId) {
2925            this.featureId = featureId;
2926
2927            refreshDecorView = false;
2928        }
2929
2930        void setStyle(Context context) {
2931            TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
2932            background = a.getResourceId(
2933                    com.android.internal.R.styleable.Theme_panelBackground, 0);
2934            fullBackground = a.getResourceId(
2935                    com.android.internal.R.styleable.Theme_panelFullBackground, 0);
2936            windowAnimations = a.getResourceId(
2937                    com.android.internal.R.styleable.Theme_windowAnimationStyle, 0);
2938            a.recycle();
2939        }
2940
2941        void setMenu(Menu menu) {
2942            this.menu = menu;
2943
2944            if (frozenMenuState != null) {
2945                ((MenuBuilder) menu).restoreHierarchyState(frozenMenuState);
2946                frozenMenuState = null;
2947            }
2948        }
2949
2950        Parcelable onSaveInstanceState() {
2951            SavedState savedState = new SavedState();
2952            savedState.featureId = featureId;
2953            savedState.isOpen = isOpen;
2954            savedState.isInExpandedMode = isInExpandedMode;
2955
2956            if (menu != null) {
2957                savedState.menuState = new Bundle();
2958                ((MenuBuilder) menu).saveHierarchyState(savedState.menuState);
2959            }
2960
2961            return savedState;
2962        }
2963
2964        void onRestoreInstanceState(Parcelable state) {
2965            SavedState savedState = (SavedState) state;
2966            featureId = savedState.featureId;
2967            wasLastOpen = savedState.isOpen;
2968            wasLastExpanded = savedState.isInExpandedMode;
2969            frozenMenuState = savedState.menuState;
2970
2971            /*
2972             * A LocalActivityManager keeps the same instance of this class around.
2973             * The first time the menu is being shown after restoring, the
2974             * Activity.onCreateOptionsMenu should be called. But, if it is the
2975             * same instance then menu != null and we won't call that method.
2976             * So, clear this.  Also clear any cached views.
2977             */
2978            menu = null;
2979            createdPanelView = null;
2980            shownPanelView = null;
2981            decorView = null;
2982        }
2983
2984        private static class SavedState implements Parcelable {
2985            int featureId;
2986            boolean isOpen;
2987            boolean isInExpandedMode;
2988            Bundle menuState;
2989
2990            public int describeContents() {
2991                return 0;
2992            }
2993
2994            public void writeToParcel(Parcel dest, int flags) {
2995                dest.writeInt(featureId);
2996                dest.writeInt(isOpen ? 1 : 0);
2997                dest.writeInt(isInExpandedMode ? 1 : 0);
2998
2999                if (isOpen) {
3000                    dest.writeBundle(menuState);
3001                }
3002            }
3003
3004            private static SavedState readFromParcel(Parcel source) {
3005                SavedState savedState = new SavedState();
3006                savedState.featureId = source.readInt();
3007                savedState.isOpen = source.readInt() == 1;
3008                savedState.isInExpandedMode = source.readInt() == 1;
3009
3010                if (savedState.isOpen) {
3011                    savedState.menuState = source.readBundle();
3012                }
3013
3014                return savedState;
3015            }
3016
3017            public static final Parcelable.Creator<SavedState> CREATOR
3018                    = new Parcelable.Creator<SavedState>() {
3019                public SavedState createFromParcel(Parcel in) {
3020                    return readFromParcel(in);
3021                }
3022
3023                public SavedState[] newArray(int size) {
3024                    return new SavedState[size];
3025                }
3026            };
3027        }
3028
3029    }
3030
3031    /**
3032     * Simple implementation of MenuBuilder.Callback that:
3033     * <li> Opens a submenu when selected.
3034     * <li> Calls back to the callback's onMenuItemSelected when an item is
3035     * selected.
3036     */
3037    private final class DialogMenuCallback implements MenuBuilder.Callback {
3038        private int mFeatureId;
3039        private MenuDialogHelper mSubMenuHelper;
3040
3041        public DialogMenuCallback(int featureId) {
3042            mFeatureId = featureId;
3043        }
3044
3045        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
3046            if (allMenusAreClosing) {
3047                Callback callback = getCallback();
3048                if (callback != null) callback.onPanelClosed(mFeatureId, menu);
3049
3050                if (menu == mContextMenu) {
3051                    dismissContextMenu();
3052                }
3053
3054                // Dismiss the submenu, if it is showing
3055                if (mSubMenuHelper != null) {
3056                    mSubMenuHelper.dismiss();
3057                    mSubMenuHelper = null;
3058                }
3059            }
3060        }
3061
3062        public void onCloseSubMenu(SubMenuBuilder menu) {
3063            Callback callback = getCallback();
3064            if (callback != null) callback.onPanelClosed(mFeatureId, menu.getRootMenu());
3065        }
3066
3067        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
3068            Callback callback = getCallback();
3069            return (callback != null) && callback.onMenuItemSelected(mFeatureId, item);
3070        }
3071
3072        public void onMenuModeChange(MenuBuilder menu) {
3073        }
3074
3075        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
3076            // Set a simple callback for the submenu
3077            subMenu.setCallback(this);
3078
3079            // The window manager will give us a valid window token
3080            mSubMenuHelper = new MenuDialogHelper(subMenu);
3081            mSubMenuHelper.show(null);
3082
3083            return true;
3084        }
3085    }
3086
3087    void sendCloseSystemWindows() {
3088        PhoneWindowManager.sendCloseSystemWindows(getContext(), null);
3089    }
3090
3091    void sendCloseSystemWindows(String reason) {
3092        PhoneWindowManager.sendCloseSystemWindows(getContext(), reason);
3093    }
3094
3095    private class ActionButtonSubmenu extends MenuPopupHelper implements Runnable {
3096        private SubMenuBuilder mSubMenu;
3097
3098        public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
3099            super(context, subMenu);
3100            mSubMenu = subMenu;
3101
3102            MenuBuilder parentMenu = subMenu.getRootMenu();
3103            MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
3104            if (!item.isActionButton()) {
3105                // Give a reasonable anchor to nested submenus.
3106                ActionMenuView amv = (ActionMenuView) parentMenu.getMenuView(
3107                        MenuBuilder.TYPE_ACTION_BUTTON, null);
3108
3109                View anchor = amv.getOverflowButton();
3110                if (anchor == null) {
3111                    anchor = amv;
3112                }
3113                setAnchorView(anchor);
3114            }
3115        }
3116
3117        @Override
3118        public void onDismiss() {
3119            super.onDismiss();
3120            mSubMenu.getCallback().onCloseSubMenu(mSubMenu);
3121            mActionButtonPopup = null;
3122        }
3123
3124        @Override
3125        public void run() {
3126            if (tryShow()) {
3127                Callback cb = getCallback();
3128                if (cb != null) {
3129                    cb.onMenuOpened(FEATURE_ACTION_BAR, mSubMenu);
3130                }
3131            }
3132        }
3133    }
3134}
3135