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