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