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