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