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