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