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