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