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