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