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