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