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