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