[go: nahoru, domu]

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