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