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