• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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 androidx.appcompat.app;
18 
19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21 import static android.view.Window.FEATURE_OPTIONS_PANEL;
22 
23 import android.app.Activity;
24 import android.app.Dialog;
25 import android.app.UiModeManager;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.ActivityInfo;
32 import android.content.pm.PackageManager;
33 import android.content.res.Configuration;
34 import android.content.res.Resources;
35 import android.content.res.TypedArray;
36 import android.graphics.PixelFormat;
37 import android.graphics.Rect;
38 import android.graphics.drawable.Drawable;
39 import android.media.AudioManager;
40 import android.os.Build;
41 import android.os.Bundle;
42 import android.os.Parcel;
43 import android.os.Parcelable;
44 import android.text.TextUtils;
45 import android.util.AndroidRuntimeException;
46 import android.util.AttributeSet;
47 import android.util.DisplayMetrics;
48 import android.util.Log;
49 import android.util.TypedValue;
50 import android.view.Gravity;
51 import android.view.KeyCharacterMap;
52 import android.view.KeyEvent;
53 import android.view.KeyboardShortcutGroup;
54 import android.view.LayoutInflater;
55 import android.view.Menu;
56 import android.view.MenuInflater;
57 import android.view.MenuItem;
58 import android.view.MotionEvent;
59 import android.view.View;
60 import android.view.ViewConfiguration;
61 import android.view.ViewGroup;
62 import android.view.ViewParent;
63 import android.view.Window;
64 import android.view.WindowManager;
65 import android.view.accessibility.AccessibilityEvent;
66 import android.widget.FrameLayout;
67 import android.widget.PopupWindow;
68 import android.widget.TextView;
69 
70 import androidx.annotation.IdRes;
71 import androidx.annotation.NonNull;
72 import androidx.annotation.Nullable;
73 import androidx.annotation.RequiresApi;
74 import androidx.annotation.VisibleForTesting;
75 import androidx.appcompat.R;
76 import androidx.appcompat.content.res.AppCompatResources;
77 import androidx.appcompat.view.ActionMode;
78 import androidx.appcompat.view.ContextThemeWrapper;
79 import androidx.appcompat.view.StandaloneActionMode;
80 import androidx.appcompat.view.SupportActionModeWrapper;
81 import androidx.appcompat.view.SupportMenuInflater;
82 import androidx.appcompat.view.WindowCallbackWrapper;
83 import androidx.appcompat.view.menu.ListMenuPresenter;
84 import androidx.appcompat.view.menu.MenuBuilder;
85 import androidx.appcompat.view.menu.MenuPresenter;
86 import androidx.appcompat.view.menu.MenuView;
87 import androidx.appcompat.widget.ActionBarContextView;
88 import androidx.appcompat.widget.AppCompatDrawableManager;
89 import androidx.appcompat.widget.ContentFrameLayout;
90 import androidx.appcompat.widget.DecorContentParent;
91 import androidx.appcompat.widget.FitWindowsViewGroup;
92 import androidx.appcompat.widget.TintTypedArray;
93 import androidx.appcompat.widget.Toolbar;
94 import androidx.appcompat.widget.VectorEnabledTintResources;
95 import androidx.appcompat.widget.ViewStubCompat;
96 import androidx.appcompat.widget.ViewUtils;
97 import androidx.core.app.NavUtils;
98 import androidx.core.view.LayoutInflaterCompat;
99 import androidx.core.view.OnApplyWindowInsetsListener;
100 import androidx.core.view.ViewCompat;
101 import androidx.core.view.ViewPropertyAnimatorCompat;
102 import androidx.core.view.ViewPropertyAnimatorListenerAdapter;
103 import androidx.core.view.WindowCompat;
104 import androidx.core.view.WindowInsetsCompat;
105 import androidx.core.widget.PopupWindowCompat;
106 
107 import org.xmlpull.v1.XmlPullParser;
108 
109 import java.util.List;
110 
111 class AppCompatDelegateImpl extends AppCompatDelegate
112         implements MenuBuilder.Callback, LayoutInflater.Factory2 {
113 
114     private static final boolean DEBUG = false;
115     private static final boolean IS_PRE_LOLLIPOP = Build.VERSION.SDK_INT < 21;
116     private static final String KEY_LOCAL_NIGHT_MODE = "appcompat:local_night_mode";
117 
118     private static final int[] sWindowBackgroundStyleable = {android.R.attr.windowBackground};
119 
120     private static boolean sInstalledExceptionHandler;
121 
122     static final String EXCEPTION_HANDLER_MESSAGE_SUFFIX= ". If the resource you are"
123             + " trying to use is a vector resource, you may be referencing it in an unsupported"
124             + " way. See AppCompatDelegate.setCompatVectorFromResourcesEnabled() for more info.";
125 
126     static {
127         if (IS_PRE_LOLLIPOP && !sInstalledExceptionHandler) {
128             final Thread.UncaughtExceptionHandler defHandler
129                     = Thread.getDefaultUncaughtExceptionHandler();
130 
131             Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
132                 @Override
133                 public void uncaughtException(Thread thread, final Throwable thowable) {
134                     if (shouldWrapException(thowable)) {
135                         // Now wrap the throwable, but append some extra information to the message
136                         final Throwable wrapped = new Resources.NotFoundException(
137                                 thowable.getMessage() + EXCEPTION_HANDLER_MESSAGE_SUFFIX);
138                         wrapped.initCause(thowable.getCause());
139                         wrapped.setStackTrace(thowable.getStackTrace());
140                         defHandler.uncaughtException(thread, wrapped);
141                     } else {
142                         defHandler.uncaughtException(thread, thowable);
143                     }
144                 }
145 
146                 private boolean shouldWrapException(Throwable throwable) {
147                     if (throwable instanceof Resources.NotFoundException) {
148                         final String message = throwable.getMessage();
149                         return message != null && (message.contains("drawable")
150                                 || message.contains("Drawable"));
151                     }
152                     return false;
153                 }
154             });
155 
156             sInstalledExceptionHandler = true;
157         }
158     }
159 
160     final Context mContext;
161     final Window mWindow;
162     final Window.Callback mOriginalWindowCallback;
163     final Window.Callback mAppCompatWindowCallback;
164     final AppCompatCallback mAppCompatCallback;
165 
166     ActionBar mActionBar;
167     MenuInflater mMenuInflater;
168 
169     private CharSequence mTitle;
170 
171     private DecorContentParent mDecorContentParent;
172     private ActionMenuPresenterCallback mActionMenuPresenterCallback;
173     private PanelMenuPresenterCallback mPanelMenuPresenterCallback;
174 
175     ActionMode mActionMode;
176     ActionBarContextView mActionModeView;
177     PopupWindow mActionModePopup;
178     Runnable mShowActionModePopup;
179     ViewPropertyAnimatorCompat mFadeAnim = null;
180 
181     private boolean mHandleNativeActionModes = true; // defaults to true
182 
183     // true if we have installed a window sub-decor layout.
184     private boolean mSubDecorInstalled;
185     private ViewGroup mSubDecor;
186 
187     private TextView mTitleView;
188     private View mStatusGuard;
189 
190     // Used to keep track of Progress Bar Window features
191     private boolean mFeatureProgress, mFeatureIndeterminateProgress;
192 
193     // true if this activity has an action bar.
194     boolean mHasActionBar;
195     // true if this activity's action bar overlays other activity content.
196     boolean mOverlayActionBar;
197     // true if this any action modes should overlay the activity content
198     boolean mOverlayActionMode;
199     // true if this activity is floating (e.g. Dialog)
200     boolean mIsFloating;
201     // true if this activity has no title
202     boolean mWindowNoTitle;
203 
204     // Used for emulating PanelFeatureState
205     private boolean mClosingActionMenu;
206     private PanelFeatureState[] mPanels;
207     private PanelFeatureState mPreparedPanel;
208 
209     private boolean mLongPressBackDown;
210 
211     private boolean mIsDestroyed;
212 
213     @NightMode
214     private int mLocalNightMode = MODE_NIGHT_UNSPECIFIED;
215     private boolean mApplyDayNightCalled;
216 
217     private AutoNightModeManager mAutoNightModeManager;
218 
219     boolean mInvalidatePanelMenuPosted;
220     int mInvalidatePanelMenuFeatures;
221     private final Runnable mInvalidatePanelMenuRunnable = new Runnable() {
222         @Override
223         public void run() {
224             if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_OPTIONS_PANEL) != 0) {
225                 doInvalidatePanelMenu(FEATURE_OPTIONS_PANEL);
226             }
227             if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_SUPPORT_ACTION_BAR) != 0) {
228                 doInvalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
229             }
230             mInvalidatePanelMenuPosted = false;
231             mInvalidatePanelMenuFeatures = 0;
232         }
233     };
234 
235     private boolean mEnableDefaultActionBarUp;
236 
237     private Rect mTempRect1;
238     private Rect mTempRect2;
239 
240     private AppCompatViewInflater mAppCompatViewInflater;
241 
242     AppCompatDelegateImpl(Context context, Window window, AppCompatCallback callback) {
243         mContext = context;
244         mWindow = window;
245         mAppCompatCallback = callback;
246 
247         mOriginalWindowCallback = mWindow.getCallback();
248         if (mOriginalWindowCallback instanceof AppCompatWindowCallback) {
249             throw new IllegalStateException(
250                     "AppCompat has already installed itself into the Window");
251         }
252         mAppCompatWindowCallback = new AppCompatWindowCallback(mOriginalWindowCallback);
253         // Now install the new callback
254         mWindow.setCallback(mAppCompatWindowCallback);
255 
256         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
257                 context, null, sWindowBackgroundStyleable);
258         final Drawable winBg = a.getDrawableIfKnown(0);
259         if (winBg != null) {
260             mWindow.setBackgroundDrawable(winBg);
261         }
262         a.recycle();
263     }
264 
265     @Override
266     public void onCreate(Bundle savedInstanceState) {
267         if (mOriginalWindowCallback instanceof Activity) {
268             if (NavUtils.getParentActivityName((Activity) mOriginalWindowCallback) != null) {
269                 // Peek at the Action Bar and update it if it already exists
270                 ActionBar ab = peekSupportActionBar();
271                 if (ab == null) {
272                     mEnableDefaultActionBarUp = true;
273                 } else {
274                     ab.setDefaultDisplayHomeAsUpEnabled(true);
275                 }
276             }
277         }
278 
279         if (savedInstanceState != null && mLocalNightMode == MODE_NIGHT_UNSPECIFIED) {
280             // If we have a icicle and we haven't had a local night mode set yet, try and read
281             // it from the icicle
282             mLocalNightMode = savedInstanceState.getInt(KEY_LOCAL_NIGHT_MODE,
283                     MODE_NIGHT_UNSPECIFIED);
284         }
285     }
286 
287     @Override
288     public void onPostCreate(Bundle savedInstanceState) {
289         // Make sure that the sub decor is installed
290         ensureSubDecor();
291     }
292 
293     @Override
294     public ActionBar getSupportActionBar() {
295         // The Action Bar should be lazily created as hasActionBar
296         // could change after onCreate
297         initWindowDecorActionBar();
298         return mActionBar;
299     }
300 
301     final ActionBar peekSupportActionBar() {
302         return mActionBar;
303     }
304 
305     final Window.Callback getWindowCallback() {
306         return mWindow.getCallback();
307     }
308 
309     private void initWindowDecorActionBar() {
310         ensureSubDecor();
311 
312         if (!mHasActionBar || mActionBar != null) {
313             return;
314         }
315 
316         if (mOriginalWindowCallback instanceof Activity) {
317             mActionBar = new WindowDecorActionBar((Activity) mOriginalWindowCallback,
318                     mOverlayActionBar);
319         } else if (mOriginalWindowCallback instanceof Dialog) {
320             mActionBar = new WindowDecorActionBar((Dialog) mOriginalWindowCallback);
321         }
322         if (mActionBar != null) {
323             mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
324         }
325     }
326 
327     @Override
328     public void setSupportActionBar(Toolbar toolbar) {
329         if (!(mOriginalWindowCallback instanceof Activity)) {
330             // Only Activities support custom Action Bars
331             return;
332         }
333 
334         final ActionBar ab = getSupportActionBar();
335         if (ab instanceof WindowDecorActionBar) {
336             throw new IllegalStateException("This Activity already has an action bar supplied " +
337                     "by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set " +
338                     "windowActionBar to false in your theme to use a Toolbar instead.");
339         }
340 
341         // If we reach here then we're setting a new action bar
342         // First clear out the MenuInflater to make sure that it is valid for the new Action Bar
343         mMenuInflater = null;
344 
345         // If we have an action bar currently, destroy it
346         if (ab != null) {
347             ab.onDestroy();
348         }
349 
350         if (toolbar != null) {
351             final ToolbarActionBar tbab = new ToolbarActionBar(toolbar,
352                     ((Activity) mOriginalWindowCallback).getTitle(), mAppCompatWindowCallback);
353             mActionBar = tbab;
354             mWindow.setCallback(tbab.getWrappedWindowCallback());
355         } else {
356             mActionBar = null;
357             // Re-set the original window callback since we may have already set a Toolbar wrapper
358             mWindow.setCallback(mAppCompatWindowCallback);
359         }
360 
361         invalidateOptionsMenu();
362     }
363 
364     final Context getActionBarThemedContext() {
365         Context context = null;
366 
367         // If we have an action bar, let it return a themed context
368         ActionBar ab = getSupportActionBar();
369         if (ab != null) {
370             context = ab.getThemedContext();
371         }
372 
373         if (context == null) {
374             context = mContext;
375         }
376         return context;
377     }
378 
379     @Override
380     public MenuInflater getMenuInflater() {
381         // Make sure that action views can get an appropriate theme.
382         if (mMenuInflater == null) {
383             initWindowDecorActionBar();
384             mMenuInflater = new SupportMenuInflater(
385                     mActionBar != null ? mActionBar.getThemedContext() : mContext);
386         }
387         return mMenuInflater;
388     }
389 
390     @SuppressWarnings("TypeParameterUnusedInFormals")
391     @Nullable
392     @Override
393     public <T extends View> T findViewById(@IdRes int id) {
394         ensureSubDecor();
395         return (T) mWindow.findViewById(id);
396     }
397 
398     @Override
399     public void onConfigurationChanged(Configuration newConfig) {
400         // If this is called before sub-decor is installed, ActionBar will not
401         // be properly initialized.
402         if (mHasActionBar && mSubDecorInstalled) {
403             // Note: The action bar will need to access
404             // view changes from superclass.
405             ActionBar ab = getSupportActionBar();
406             if (ab != null) {
407                 ab.onConfigurationChanged(newConfig);
408             }
409         }
410 
411         // Make sure that the DrawableManager knows about the new config
412         AppCompatDrawableManager.get().onConfigurationChanged(mContext);
413 
414         // Re-apply Day/Night to the new configuration
415         applyDayNight();
416     }
417 
418     @Override
419     public void onStart() {
420         // This will apply day/night if the time has changed, it will also call through to
421         // setupAutoNightModeIfNeeded()
422         applyDayNight();
423     }
424 
425     @Override
426     public void onStop() {
427         ActionBar ab = getSupportActionBar();
428         if (ab != null) {
429             ab.setShowHideAnimationEnabled(false);
430         }
431 
432         // Make sure we clean up any receivers setup for AUTO mode
433         if (mAutoNightModeManager != null) {
434             mAutoNightModeManager.cleanup();
435         }
436     }
437 
438     @Override
439     public void onPostResume() {
440         ActionBar ab = getSupportActionBar();
441         if (ab != null) {
442             ab.setShowHideAnimationEnabled(true);
443         }
444     }
445 
446     @Override
447     public void setContentView(View v) {
448         ensureSubDecor();
449         ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
450         contentParent.removeAllViews();
451         contentParent.addView(v);
452         mOriginalWindowCallback.onContentChanged();
453     }
454 
455     @Override
456     public void setContentView(int resId) {
457         ensureSubDecor();
458         ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
459         contentParent.removeAllViews();
460         LayoutInflater.from(mContext).inflate(resId, contentParent);
461         mOriginalWindowCallback.onContentChanged();
462     }
463 
464     @Override
465     public void setContentView(View v, ViewGroup.LayoutParams lp) {
466         ensureSubDecor();
467         ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
468         contentParent.removeAllViews();
469         contentParent.addView(v, lp);
470         mOriginalWindowCallback.onContentChanged();
471     }
472 
473     @Override
474     public void addContentView(View v, ViewGroup.LayoutParams lp) {
475         ensureSubDecor();
476         ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
477         contentParent.addView(v, lp);
478         mOriginalWindowCallback.onContentChanged();
479     }
480 
481     @Override
482     public void onSaveInstanceState(Bundle outState) {
483         if (mLocalNightMode != MODE_NIGHT_UNSPECIFIED) {
484             // If we have a local night mode set, save it
485             outState.putInt(KEY_LOCAL_NIGHT_MODE, mLocalNightMode);
486         }
487     }
488 
489     @Override
490     public void onDestroy() {
491         if (mInvalidatePanelMenuPosted) {
492             mWindow.getDecorView().removeCallbacks(mInvalidatePanelMenuRunnable);
493         }
494 
495         mIsDestroyed = true;
496 
497         if (mActionBar != null) {
498             mActionBar.onDestroy();
499         }
500 
501         // Make sure we clean up any receivers setup for AUTO mode
502         if (mAutoNightModeManager != null) {
503             mAutoNightModeManager.cleanup();
504         }
505     }
506 
507     private void ensureSubDecor() {
508         if (!mSubDecorInstalled) {
509             mSubDecor = createSubDecor();
510 
511             // If a title was set before we installed the decor, propagate it now
512             CharSequence title = getTitle();
513             if (!TextUtils.isEmpty(title)) {
514                 if (mDecorContentParent != null) {
515                     mDecorContentParent.setWindowTitle(title);
516                 } else if (peekSupportActionBar() != null) {
517                     peekSupportActionBar().setWindowTitle(title);
518                 } else if (mTitleView != null) {
519                     mTitleView.setText(title);
520                 }
521             }
522 
523             applyFixedSizeWindow();
524 
525             onSubDecorInstalled(mSubDecor);
526 
527             mSubDecorInstalled = true;
528 
529             // Invalidate if the panel menu hasn't been created before this.
530             // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
531             // being called in the middle of onCreate or similar.
532             // A pending invalidation will typically be resolved before the posted message
533             // would run normally in order to satisfy instance state restoration.
534             PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
535             if (!mIsDestroyed && (st == null || st.menu == null)) {
536                 invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
537             }
538         }
539     }
540 
541     private ViewGroup createSubDecor() {
542         TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
543 
544         if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
545             a.recycle();
546             throw new IllegalStateException(
547                     "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
548         }
549 
550         if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
551             requestWindowFeature(Window.FEATURE_NO_TITLE);
552         } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
553             // Don't allow an action bar if there is no title.
554             requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
555         }
556         if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
557             requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
558         }
559         if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
560             requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
561         }
562         mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
563         a.recycle();
564 
565         // Now let's make sure that the Window has installed its decor by retrieving it
566         mWindow.getDecorView();
567 
568         final LayoutInflater inflater = LayoutInflater.from(mContext);
569         ViewGroup subDecor = null;
570 
571 
572         if (!mWindowNoTitle) {
573             if (mIsFloating) {
574                 // If we're floating, inflate the dialog title decor
575                 subDecor = (ViewGroup) inflater.inflate(
576                         R.layout.abc_dialog_title_material, null);
577 
578                 // Floating windows can never have an action bar, reset the flags
579                 mHasActionBar = mOverlayActionBar = false;
580             } else if (mHasActionBar) {
581                 /**
582                  * This needs some explanation. As we can not use the android:theme attribute
583                  * pre-L, we emulate it by manually creating a LayoutInflater using a
584                  * ContextThemeWrapper pointing to actionBarTheme.
585                  */
586                 TypedValue outValue = new TypedValue();
587                 mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
588 
589                 Context themedContext;
590                 if (outValue.resourceId != 0) {
591                     themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
592                 } else {
593                     themedContext = mContext;
594                 }
595 
596                 // Now inflate the view using the themed context and set it as the content view
597                 subDecor = (ViewGroup) LayoutInflater.from(themedContext)
598                         .inflate(R.layout.abc_screen_toolbar, null);
599 
600                 mDecorContentParent = (DecorContentParent) subDecor
601                         .findViewById(R.id.decor_content_parent);
602                 mDecorContentParent.setWindowCallback(getWindowCallback());
603 
604                 /**
605                  * Propagate features to DecorContentParent
606                  */
607                 if (mOverlayActionBar) {
608                     mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
609                 }
610                 if (mFeatureProgress) {
611                     mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
612                 }
613                 if (mFeatureIndeterminateProgress) {
614                     mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
615                 }
616             }
617         } else {
618             if (mOverlayActionMode) {
619                 subDecor = (ViewGroup) inflater.inflate(
620                         R.layout.abc_screen_simple_overlay_action_mode, null);
621             } else {
622                 subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
623             }
624 
625             if (Build.VERSION.SDK_INT >= 21) {
626                 // If we're running on L or above, we can rely on ViewCompat's
627                 // setOnApplyWindowInsetsListener
628                 ViewCompat.setOnApplyWindowInsetsListener(subDecor,
629                         new OnApplyWindowInsetsListener() {
630                             @Override
631                             public WindowInsetsCompat onApplyWindowInsets(View v,
632                                     WindowInsetsCompat insets) {
633                                 final int top = insets.getSystemWindowInsetTop();
634                                 final int newTop = updateStatusGuard(top);
635 
636                                 if (top != newTop) {
637                                     insets = insets.replaceSystemWindowInsets(
638                                             insets.getSystemWindowInsetLeft(),
639                                             newTop,
640                                             insets.getSystemWindowInsetRight(),
641                                             insets.getSystemWindowInsetBottom());
642                                 }
643 
644                                 // Now apply the insets on our view
645                                 return ViewCompat.onApplyWindowInsets(v, insets);
646                             }
647                         });
648             } else {
649                 // Else, we need to use our own FitWindowsViewGroup handling
650                 ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
651                         new FitWindowsViewGroup.OnFitSystemWindowsListener() {
652                             @Override
653                             public void onFitSystemWindows(Rect insets) {
654                                 insets.top = updateStatusGuard(insets.top);
655                             }
656                         });
657             }
658         }
659 
660         if (subDecor == null) {
661             throw new IllegalArgumentException(
662                     "AppCompat does not support the current theme features: { "
663                             + "windowActionBar: " + mHasActionBar
664                             + ", windowActionBarOverlay: "+ mOverlayActionBar
665                             + ", android:windowIsFloating: " + mIsFloating
666                             + ", windowActionModeOverlay: " + mOverlayActionMode
667                             + ", windowNoTitle: " + mWindowNoTitle
668                             + " }");
669         }
670 
671         if (mDecorContentParent == null) {
672             mTitleView = (TextView) subDecor.findViewById(R.id.title);
673         }
674 
675         // Make the decor optionally fit system windows, like the window's decor
676         ViewUtils.makeOptionalFitsSystemWindows(subDecor);
677 
678         final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
679                 R.id.action_bar_activity_content);
680 
681         final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
682         if (windowContentView != null) {
683             // There might be Views already added to the Window's content view so we need to
684             // migrate them to our content view
685             while (windowContentView.getChildCount() > 0) {
686                 final View child = windowContentView.getChildAt(0);
687                 windowContentView.removeViewAt(0);
688                 contentView.addView(child);
689             }
690 
691             // Change our content FrameLayout to use the android.R.id.content id.
692             // Useful for fragments.
693             windowContentView.setId(View.NO_ID);
694             contentView.setId(android.R.id.content);
695 
696             // The decorContent may have a foreground drawable set (windowContentOverlay).
697             // Remove this as we handle it ourselves
698             if (windowContentView instanceof FrameLayout) {
699                 ((FrameLayout) windowContentView).setForeground(null);
700             }
701         }
702 
703         // Now set the Window's content view with the decor
704         mWindow.setContentView(subDecor);
705 
706         contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
707             @Override
708             public void onAttachedFromWindow() {}
709 
710             @Override
711             public void onDetachedFromWindow() {
712                 dismissPopups();
713             }
714         });
715 
716         return subDecor;
717     }
718 
719     void onSubDecorInstalled(ViewGroup subDecor) {}
720 
721     private void applyFixedSizeWindow() {
722         ContentFrameLayout cfl = (ContentFrameLayout) mSubDecor.findViewById(android.R.id.content);
723 
724         // This is a bit weird. In the framework, the window sizing attributes control
725         // the decor view's size, meaning that any padding is inset for the min/max widths below.
726         // We don't control measurement at that level, so we need to workaround it by making sure
727         // that the decor view's padding is taken into account.
728         final View windowDecor = mWindow.getDecorView();
729         cfl.setDecorPadding(windowDecor.getPaddingLeft(),
730                 windowDecor.getPaddingTop(), windowDecor.getPaddingRight(),
731                 windowDecor.getPaddingBottom());
732 
733         TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
734         a.getValue(R.styleable.AppCompatTheme_windowMinWidthMajor, cfl.getMinWidthMajor());
735         a.getValue(R.styleable.AppCompatTheme_windowMinWidthMinor, cfl.getMinWidthMinor());
736 
737         if (a.hasValue(R.styleable.AppCompatTheme_windowFixedWidthMajor)) {
738             a.getValue(R.styleable.AppCompatTheme_windowFixedWidthMajor,
739                     cfl.getFixedWidthMajor());
740         }
741         if (a.hasValue(R.styleable.AppCompatTheme_windowFixedWidthMinor)) {
742             a.getValue(R.styleable.AppCompatTheme_windowFixedWidthMinor,
743                     cfl.getFixedWidthMinor());
744         }
745         if (a.hasValue(R.styleable.AppCompatTheme_windowFixedHeightMajor)) {
746             a.getValue(R.styleable.AppCompatTheme_windowFixedHeightMajor,
747                     cfl.getFixedHeightMajor());
748         }
749         if (a.hasValue(R.styleable.AppCompatTheme_windowFixedHeightMinor)) {
750             a.getValue(R.styleable.AppCompatTheme_windowFixedHeightMinor,
751                     cfl.getFixedHeightMinor());
752         }
753         a.recycle();
754 
755         cfl.requestLayout();
756     }
757 
758     @Override
759     public boolean requestWindowFeature(int featureId) {
760         featureId = sanitizeWindowFeatureId(featureId);
761 
762         if (mWindowNoTitle && featureId == FEATURE_SUPPORT_ACTION_BAR) {
763             return false; // Ignore. No title dominates.
764         }
765         if (mHasActionBar && featureId == Window.FEATURE_NO_TITLE) {
766             // Remove the action bar feature if we have no title. No title dominates.
767             mHasActionBar = false;
768         }
769 
770         switch (featureId) {
771             case FEATURE_SUPPORT_ACTION_BAR:
772                 throwFeatureRequestIfSubDecorInstalled();
773                 mHasActionBar = true;
774                 return true;
775             case FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
776                 throwFeatureRequestIfSubDecorInstalled();
777                 mOverlayActionBar = true;
778                 return true;
779             case FEATURE_ACTION_MODE_OVERLAY:
780                 throwFeatureRequestIfSubDecorInstalled();
781                 mOverlayActionMode = true;
782                 return true;
783             case Window.FEATURE_PROGRESS:
784                 throwFeatureRequestIfSubDecorInstalled();
785                 mFeatureProgress = true;
786                 return true;
787             case Window.FEATURE_INDETERMINATE_PROGRESS:
788                 throwFeatureRequestIfSubDecorInstalled();
789                 mFeatureIndeterminateProgress = true;
790                 return true;
791             case Window.FEATURE_NO_TITLE:
792                 throwFeatureRequestIfSubDecorInstalled();
793                 mWindowNoTitle = true;
794                 return true;
795         }
796 
797         return mWindow.requestFeature(featureId);
798     }
799 
800     @Override
801     public boolean hasWindowFeature(int featureId) {
802         boolean result = false;
803         switch (sanitizeWindowFeatureId(featureId)) {
804             case FEATURE_SUPPORT_ACTION_BAR:
805                 result = mHasActionBar;
806                 break;
807             case FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
808                 result = mOverlayActionBar;
809                 break;
810             case FEATURE_ACTION_MODE_OVERLAY:
811                 result = mOverlayActionMode;
812                 break;
813             case Window.FEATURE_PROGRESS:
814                 result = mFeatureProgress;
815                 break;
816             case Window.FEATURE_INDETERMINATE_PROGRESS:
817                 result = mFeatureIndeterminateProgress;
818                 break;
819             case Window.FEATURE_NO_TITLE:
820                 result = mWindowNoTitle;
821                 break;
822         }
823         return result || mWindow.hasFeature(featureId);
824     }
825 
826     @Override
827     public final void setTitle(CharSequence title) {
828         mTitle = title;
829 
830         if (mDecorContentParent != null) {
831             mDecorContentParent.setWindowTitle(title);
832         } else if (peekSupportActionBar() != null) {
833             peekSupportActionBar().setWindowTitle(title);
834         } else if (mTitleView != null) {
835             mTitleView.setText(title);
836         }
837     }
838 
839     final CharSequence getTitle() {
840         // If the original window callback is an Activity, we'll use its title
841         if (mOriginalWindowCallback instanceof Activity) {
842             return ((Activity) mOriginalWindowCallback).getTitle();
843         }
844         // Else, we'll return the title we have recorded ourselves
845         return mTitle;
846     }
847 
848     void onPanelClosed(final int featureId) {
849         if (featureId == FEATURE_SUPPORT_ACTION_BAR) {
850             ActionBar ab = getSupportActionBar();
851             if (ab != null) {
852                 ab.dispatchMenuVisibilityChanged(false);
853             }
854         } else if (featureId == FEATURE_OPTIONS_PANEL) {
855             // Make sure that the options panel is closed. This is mainly used when we're using a
856             // ToolbarActionBar
857             PanelFeatureState st = getPanelState(featureId, true);
858             if (st.isOpen) {
859                 closePanel(st, false);
860             }
861         }
862     }
863 
864     void onMenuOpened(final int featureId) {
865         if (featureId == FEATURE_SUPPORT_ACTION_BAR) {
866             ActionBar ab = getSupportActionBar();
867             if (ab != null) {
868                 ab.dispatchMenuVisibilityChanged(true);
869             }
870         }
871     }
872 
873     @Override
874     public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
875         final Window.Callback cb = getWindowCallback();
876         if (cb != null && !mIsDestroyed) {
877             final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
878             if (panel != null) {
879                 return cb.onMenuItemSelected(panel.featureId, item);
880             }
881         }
882         return false;
883     }
884 
885     @Override
886     public void onMenuModeChange(MenuBuilder menu) {
887         reopenMenu(menu, true);
888     }
889 
890     @Override
891     public ActionMode startSupportActionMode(@NonNull final ActionMode.Callback callback) {
892         if (callback == null) {
893             throw new IllegalArgumentException("ActionMode callback can not be null.");
894         }
895 
896         if (mActionMode != null) {
897             mActionMode.finish();
898         }
899 
900         final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapperV9(callback);
901 
902         ActionBar ab = getSupportActionBar();
903         if (ab != null) {
904             mActionMode = ab.startActionMode(wrappedCallback);
905             if (mActionMode != null && mAppCompatCallback != null) {
906                 mAppCompatCallback.onSupportActionModeStarted(mActionMode);
907             }
908         }
909 
910         if (mActionMode == null) {
911             // If the action bar didn't provide an action mode, start the emulated window one
912             mActionMode = startSupportActionModeFromWindow(wrappedCallback);
913         }
914 
915         return mActionMode;
916     }
917 
918     @Override
919     public void invalidateOptionsMenu() {
920         final ActionBar ab = getSupportActionBar();
921         if (ab != null && ab.invalidateOptionsMenu()) return;
922 
923         invalidatePanelMenu(FEATURE_OPTIONS_PANEL);
924     }
925 
926     ActionMode startSupportActionModeFromWindow(@NonNull ActionMode.Callback callback) {
927         endOnGoingFadeAnimation();
928         if (mActionMode != null) {
929             mActionMode.finish();
930         }
931 
932         if (!(callback instanceof ActionModeCallbackWrapperV9)) {
933             // If the callback hasn't been wrapped yet, wrap it
934             callback = new ActionModeCallbackWrapperV9(callback);
935         }
936 
937         ActionMode mode = null;
938         if (mAppCompatCallback != null && !mIsDestroyed) {
939             try {
940                 mode = mAppCompatCallback.onWindowStartingSupportActionMode(callback);
941             } catch (AbstractMethodError ame) {
942                 // Older apps might not implement this callback method.
943             }
944         }
945 
946         if (mode != null) {
947             mActionMode = mode;
948         } else {
949             if (mActionModeView == null) {
950                 if (mIsFloating) {
951                     // Use the action bar theme.
952                     final TypedValue outValue = new TypedValue();
953                     final Resources.Theme baseTheme = mContext.getTheme();
954                     baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
955 
956                     final Context actionBarContext;
957                     if (outValue.resourceId != 0) {
958                         final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
959                         actionBarTheme.setTo(baseTheme);
960                         actionBarTheme.applyStyle(outValue.resourceId, true);
961 
962                         actionBarContext = new ContextThemeWrapper(mContext, 0);
963                         actionBarContext.getTheme().setTo(actionBarTheme);
964                     } else {
965                         actionBarContext = mContext;
966                     }
967 
968                     mActionModeView = new ActionBarContextView(actionBarContext);
969                     mActionModePopup = new PopupWindow(actionBarContext, null,
970                             R.attr.actionModePopupWindowStyle);
971                     PopupWindowCompat.setWindowLayoutType(mActionModePopup,
972                             WindowManager.LayoutParams.TYPE_APPLICATION);
973                     mActionModePopup.setContentView(mActionModeView);
974                     mActionModePopup.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
975 
976                     actionBarContext.getTheme().resolveAttribute(
977                             R.attr.actionBarSize, outValue, true);
978                     final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
979                             actionBarContext.getResources().getDisplayMetrics());
980                     mActionModeView.setContentHeight(height);
981                     mActionModePopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
982                     mShowActionModePopup = new Runnable() {
983                         @Override
984                         public void run() {
985                             mActionModePopup.showAtLocation(
986                                     mActionModeView,
987                                     Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
988                             endOnGoingFadeAnimation();
989 
990                             if (shouldAnimateActionModeView()) {
991                                 mActionModeView.setAlpha(0f);
992                                 mFadeAnim = ViewCompat.animate(mActionModeView).alpha(1f);
993                                 mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
994                                     @Override
995                                     public void onAnimationStart(View view) {
996                                         mActionModeView.setVisibility(View.VISIBLE);
997                                     }
998 
999                                     @Override
1000                                     public void onAnimationEnd(View view) {
1001                                         mActionModeView.setAlpha(1f);
1002                                         mFadeAnim.setListener(null);
1003                                         mFadeAnim = null;
1004                                     }
1005                                 });
1006                             } else {
1007                                 mActionModeView.setAlpha(1f);
1008                                 mActionModeView.setVisibility(View.VISIBLE);
1009                             }
1010                         }
1011                     };
1012                 } else {
1013                     ViewStubCompat stub = (ViewStubCompat) mSubDecor
1014                             .findViewById(R.id.action_mode_bar_stub);
1015                     if (stub != null) {
1016                         // Set the layout inflater so that it is inflated with the action bar's context
1017                         stub.setLayoutInflater(LayoutInflater.from(getActionBarThemedContext()));
1018                         mActionModeView = (ActionBarContextView) stub.inflate();
1019                     }
1020                 }
1021             }
1022 
1023             if (mActionModeView != null) {
1024                 endOnGoingFadeAnimation();
1025                 mActionModeView.killMode();
1026                 mode = new StandaloneActionMode(mActionModeView.getContext(), mActionModeView,
1027                         callback, mActionModePopup == null);
1028                 if (callback.onCreateActionMode(mode, mode.getMenu())) {
1029                     mode.invalidate();
1030                     mActionModeView.initForMode(mode);
1031                     mActionMode = mode;
1032 
1033                     if (shouldAnimateActionModeView()) {
1034                         mActionModeView.setAlpha(0f);
1035                         mFadeAnim = ViewCompat.animate(mActionModeView).alpha(1f);
1036                         mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
1037                             @Override
1038                             public void onAnimationStart(View view) {
1039                                 mActionModeView.setVisibility(View.VISIBLE);
1040                                 mActionModeView.sendAccessibilityEvent(
1041                                         AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1042                                 if (mActionModeView.getParent() instanceof View) {
1043                                     ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
1044                                 }
1045                             }
1046 
1047                             @Override
1048                             public void onAnimationEnd(View view) {
1049                                 mActionModeView.setAlpha(1f);
1050                                 mFadeAnim.setListener(null);
1051                                 mFadeAnim = null;
1052                             }
1053                         });
1054                     } else {
1055                         mActionModeView.setAlpha(1f);
1056                         mActionModeView.setVisibility(View.VISIBLE);
1057                         mActionModeView.sendAccessibilityEvent(
1058                                 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1059                         if (mActionModeView.getParent() instanceof View) {
1060                             ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
1061                         }
1062                     }
1063 
1064                     if (mActionModePopup != null) {
1065                         mWindow.getDecorView().post(mShowActionModePopup);
1066                     }
1067                 } else {
1068                     mActionMode = null;
1069                 }
1070             }
1071         }
1072         if (mActionMode != null && mAppCompatCallback != null) {
1073             mAppCompatCallback.onSupportActionModeStarted(mActionMode);
1074         }
1075         return mActionMode;
1076     }
1077 
1078     final boolean shouldAnimateActionModeView() {
1079         // We only to animate the action mode in if the sub decor has already been laid out.
1080         // If it hasn't been laid out, it hasn't been drawn to screen yet.
1081         return mSubDecorInstalled && mSubDecor != null && ViewCompat.isLaidOut(mSubDecor);
1082     }
1083 
1084     @Override
1085     public void setHandleNativeActionModesEnabled(boolean enabled) {
1086         mHandleNativeActionModes = enabled;
1087     }
1088 
1089     @Override
1090     public boolean isHandleNativeActionModesEnabled() {
1091         return mHandleNativeActionModes;
1092     }
1093 
1094     void endOnGoingFadeAnimation() {
1095         if (mFadeAnim != null) {
1096             mFadeAnim.cancel();
1097         }
1098     }
1099 
1100     boolean onBackPressed() {
1101         // Back cancels action modes first.
1102         if (mActionMode != null) {
1103             mActionMode.finish();
1104             return true;
1105         }
1106 
1107         // Next collapse any expanded action views.
1108         ActionBar ab = getSupportActionBar();
1109         if (ab != null && ab.collapseActionView()) {
1110             return true;
1111         }
1112 
1113         // Let the call through...
1114         return false;
1115     }
1116 
1117     boolean onKeyShortcut(int keyCode, KeyEvent ev) {
1118         // Let the Action Bar have a chance at handling the shortcut
1119         ActionBar ab = getSupportActionBar();
1120         if (ab != null && ab.onKeyShortcut(keyCode, ev)) {
1121             return true;
1122         }
1123 
1124         // If the panel is already prepared, then perform the shortcut using it.
1125         boolean handled;
1126         if (mPreparedPanel != null) {
1127             handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev,
1128                     Menu.FLAG_PERFORM_NO_CLOSE);
1129             if (handled) {
1130                 if (mPreparedPanel != null) {
1131                     mPreparedPanel.isHandled = true;
1132                 }
1133                 return true;
1134             }
1135         }
1136 
1137         // If the panel is not prepared, then we may be trying to handle a shortcut key
1138         // combination such as Control+C.  Temporarily prepare the panel then mark it
1139         // unprepared again when finished to ensure that the panel will again be prepared
1140         // the next time it is shown for real.
1141         if (mPreparedPanel == null) {
1142             PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1143             preparePanel(st, ev);
1144             handled = performPanelShortcut(st, ev.getKeyCode(), ev, Menu.FLAG_PERFORM_NO_CLOSE);
1145             st.isPrepared = false;
1146             if (handled) {
1147                 return true;
1148             }
1149         }
1150         return false;
1151     }
1152 
1153     boolean dispatchKeyEvent(KeyEvent event) {
1154         if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) {
1155             // If this is a MENU event, let the Activity have a go.
1156             if (mOriginalWindowCallback.dispatchKeyEvent(event)) {
1157                 return true;
1158             }
1159         }
1160 
1161         final int keyCode = event.getKeyCode();
1162         final int action = event.getAction();
1163         final boolean isDown = action == KeyEvent.ACTION_DOWN;
1164 
1165         return isDown ? onKeyDown(keyCode, event) : onKeyUp(keyCode, event);
1166     }
1167 
1168     boolean onKeyUp(int keyCode, KeyEvent event) {
1169         switch (keyCode) {
1170             case KeyEvent.KEYCODE_MENU:
1171                 onKeyUpPanel(Window.FEATURE_OPTIONS_PANEL, event);
1172                 return true;
1173             case KeyEvent.KEYCODE_BACK:
1174                 final boolean wasLongPressBackDown = mLongPressBackDown;
1175                 mLongPressBackDown = false;
1176 
1177                 PanelFeatureState st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1178                 if (st != null && st.isOpen) {
1179                     if (!wasLongPressBackDown) {
1180                         // Certain devices allow opening the options menu via a long press of the
1181                         // back button. We should only close the open options menu if it wasn't
1182                         // opened via a long press gesture.
1183                         closePanel(st, true);
1184                     }
1185                     return true;
1186                 }
1187                 if (onBackPressed()) {
1188                     return true;
1189                 }
1190                 break;
1191         }
1192         return false;
1193     }
1194 
1195     boolean onKeyDown(int keyCode, KeyEvent event) {
1196         switch (keyCode) {
1197             case KeyEvent.KEYCODE_MENU:
1198                 onKeyDownPanel(Window.FEATURE_OPTIONS_PANEL, event);
1199                 // We need to return true here and not let it bubble up to the Window.
1200                 // For empty menus, PhoneWindow's KEYCODE_BACK handling will steals all events,
1201                 // not allowing the Activity to call onBackPressed().
1202                 return true;
1203             case KeyEvent.KEYCODE_BACK:
1204                 // Certain devices allow opening the options menu via a long press of the back
1205                 // button. We keep a record of whether the last event is from a long press.
1206                 mLongPressBackDown = (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
1207                 break;
1208         }
1209         return false;
1210     }
1211 
1212     @Override
1213     public View createView(View parent, final String name, @NonNull Context context,
1214             @NonNull AttributeSet attrs) {
1215         if (mAppCompatViewInflater == null) {
1216             TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
1217             String viewInflaterClassName =
1218                     a.getString(R.styleable.AppCompatTheme_viewInflaterClass);
1219             if ((viewInflaterClassName == null)
1220                     || AppCompatViewInflater.class.getName().equals(viewInflaterClassName)) {
1221                 // Either default class name or set explicitly to null. In both cases
1222                 // create the base inflater (no reflection)
1223                 mAppCompatViewInflater = new AppCompatViewInflater();
1224             } else {
1225                 try {
1226                     Class viewInflaterClass = Class.forName(viewInflaterClassName);
1227                     mAppCompatViewInflater =
1228                             (AppCompatViewInflater) viewInflaterClass.getDeclaredConstructor()
1229                                     .newInstance();
1230                 } catch (Throwable t) {
1231                     Log.i(TAG, "Failed to instantiate custom view inflater "
1232                             + viewInflaterClassName + ". Falling back to default.", t);
1233                     mAppCompatViewInflater = new AppCompatViewInflater();
1234                 }
1235             }
1236         }
1237 
1238         boolean inheritContext = false;
1239         if (IS_PRE_LOLLIPOP) {
1240             inheritContext = (attrs instanceof XmlPullParser)
1241                     // If we have a XmlPullParser, we can detect where we are in the layout
1242                     ? ((XmlPullParser) attrs).getDepth() > 1
1243                     // Otherwise we have to use the old heuristic
1244                     : shouldInheritContext((ViewParent) parent);
1245         }
1246 
1247         return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
1248                 IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
1249                 true, /* Read read app:theme as a fallback at all times for legacy reasons */
1250                 VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
1251         );
1252     }
1253 
1254     private boolean shouldInheritContext(ViewParent parent) {
1255         if (parent == null) {
1256             // The initial parent is null so just return false
1257             return false;
1258         }
1259         final View windowDecor = mWindow.getDecorView();
1260         while (true) {
1261             if (parent == null) {
1262                 // Bingo. We've hit a view which has a null parent before being terminated from
1263                 // the loop. This is (most probably) because it's the root view in an inflation
1264                 // call, therefore we should inherit. This works as the inflated layout is only
1265                 // added to the hierarchy at the end of the inflate() call.
1266                 return true;
1267             } else if (parent == windowDecor || !(parent instanceof View)
1268                     || ViewCompat.isAttachedToWindow((View) parent)) {
1269                 // We have either hit the window's decor view, a parent which isn't a View
1270                 // (i.e. ViewRootImpl), or an attached view, so we know that the original parent
1271                 // is currently added to the view hierarchy. This means that it has not be
1272                 // inflated in the current inflate() call and we should not inherit the context.
1273                 return false;
1274             }
1275             parent = parent.getParent();
1276         }
1277     }
1278 
1279     @Override
1280     public void installViewFactory() {
1281         LayoutInflater layoutInflater = LayoutInflater.from(mContext);
1282         if (layoutInflater.getFactory() == null) {
1283             LayoutInflaterCompat.setFactory2(layoutInflater, this);
1284         } else {
1285             if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {
1286                 Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
1287                         + " so we can not install AppCompat's");
1288             }
1289         }
1290     }
1291 
1292     /**
1293      * From {@link LayoutInflater.Factory2}.
1294      */
1295     @Override
1296     public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
1297         return createView(parent, name, context, attrs);
1298     }
1299 
1300     /**
1301      * From {@link LayoutInflater.Factory2}.
1302      */
1303     @Override
1304     public View onCreateView(String name, Context context, AttributeSet attrs) {
1305         return onCreateView(null, name, context, attrs);
1306     }
1307 
1308     private void openPanel(final PanelFeatureState st, KeyEvent event) {
1309         // Already open, return
1310         if (st.isOpen || mIsDestroyed) {
1311             return;
1312         }
1313 
1314         // Don't open an options panel on xlarge devices.
1315         // (The app should be using an action bar for menu items.)
1316         if (st.featureId == FEATURE_OPTIONS_PANEL) {
1317             Configuration config = mContext.getResources().getConfiguration();
1318             boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
1319                     == Configuration.SCREENLAYOUT_SIZE_XLARGE;
1320             if (isXLarge) {
1321                 return;
1322             }
1323         }
1324 
1325         Window.Callback cb = getWindowCallback();
1326         if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) {
1327             // Callback doesn't want the menu to open, reset any state
1328             closePanel(st, true);
1329             return;
1330         }
1331 
1332         final WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
1333         if (wm == null) {
1334             return;
1335         }
1336 
1337         // Prepare panel (should have been done before, but just in case)
1338         if (!preparePanel(st, event)) {
1339             return;
1340         }
1341 
1342         int width = WRAP_CONTENT;
1343         if (st.decorView == null || st.refreshDecorView) {
1344             if (st.decorView == null) {
1345                 // Initialize the panel decor, this will populate st.decorView
1346                 if (!initializePanelDecor(st) || (st.decorView == null))
1347                     return;
1348             } else if (st.refreshDecorView && (st.decorView.getChildCount() > 0)) {
1349                 // Decor needs refreshing, so remove its views
1350                 st.decorView.removeAllViews();
1351             }
1352 
1353             // This will populate st.shownPanelView
1354             if (!initializePanelContent(st) || !st.hasPanelItems()) {
1355                 return;
1356             }
1357 
1358             ViewGroup.LayoutParams lp = st.shownPanelView.getLayoutParams();
1359             if (lp == null) {
1360                 lp = new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
1361             }
1362 
1363             int backgroundResId = st.background;
1364             st.decorView.setBackgroundResource(backgroundResId);
1365 
1366             ViewParent shownPanelParent = st.shownPanelView.getParent();
1367             if (shownPanelParent != null && shownPanelParent instanceof ViewGroup) {
1368                 ((ViewGroup) shownPanelParent).removeView(st.shownPanelView);
1369             }
1370             st.decorView.addView(st.shownPanelView, lp);
1371 
1372             /*
1373              * Give focus to the view, if it or one of its children does not
1374              * already have it.
1375              */
1376             if (!st.shownPanelView.hasFocus()) {
1377                 st.shownPanelView.requestFocus();
1378             }
1379         } else if (st.createdPanelView != null) {
1380             // If we already had a panel view, carry width=MATCH_PARENT through
1381             // as we did above when it was created.
1382             ViewGroup.LayoutParams lp = st.createdPanelView.getLayoutParams();
1383             if (lp != null && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
1384                 width = MATCH_PARENT;
1385             }
1386         }
1387 
1388         st.isHandled = false;
1389 
1390         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1391                 width, WRAP_CONTENT,
1392                 st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
1393                 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1394                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1395                 PixelFormat.TRANSLUCENT);
1396 
1397         lp.gravity = st.gravity;
1398         lp.windowAnimations = st.windowAnimations;
1399 
1400         wm.addView(st.decorView, lp);
1401         st.isOpen = true;
1402     }
1403 
1404     private boolean initializePanelDecor(PanelFeatureState st) {
1405         st.setStyle(getActionBarThemedContext());
1406         st.decorView = new ListMenuDecorView(st.listPresenterContext);
1407         st.gravity = Gravity.CENTER | Gravity.BOTTOM;
1408         return true;
1409     }
1410 
1411     private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
1412         if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu()
1413                 && (!ViewConfiguration.get(mContext).hasPermanentMenuKey()
1414                         || mDecorContentParent.isOverflowMenuShowPending())) {
1415 
1416             final Window.Callback cb = getWindowCallback();
1417 
1418             if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {
1419                 if (cb != null && !mIsDestroyed) {
1420                     // If we have a menu invalidation pending, do it now.
1421                     if (mInvalidatePanelMenuPosted &&
1422                             (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) {
1423                         mWindow.getDecorView().removeCallbacks(mInvalidatePanelMenuRunnable);
1424                         mInvalidatePanelMenuRunnable.run();
1425                     }
1426 
1427                     final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1428 
1429                     // If we don't have a menu or we're waiting for a full content refresh,
1430                     // forget it. This is a lingering event that no longer matters.
1431                     if (st.menu != null && !st.refreshMenuContent &&
1432                             cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
1433                         cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, st.menu);
1434                         mDecorContentParent.showOverflowMenu();
1435                     }
1436                 }
1437             } else {
1438                 mDecorContentParent.hideOverflowMenu();
1439                 if (!mIsDestroyed) {
1440                     final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1441                     cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, st.menu);
1442                 }
1443             }
1444             return;
1445         }
1446 
1447         PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1448 
1449         st.refreshDecorView = true;
1450         closePanel(st, false);
1451 
1452         openPanel(st, null);
1453     }
1454 
1455     private boolean initializePanelMenu(final PanelFeatureState st) {
1456         Context context = mContext;
1457 
1458         // If we have an action bar, initialize the menu with the right theme.
1459         if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_SUPPORT_ACTION_BAR) &&
1460                 mDecorContentParent != null) {
1461             final TypedValue outValue = new TypedValue();
1462             final Resources.Theme baseTheme = context.getTheme();
1463             baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1464 
1465             Resources.Theme widgetTheme = null;
1466             if (outValue.resourceId != 0) {
1467                 widgetTheme = context.getResources().newTheme();
1468                 widgetTheme.setTo(baseTheme);
1469                 widgetTheme.applyStyle(outValue.resourceId, true);
1470                 widgetTheme.resolveAttribute(
1471                         R.attr.actionBarWidgetTheme, outValue, true);
1472             } else {
1473                 baseTheme.resolveAttribute(
1474                         R.attr.actionBarWidgetTheme, outValue, true);
1475             }
1476 
1477             if (outValue.resourceId != 0) {
1478                 if (widgetTheme == null) {
1479                     widgetTheme = context.getResources().newTheme();
1480                     widgetTheme.setTo(baseTheme);
1481                 }
1482                 widgetTheme.applyStyle(outValue.resourceId, true);
1483             }
1484 
1485             if (widgetTheme != null) {
1486                 context = new ContextThemeWrapper(context, 0);
1487                 context.getTheme().setTo(widgetTheme);
1488             }
1489         }
1490 
1491         final MenuBuilder menu = new MenuBuilder(context);
1492         menu.setCallback(this);
1493         st.setMenu(menu);
1494 
1495         return true;
1496     }
1497 
1498     private boolean initializePanelContent(PanelFeatureState st) {
1499         if (st.createdPanelView != null) {
1500             st.shownPanelView = st.createdPanelView;
1501             return true;
1502         }
1503 
1504         if (st.menu == null) {
1505             return false;
1506         }
1507 
1508         if (mPanelMenuPresenterCallback == null) {
1509             mPanelMenuPresenterCallback = new PanelMenuPresenterCallback();
1510         }
1511 
1512         MenuView menuView = st.getListMenuView(mPanelMenuPresenterCallback);
1513 
1514         st.shownPanelView = (View) menuView;
1515 
1516         return st.shownPanelView != null;
1517     }
1518 
1519     private boolean preparePanel(PanelFeatureState st, KeyEvent event) {
1520         if (mIsDestroyed) {
1521             return false;
1522         }
1523 
1524         // Already prepared (isPrepared will be reset to false later)
1525         if (st.isPrepared) {
1526             return true;
1527         }
1528 
1529         if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
1530             // Another Panel is prepared and possibly open, so close it
1531             closePanel(mPreparedPanel, false);
1532         }
1533 
1534         final Window.Callback cb = getWindowCallback();
1535 
1536         if (cb != null) {
1537             st.createdPanelView = cb.onCreatePanelView(st.featureId);
1538         }
1539 
1540         final boolean isActionBarMenu =
1541                 (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_SUPPORT_ACTION_BAR);
1542 
1543         if (isActionBarMenu && mDecorContentParent != null) {
1544             // Enforce ordering guarantees around events so that the action bar never
1545             // dispatches menu-related events before the panel is prepared.
1546             mDecorContentParent.setMenuPrepared();
1547         }
1548 
1549         if (st.createdPanelView == null &&
1550                 (!isActionBarMenu || !(peekSupportActionBar() instanceof ToolbarActionBar))) {
1551             // Since ToolbarActionBar handles the list options menu itself, we only want to
1552             // init this menu panel if we're not using a TAB.
1553             if (st.menu == null || st.refreshMenuContent) {
1554                 if (st.menu == null) {
1555                     if (!initializePanelMenu(st) || (st.menu == null)) {
1556                         return false;
1557                     }
1558                 }
1559 
1560                 if (isActionBarMenu && mDecorContentParent != null) {
1561                     if (mActionMenuPresenterCallback == null) {
1562                         mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
1563                     }
1564                     mDecorContentParent.setMenu(st.menu, mActionMenuPresenterCallback);
1565                 }
1566 
1567                 // Creating the panel menu will involve a lot of manipulation;
1568                 // don't dispatch change events to presenters until we're done.
1569                 st.menu.stopDispatchingItemsChanged();
1570                 if (!cb.onCreatePanelMenu(st.featureId, st.menu)) {
1571                     // Ditch the menu created above
1572                     st.setMenu(null);
1573 
1574                     if (isActionBarMenu && mDecorContentParent != null) {
1575                         // Don't show it in the action bar either
1576                         mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
1577                     }
1578 
1579                     return false;
1580                 }
1581 
1582                 st.refreshMenuContent = false;
1583             }
1584 
1585             // Preparing the panel menu can involve a lot of manipulation;
1586             // don't dispatch change events to presenters until we're done.
1587             st.menu.stopDispatchingItemsChanged();
1588 
1589             // Restore action view state before we prepare. This gives apps
1590             // an opportunity to override frozen/restored state in onPrepare.
1591             if (st.frozenActionViewState != null) {
1592                 st.menu.restoreActionViewStates(st.frozenActionViewState);
1593                 st.frozenActionViewState = null;
1594             }
1595 
1596             // Callback and return if the callback does not want to show the menu
1597             if (!cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
1598                 if (isActionBarMenu && mDecorContentParent != null) {
1599                     // The app didn't want to show the menu for now but it still exists.
1600                     // Clear it out of the action bar.
1601                     mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
1602                 }
1603                 st.menu.startDispatchingItemsChanged();
1604                 return false;
1605             }
1606 
1607             // Set the proper keymap
1608             KeyCharacterMap kmap = KeyCharacterMap.load(
1609                     event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
1610             st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;
1611             st.menu.setQwertyMode(st.qwertyMode);
1612             st.menu.startDispatchingItemsChanged();
1613         }
1614 
1615         // Set other state
1616         st.isPrepared = true;
1617         st.isHandled = false;
1618         mPreparedPanel = st;
1619 
1620         return true;
1621     }
1622 
1623     void checkCloseActionMenu(MenuBuilder menu) {
1624         if (mClosingActionMenu) {
1625             return;
1626         }
1627 
1628         mClosingActionMenu = true;
1629         mDecorContentParent.dismissPopups();
1630         Window.Callback cb = getWindowCallback();
1631         if (cb != null && !mIsDestroyed) {
1632             cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, menu);
1633         }
1634         mClosingActionMenu = false;
1635     }
1636 
1637     void closePanel(int featureId) {
1638         closePanel(getPanelState(featureId, true), true);
1639     }
1640 
1641     void closePanel(PanelFeatureState st, boolean doCallback) {
1642         if (doCallback && st.featureId == FEATURE_OPTIONS_PANEL &&
1643                 mDecorContentParent != null && mDecorContentParent.isOverflowMenuShowing()) {
1644             checkCloseActionMenu(st.menu);
1645             return;
1646         }
1647 
1648         final WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
1649         if (wm != null && st.isOpen && st.decorView != null) {
1650             wm.removeView(st.decorView);
1651 
1652             if (doCallback) {
1653                 callOnPanelClosed(st.featureId, st, null);
1654             }
1655         }
1656 
1657         st.isPrepared = false;
1658         st.isHandled = false;
1659         st.isOpen = false;
1660 
1661         // This view is no longer shown, so null it out
1662         st.shownPanelView = null;
1663 
1664         // Next time the menu opens, it should not be in expanded mode, so
1665         // force a refresh of the decor
1666         st.refreshDecorView = true;
1667 
1668         if (mPreparedPanel == st) {
1669             mPreparedPanel = null;
1670         }
1671     }
1672 
1673     private boolean onKeyDownPanel(int featureId, KeyEvent event) {
1674         if (event.getRepeatCount() == 0) {
1675             PanelFeatureState st = getPanelState(featureId, true);
1676             if (!st.isOpen) {
1677                 return preparePanel(st, event);
1678             }
1679         }
1680 
1681         return false;
1682     }
1683 
1684     private boolean onKeyUpPanel(int featureId, KeyEvent event) {
1685         if (mActionMode != null) {
1686             return false;
1687         }
1688 
1689         boolean handled = false;
1690         final PanelFeatureState st = getPanelState(featureId, true);
1691         if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
1692                 mDecorContentParent.canShowOverflowMenu() &&
1693                 !ViewConfiguration.get(mContext).hasPermanentMenuKey()) {
1694             if (!mDecorContentParent.isOverflowMenuShowing()) {
1695                 if (!mIsDestroyed && preparePanel(st, event)) {
1696                     handled = mDecorContentParent.showOverflowMenu();
1697                 }
1698             } else {
1699                 handled = mDecorContentParent.hideOverflowMenu();
1700             }
1701         } else {
1702             if (st.isOpen || st.isHandled) {
1703                 // Play the sound effect if the user closed an open menu (and not if
1704                 // they just released a menu shortcut)
1705                 handled = st.isOpen;
1706                 // Close menu
1707                 closePanel(st, true);
1708             } else if (st.isPrepared) {
1709                 boolean show = true;
1710                 if (st.refreshMenuContent) {
1711                     // Something may have invalidated the menu since we prepared it.
1712                     // Re-prepare it to refresh.
1713                     st.isPrepared = false;
1714                     show = preparePanel(st, event);
1715                 }
1716 
1717                 if (show) {
1718                     // Show menu
1719                     openPanel(st, event);
1720                     handled = true;
1721                 }
1722             }
1723         }
1724 
1725         if (handled) {
1726             AudioManager audioManager = (AudioManager) mContext.getSystemService(
1727                     Context.AUDIO_SERVICE);
1728             if (audioManager != null) {
1729                 audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
1730             } else {
1731                 Log.w(TAG, "Couldn't get audio manager");
1732             }
1733         }
1734         return handled;
1735     }
1736 
1737     void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) {
1738         // Try to get a menu
1739         if (menu == null) {
1740             // Need a panel to grab the menu, so try to get that
1741             if (panel == null) {
1742                 if ((featureId >= 0) && (featureId < mPanels.length)) {
1743                     panel = mPanels[featureId];
1744                 }
1745             }
1746 
1747             if (panel != null) {
1748                 // menu still may be null, which is okay--we tried our best
1749                 menu = panel.menu;
1750             }
1751         }
1752 
1753         // If the panel is not open, do not callback
1754         if ((panel != null) && (!panel.isOpen))
1755             return;
1756 
1757         if (!mIsDestroyed) {
1758             // We need to be careful which callback we dispatch the call to. We can not dispatch
1759             // this to the Window's callback since that will call back into this method and cause a
1760             // crash. Instead we need to dispatch down to the original Activity/Dialog/etc.
1761             mOriginalWindowCallback.onPanelClosed(featureId, menu);
1762         }
1763     }
1764 
1765     PanelFeatureState findMenuPanel(Menu menu) {
1766         final PanelFeatureState[] panels = mPanels;
1767         final int N = panels != null ? panels.length : 0;
1768         for (int i = 0; i < N; i++) {
1769             final PanelFeatureState panel = panels[i];
1770             if (panel != null && panel.menu == menu) {
1771                 return panel;
1772             }
1773         }
1774         return null;
1775     }
1776 
1777     protected PanelFeatureState getPanelState(int featureId, boolean required) {
1778         PanelFeatureState[] ar;
1779         if ((ar = mPanels) == null || ar.length <= featureId) {
1780             PanelFeatureState[] nar = new PanelFeatureState[featureId + 1];
1781             if (ar != null) {
1782                 System.arraycopy(ar, 0, nar, 0, ar.length);
1783             }
1784             mPanels = ar = nar;
1785         }
1786 
1787         PanelFeatureState st = ar[featureId];
1788         if (st == null) {
1789             ar[featureId] = st = new PanelFeatureState(featureId);
1790         }
1791         return st;
1792     }
1793 
1794     private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
1795             int flags) {
1796         if (event.isSystem()) {
1797             return false;
1798         }
1799 
1800         boolean handled = false;
1801 
1802         // Only try to perform menu shortcuts if preparePanel returned true (possible false
1803         // return value from application not wanting to show the menu).
1804         if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) {
1805             // The menu is prepared now, perform the shortcut on it
1806             handled = st.menu.performShortcut(keyCode, event, flags);
1807         }
1808 
1809         if (handled) {
1810             // Only close down the menu if we don't have an action bar keeping it open.
1811             if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) {
1812                 closePanel(st, true);
1813             }
1814         }
1815 
1816         return handled;
1817     }
1818 
1819     private void invalidatePanelMenu(int featureId) {
1820         mInvalidatePanelMenuFeatures |= 1 << featureId;
1821 
1822         if (!mInvalidatePanelMenuPosted) {
1823             ViewCompat.postOnAnimation(mWindow.getDecorView(), mInvalidatePanelMenuRunnable);
1824             mInvalidatePanelMenuPosted = true;
1825         }
1826     }
1827 
1828     void doInvalidatePanelMenu(int featureId) {
1829         PanelFeatureState st = getPanelState(featureId, true);
1830         Bundle savedActionViewStates = null;
1831         if (st.menu != null) {
1832             savedActionViewStates = new Bundle();
1833             st.menu.saveActionViewStates(savedActionViewStates);
1834             if (savedActionViewStates.size() > 0) {
1835                 st.frozenActionViewState = savedActionViewStates;
1836             }
1837             // This will be started again when the panel is prepared.
1838             st.menu.stopDispatchingItemsChanged();
1839             st.menu.clear();
1840         }
1841         st.refreshMenuContent = true;
1842         st.refreshDecorView = true;
1843 
1844         // Prepare the options panel if we have an action bar
1845         if ((featureId == FEATURE_SUPPORT_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)
1846                 && mDecorContentParent != null) {
1847             st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1848             if (st != null) {
1849                 st.isPrepared = false;
1850                 preparePanel(st, null);
1851             }
1852         }
1853     }
1854 
1855     /**
1856      * Updates the status bar guard
1857      *
1858      * @param insetTop the current top system window inset
1859      * @return the new top system window inset
1860      */
1861     int updateStatusGuard(int insetTop) {
1862         boolean showStatusGuard = false;
1863         // Show the status guard when the non-overlay contextual action bar is showing
1864         if (mActionModeView != null) {
1865             if (mActionModeView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
1866                 ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)
1867                         mActionModeView.getLayoutParams();
1868                 boolean mlpChanged = false;
1869 
1870                 if (mActionModeView.isShown()) {
1871                     if (mTempRect1 == null) {
1872                         mTempRect1 = new Rect();
1873                         mTempRect2 = new Rect();
1874                     }
1875                     final Rect insets = mTempRect1;
1876                     final Rect localInsets = mTempRect2;
1877                     insets.set(0, insetTop, 0, 0);
1878 
1879                     ViewUtils.computeFitSystemWindows(mSubDecor, insets, localInsets);
1880                     final int newMargin = localInsets.top == 0 ? insetTop : 0;
1881                     if (mlp.topMargin != newMargin) {
1882                         mlpChanged = true;
1883                         mlp.topMargin = insetTop;
1884 
1885                         if (mStatusGuard == null) {
1886                             mStatusGuard = new View(mContext);
1887                             mStatusGuard.setBackgroundColor(mContext.getResources()
1888                                     .getColor(R.color.abc_input_method_navigation_guard));
1889                             mSubDecor.addView(mStatusGuard, -1,
1890                                     new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1891                                             insetTop));
1892                         } else {
1893                             ViewGroup.LayoutParams lp = mStatusGuard.getLayoutParams();
1894                             if (lp.height != insetTop) {
1895                                 lp.height = insetTop;
1896                                 mStatusGuard.setLayoutParams(lp);
1897                             }
1898                         }
1899                     }
1900 
1901                     // The action mode's theme may differ from the app, so
1902                     // always show the status guard above it.
1903                     showStatusGuard = mStatusGuard != null;
1904 
1905                     // We only need to consume the insets if the action
1906                     // mode is overlaid on the app content (e.g. it's
1907                     // sitting in a FrameLayout, see
1908                     // screen_simple_overlay_action_mode.xml).
1909                     if (!mOverlayActionMode && showStatusGuard) {
1910                         insetTop = 0;
1911                     }
1912                 } else {
1913                     // reset top margin
1914                     if (mlp.topMargin != 0) {
1915                         mlpChanged = true;
1916                         mlp.topMargin = 0;
1917                     }
1918                 }
1919                 if (mlpChanged) {
1920                     mActionModeView.setLayoutParams(mlp);
1921                 }
1922             }
1923         }
1924         if (mStatusGuard != null) {
1925             mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
1926         }
1927 
1928         return insetTop;
1929     }
1930 
1931     private void throwFeatureRequestIfSubDecorInstalled() {
1932         if (mSubDecorInstalled) {
1933             throw new AndroidRuntimeException(
1934                     "Window feature must be requested before adding content");
1935         }
1936     }
1937 
1938     private int sanitizeWindowFeatureId(int featureId) {
1939         if (featureId == WindowCompat.FEATURE_ACTION_BAR) {
1940             Log.i(TAG, "You should now use the AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR"
1941                     + " id when requesting this feature.");
1942             return FEATURE_SUPPORT_ACTION_BAR;
1943         } else if (featureId == WindowCompat.FEATURE_ACTION_BAR_OVERLAY) {
1944             Log.i(TAG, "You should now use the AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR_OVERLAY"
1945                     + " id when requesting this feature.");
1946             return FEATURE_SUPPORT_ACTION_BAR_OVERLAY;
1947         }
1948         // Else we'll just return the original id
1949         return featureId;
1950     }
1951 
1952     ViewGroup getSubDecor() {
1953         return mSubDecor;
1954     }
1955 
1956     void dismissPopups() {
1957         if (mDecorContentParent != null) {
1958             mDecorContentParent.dismissPopups();
1959         }
1960 
1961         if (mActionModePopup != null) {
1962             mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
1963             if (mActionModePopup.isShowing()) {
1964                 try {
1965                     mActionModePopup.dismiss();
1966                 } catch (IllegalArgumentException e) {
1967                     // Pre-v18, there are times when the Window will remove the popup before us.
1968                     // In these cases we need to swallow the resulting exception.
1969                 }
1970             }
1971             mActionModePopup = null;
1972         }
1973         endOnGoingFadeAnimation();
1974 
1975         PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
1976         if (st != null && st.menu != null) {
1977             st.menu.close();
1978         }
1979     }
1980 
1981     @Override
1982     public boolean applyDayNight() {
1983         boolean applied = false;
1984 
1985         @NightMode final int nightMode = getNightMode();
1986         @ApplyableNightMode final int modeToApply = mapNightMode(nightMode);
1987         if (modeToApply != MODE_NIGHT_FOLLOW_SYSTEM) {
1988             applied = updateForNightMode(modeToApply);
1989         }
1990 
1991         if (nightMode == MODE_NIGHT_AUTO) {
1992             // If we're already been started, we may need to setup auto mode again
1993             ensureAutoNightModeManager();
1994             mAutoNightModeManager.setup();
1995         }
1996 
1997         mApplyDayNightCalled = true;
1998         return applied;
1999     }
2000 
2001     @Override
2002     public void setLocalNightMode(@NightMode final int mode) {
2003         switch (mode) {
2004             case MODE_NIGHT_AUTO:
2005             case MODE_NIGHT_NO:
2006             case MODE_NIGHT_YES:
2007             case MODE_NIGHT_FOLLOW_SYSTEM:
2008                 if (mLocalNightMode != mode) {
2009                     mLocalNightMode = mode;
2010                     if (mApplyDayNightCalled) {
2011                         // If we've already applied day night, re-apply since we won't be
2012                         // called again
2013                         applyDayNight();
2014                     }
2015                 }
2016                 break;
2017             default:
2018                 Log.i(TAG, "setLocalNightMode() called with an unknown mode");
2019                 break;
2020         }
2021     }
2022 
2023     @ApplyableNightMode
2024     int mapNightMode(@NightMode final int mode) {
2025         switch (mode) {
2026             case MODE_NIGHT_AUTO:
2027                 if (Build.VERSION.SDK_INT >= 23) {
2028                     UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
2029                     if (uiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO) {
2030                         // If we're set to AUTO and the system's auto night mode is already enabled,
2031                         // we'll just let the system handle it by returning FOLLOW_SYSTEM
2032                         return MODE_NIGHT_FOLLOW_SYSTEM;
2033                     }
2034                 }
2035                 ensureAutoNightModeManager();
2036                 return mAutoNightModeManager.getApplyableNightMode();
2037             case MODE_NIGHT_UNSPECIFIED:
2038                 // If we don't have a mode specified, just let the system handle it
2039                 return MODE_NIGHT_FOLLOW_SYSTEM;
2040             default:
2041                 return mode;
2042         }
2043     }
2044 
2045     @NightMode
2046     private int getNightMode() {
2047         return mLocalNightMode != MODE_NIGHT_UNSPECIFIED ? mLocalNightMode : getDefaultNightMode();
2048     }
2049 
2050     /**
2051      * Updates the {@link Resources} configuration {@code uiMode} with the
2052      * chosen {@code UI_MODE_NIGHT} value.
2053      */
2054     private boolean updateForNightMode(@ApplyableNightMode final int mode) {
2055         final Resources res = mContext.getResources();
2056         final Configuration conf = res.getConfiguration();
2057         final int currentNightMode = conf.uiMode & Configuration.UI_MODE_NIGHT_MASK;
2058 
2059         final int newNightMode = (mode == MODE_NIGHT_YES)
2060                 ? Configuration.UI_MODE_NIGHT_YES
2061                 : Configuration.UI_MODE_NIGHT_NO;
2062 
2063         if (currentNightMode != newNightMode) {
2064             if (shouldRecreateOnNightModeChange()) {
2065                 if (DEBUG) {
2066                     Log.d(TAG, "applyNightMode() | Night mode changed, recreating Activity");
2067                 }
2068                 // If we've already been created, we need to recreate the Activity for the
2069                 // mode to be applied
2070                 final Activity activity = (Activity) mContext;
2071                 activity.recreate();
2072             } else {
2073                 if (DEBUG) {
2074                     Log.d(TAG, "applyNightMode() | Night mode changed, updating configuration");
2075                 }
2076                 final Configuration config = new Configuration(conf);
2077                 final DisplayMetrics metrics = res.getDisplayMetrics();
2078 
2079                 // Update the UI Mode to reflect the new night mode
2080                 config.uiMode = newNightMode | (config.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
2081                 res.updateConfiguration(config, metrics);
2082 
2083                 // We may need to flush the Resources' drawable cache due to framework bugs.
2084                 if (!(Build.VERSION.SDK_INT >= 26)) {
2085                     ResourcesFlusher.flush(res);
2086                 }
2087             }
2088             return true;
2089         } else {
2090             if (DEBUG) {
2091                 Log.d(TAG, "applyNightMode() | Skipping. Night mode has not changed: " + mode);
2092             }
2093         }
2094         return false;
2095     }
2096 
2097     private void ensureAutoNightModeManager() {
2098         if (mAutoNightModeManager == null) {
2099             mAutoNightModeManager = new AutoNightModeManager(TwilightManager.getInstance(mContext));
2100         }
2101     }
2102 
2103     @VisibleForTesting
2104     final AutoNightModeManager getAutoNightModeManager() {
2105         ensureAutoNightModeManager();
2106         return mAutoNightModeManager;
2107     }
2108 
2109     private boolean shouldRecreateOnNightModeChange() {
2110         if (mApplyDayNightCalled && mContext instanceof Activity) {
2111             // If we've already applyDayNight() (via setTheme), we need to check if the
2112             // Activity has configChanges set to handle uiMode changes
2113             final PackageManager pm = mContext.getPackageManager();
2114             try {
2115                 final ActivityInfo info = pm.getActivityInfo(
2116                         new ComponentName(mContext, mContext.getClass()), 0);
2117                 // We should return true (to recreate) if configChanges does not want to
2118                 // handle uiMode
2119                 return (info.configChanges & ActivityInfo.CONFIG_UI_MODE) == 0;
2120             } catch (PackageManager.NameNotFoundException e) {
2121                 // This shouldn't happen but let's not crash because of it, we'll just log and
2122                 // return true (since most apps will do that anyway)
2123                 Log.d(TAG, "Exception while getting ActivityInfo", e);
2124                 return true;
2125             }
2126         }
2127         return false;
2128     }
2129 
2130     /**
2131      * Clears out internal reference when the action mode is destroyed.
2132      */
2133     class ActionModeCallbackWrapperV9 implements ActionMode.Callback {
2134         private ActionMode.Callback mWrapped;
2135 
2136         public ActionModeCallbackWrapperV9(ActionMode.Callback wrapped) {
2137             mWrapped = wrapped;
2138         }
2139 
2140         @Override
2141         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2142             return mWrapped.onCreateActionMode(mode, menu);
2143         }
2144 
2145         @Override
2146         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2147             return mWrapped.onPrepareActionMode(mode, menu);
2148         }
2149 
2150         @Override
2151         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2152             return mWrapped.onActionItemClicked(mode, item);
2153         }
2154 
2155         @Override
2156         public void onDestroyActionMode(ActionMode mode) {
2157             mWrapped.onDestroyActionMode(mode);
2158             if (mActionModePopup != null) {
2159                 mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
2160             }
2161 
2162             if (mActionModeView != null) {
2163                 endOnGoingFadeAnimation();
2164                 mFadeAnim = ViewCompat.animate(mActionModeView).alpha(0f);
2165                 mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
2166                     @Override
2167                     public void onAnimationEnd(View view) {
2168                         mActionModeView.setVisibility(View.GONE);
2169                         if (mActionModePopup != null) {
2170                             mActionModePopup.dismiss();
2171                         } else if (mActionModeView.getParent() instanceof View) {
2172                             ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
2173                         }
2174                         mActionModeView.removeAllViews();
2175                         mFadeAnim.setListener(null);
2176                         mFadeAnim = null;
2177                     }
2178                 });
2179             }
2180             if (mAppCompatCallback != null) {
2181                 mAppCompatCallback.onSupportActionModeFinished(mActionMode);
2182             }
2183             mActionMode = null;
2184         }
2185     }
2186 
2187     private final class PanelMenuPresenterCallback implements MenuPresenter.Callback {
2188         PanelMenuPresenterCallback() {
2189         }
2190 
2191         @Override
2192         public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
2193             final Menu parentMenu = menu.getRootMenu();
2194             final boolean isSubMenu = parentMenu != menu;
2195             final PanelFeatureState panel = findMenuPanel(isSubMenu ? parentMenu : menu);
2196             if (panel != null) {
2197                 if (isSubMenu) {
2198                     callOnPanelClosed(panel.featureId, panel, parentMenu);
2199                     closePanel(panel, true);
2200                 } else {
2201                     // Close the panel and only do the callback if the menu is being
2202                     // closed completely, not if opening a sub menu
2203                     closePanel(panel, allMenusAreClosing);
2204                 }
2205             }
2206         }
2207 
2208         @Override
2209         public boolean onOpenSubMenu(MenuBuilder subMenu) {
2210             if (subMenu == null && mHasActionBar) {
2211                 Window.Callback cb = getWindowCallback();
2212                 if (cb != null && !mIsDestroyed) {
2213                     cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, subMenu);
2214                 }
2215             }
2216             return true;
2217         }
2218     }
2219 
2220     private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
2221         ActionMenuPresenterCallback() {
2222         }
2223 
2224         @Override
2225         public boolean onOpenSubMenu(MenuBuilder subMenu) {
2226             Window.Callback cb = getWindowCallback();
2227             if (cb != null) {
2228                 cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, subMenu);
2229             }
2230             return true;
2231         }
2232 
2233         @Override
2234         public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
2235             checkCloseActionMenu(menu);
2236         }
2237     }
2238 
2239     protected static final class PanelFeatureState {
2240 
2241         /** Feature ID for this panel. */
2242         int featureId;
2243 
2244         int background;
2245 
2246         int gravity;
2247 
2248         int x;
2249 
2250         int y;
2251 
2252         int windowAnimations;
2253 
2254         /** Dynamic state of the panel. */
2255         ViewGroup decorView;
2256 
2257         /** The panel that we are actually showing. */
2258         View shownPanelView;
2259 
2260         /** The panel that was returned by onCreatePanelView(). */
2261         View createdPanelView;
2262 
2263         /** Use {@link #setMenu} to set this. */
2264         MenuBuilder menu;
2265 
2266         ListMenuPresenter listMenuPresenter;
2267 
2268         Context listPresenterContext;
2269 
2270         /**
2271          * Whether the panel has been prepared (see
2272          * {@link #preparePanel}).
2273          */
2274         boolean isPrepared;
2275 
2276         /**
2277          * Whether an item's action has been performed. This happens in obvious
2278          * scenarios (user clicks on menu item), but can also happen with
2279          * chording menu+(shortcut key).
2280          */
2281         boolean isHandled;
2282 
2283         boolean isOpen;
2284 
2285         public boolean qwertyMode;
2286 
2287         boolean refreshDecorView;
2288 
2289         boolean refreshMenuContent;
2290 
2291         boolean wasLastOpen;
2292 
2293         /**
2294          * Contains the state of the menu when told to freeze.
2295          */
2296         Bundle frozenMenuState;
2297 
2298         /**
2299          * Contains the state of associated action views when told to freeze.
2300          * These are saved across invalidations.
2301          */
2302         Bundle frozenActionViewState;
2303 
2304         PanelFeatureState(int featureId) {
2305             this.featureId = featureId;
2306 
2307             refreshDecorView = false;
2308         }
2309 
2310         public boolean hasPanelItems() {
2311             if (shownPanelView == null) return false;
2312             if (createdPanelView != null) return true;
2313 
2314             return listMenuPresenter.getAdapter().getCount() > 0;
2315         }
2316 
2317         /**
2318          * Unregister and free attached MenuPresenters. They will be recreated as needed.
2319          */
2320         public void clearMenuPresenters() {
2321             if (menu != null) {
2322                 menu.removeMenuPresenter(listMenuPresenter);
2323             }
2324             listMenuPresenter = null;
2325         }
2326 
2327         void setStyle(Context context) {
2328             final TypedValue outValue = new TypedValue();
2329             final Resources.Theme widgetTheme = context.getResources().newTheme();
2330             widgetTheme.setTo(context.getTheme());
2331 
2332             // First apply the actionBarPopupTheme
2333             widgetTheme.resolveAttribute(R.attr.actionBarPopupTheme, outValue, true);
2334             if (outValue.resourceId != 0) {
2335                 widgetTheme.applyStyle(outValue.resourceId, true);
2336             }
2337 
2338             // Now apply the panelMenuListTheme
2339             widgetTheme.resolveAttribute(R.attr.panelMenuListTheme, outValue, true);
2340             if (outValue.resourceId != 0) {
2341                 widgetTheme.applyStyle(outValue.resourceId, true);
2342             } else {
2343                 widgetTheme.applyStyle(R.style.Theme_AppCompat_CompactMenu, true);
2344             }
2345 
2346             context = new ContextThemeWrapper(context, 0);
2347             context.getTheme().setTo(widgetTheme);
2348 
2349             listPresenterContext = context;
2350 
2351             TypedArray a = context.obtainStyledAttributes(R.styleable.AppCompatTheme);
2352             background = a.getResourceId(
2353                     R.styleable.AppCompatTheme_panelBackground, 0);
2354             windowAnimations = a.getResourceId(
2355                     R.styleable.AppCompatTheme_android_windowAnimationStyle, 0);
2356             a.recycle();
2357         }
2358 
2359         void setMenu(MenuBuilder menu) {
2360             if (menu == this.menu) return;
2361 
2362             if (this.menu != null) {
2363                 this.menu.removeMenuPresenter(listMenuPresenter);
2364             }
2365             this.menu = menu;
2366             if (menu != null) {
2367                 if (listMenuPresenter != null) menu.addMenuPresenter(listMenuPresenter);
2368             }
2369         }
2370 
2371         MenuView getListMenuView(MenuPresenter.Callback cb) {
2372             if (menu == null) return null;
2373 
2374             if (listMenuPresenter == null) {
2375                 listMenuPresenter = new ListMenuPresenter(listPresenterContext,
2376                         R.layout.abc_list_menu_item_layout);
2377                 listMenuPresenter.setCallback(cb);
2378                 menu.addMenuPresenter(listMenuPresenter);
2379             }
2380 
2381             MenuView result = listMenuPresenter.getMenuView(decorView);
2382 
2383             return result;
2384         }
2385 
2386         Parcelable onSaveInstanceState() {
2387             SavedState savedState = new SavedState();
2388             savedState.featureId = featureId;
2389             savedState.isOpen = isOpen;
2390 
2391             if (menu != null) {
2392                 savedState.menuState = new Bundle();
2393                 menu.savePresenterStates(savedState.menuState);
2394             }
2395 
2396             return savedState;
2397         }
2398 
2399         void onRestoreInstanceState(Parcelable state) {
2400             SavedState savedState = (SavedState) state;
2401             featureId = savedState.featureId;
2402             wasLastOpen = savedState.isOpen;
2403             frozenMenuState = savedState.menuState;
2404 
2405             shownPanelView = null;
2406             decorView = null;
2407         }
2408 
2409         void applyFrozenState() {
2410             if (menu != null && frozenMenuState != null) {
2411                 menu.restorePresenterStates(frozenMenuState);
2412                 frozenMenuState = null;
2413             }
2414         }
2415 
2416         private static class SavedState implements Parcelable {
2417             int featureId;
2418             boolean isOpen;
2419             Bundle menuState;
2420 
2421             SavedState() {
2422             }
2423 
2424             @Override
2425             public int describeContents() {
2426                 return 0;
2427             }
2428 
2429             @Override
2430             public void writeToParcel(Parcel dest, int flags) {
2431                 dest.writeInt(featureId);
2432                 dest.writeInt(isOpen ? 1 : 0);
2433 
2434                 if (isOpen) {
2435                     dest.writeBundle(menuState);
2436                 }
2437             }
2438 
2439             static SavedState readFromParcel(Parcel source, ClassLoader loader) {
2440                 SavedState savedState = new SavedState();
2441                 savedState.featureId = source.readInt();
2442                 savedState.isOpen = source.readInt() == 1;
2443 
2444                 if (savedState.isOpen) {
2445                     savedState.menuState = source.readBundle(loader);
2446                 }
2447 
2448                 return savedState;
2449             }
2450 
2451             public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
2452                 @Override
2453                 public SavedState createFromParcel(Parcel in, ClassLoader loader) {
2454                     return readFromParcel(in, loader);
2455                 }
2456 
2457                 @Override
2458                 public SavedState createFromParcel(Parcel in) {
2459                     return readFromParcel(in, null);
2460                 }
2461 
2462                 @Override
2463                 public SavedState[] newArray(int size) {
2464                     return new SavedState[size];
2465                 }
2466             };
2467         }
2468     }
2469 
2470     private class ListMenuDecorView extends ContentFrameLayout {
2471         public ListMenuDecorView(Context context) {
2472             super(context);
2473         }
2474 
2475         @Override
2476         public boolean dispatchKeyEvent(KeyEvent event) {
2477             return AppCompatDelegateImpl.this.dispatchKeyEvent(event)
2478                     || super.dispatchKeyEvent(event);
2479         }
2480 
2481         @Override
2482         public boolean onInterceptTouchEvent(MotionEvent event) {
2483             int action = event.getAction();
2484             if (action == MotionEvent.ACTION_DOWN) {
2485                 int x = (int) event.getX();
2486                 int y = (int) event.getY();
2487                 if (isOutOfBounds(x, y)) {
2488                     closePanel(Window.FEATURE_OPTIONS_PANEL);
2489                     return true;
2490                 }
2491             }
2492             return super.onInterceptTouchEvent(event);
2493         }
2494 
2495         @Override
2496         public void setBackgroundResource(int resid) {
2497             setBackgroundDrawable(AppCompatResources.getDrawable(getContext(), resid));
2498         }
2499 
2500         private boolean isOutOfBounds(int x, int y) {
2501             return x < -5 || y < -5 || x > (getWidth() + 5) || y > (getHeight() + 5);
2502         }
2503     }
2504 
2505 
2506     class AppCompatWindowCallback extends WindowCallbackWrapper {
2507         AppCompatWindowCallback(Window.Callback callback) {
2508             super(callback);
2509         }
2510 
2511         @Override
2512         public boolean dispatchKeyEvent(KeyEvent event) {
2513             return AppCompatDelegateImpl.this.dispatchKeyEvent(event)
2514                     || super.dispatchKeyEvent(event);
2515         }
2516 
2517         @Override
2518         public boolean dispatchKeyShortcutEvent(KeyEvent event) {
2519             return super.dispatchKeyShortcutEvent(event)
2520                     || AppCompatDelegateImpl.this.onKeyShortcut(event.getKeyCode(), event);
2521         }
2522 
2523         @Override
2524         public boolean onCreatePanelMenu(int featureId, Menu menu) {
2525             if (featureId == Window.FEATURE_OPTIONS_PANEL && !(menu instanceof MenuBuilder)) {
2526                 // If this is an options menu but it's not an AppCompat menu, we eat the event
2527                 // and return false
2528                 return false;
2529             }
2530             return super.onCreatePanelMenu(featureId, menu);
2531         }
2532 
2533         @Override
2534         public void onContentChanged() {
2535             // We purposely do not propagate this call as this is called when we install
2536             // our sub-decor rather than the user's content
2537         }
2538 
2539         @Override
2540         public boolean onPreparePanel(int featureId, View view, Menu menu) {
2541             final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null;
2542 
2543             if (featureId == Window.FEATURE_OPTIONS_PANEL && mb == null) {
2544                 // If this is an options menu but it's not an AppCompat menu, we eat the event
2545                 // and return false
2546                 return false;
2547             }
2548 
2549             // On ICS and below devices, onPreparePanel calls menu.hasVisibleItems() to determine
2550             // if a panel is prepared. This interferes with any initially invisible items, which
2551             // are later made visible. We workaround it by making hasVisibleItems() always
2552             // return true during the onPreparePanel call.
2553             if (mb != null) {
2554                 mb.setOverrideVisibleItems(true);
2555             }
2556 
2557             final boolean handled = super.onPreparePanel(featureId, view, menu);
2558 
2559             if (mb != null) {
2560                 mb.setOverrideVisibleItems(false);
2561             }
2562 
2563             return handled;
2564         }
2565 
2566         @Override
2567         public boolean onMenuOpened(int featureId, Menu menu) {
2568             super.onMenuOpened(featureId, menu);
2569             AppCompatDelegateImpl.this.onMenuOpened(featureId);
2570             return true;
2571         }
2572 
2573         @Override
2574         public void onPanelClosed(int featureId, Menu menu) {
2575             super.onPanelClosed(featureId, menu);
2576             AppCompatDelegateImpl.this.onPanelClosed(featureId);
2577         }
2578 
2579         @Override
2580         public android.view.ActionMode onWindowStartingActionMode(
2581                 android.view.ActionMode.Callback callback) {
2582             if (Build.VERSION.SDK_INT >= 23) {
2583                 // No-op on API 23+
2584                 return null;
2585             }
2586             // We wrap in a support action mode on v14+ if enabled
2587             if (isHandleNativeActionModesEnabled()) {
2588                 return startAsSupportActionMode(callback);
2589             }
2590             // Else, let the call fall through to the wrapped callback
2591             return super.onWindowStartingActionMode(callback);
2592         }
2593 
2594         /**
2595          * Wrap the framework {@link android.view.ActionMode.Callback} in a support action mode and
2596          * let AppCompat display it.
2597          */
2598         final android.view.ActionMode startAsSupportActionMode(
2599                 android.view.ActionMode.Callback callback) {
2600             // Wrap the callback as a v7 ActionMode.Callback
2601             final SupportActionModeWrapper.CallbackWrapper callbackWrapper =
2602                     new SupportActionModeWrapper.CallbackWrapper(mContext, callback);
2603 
2604             // Try and start a support action mode using the wrapped callback
2605             final androidx.appcompat.view.ActionMode supportActionMode =
2606                     startSupportActionMode(callbackWrapper);
2607 
2608             if (supportActionMode != null) {
2609                 // If we received a support action mode, wrap and return it
2610                 return callbackWrapper.getActionModeWrapper(supportActionMode);
2611             }
2612             return null;
2613         }
2614 
2615         @Override
2616         @RequiresApi(23)
2617         public android.view.ActionMode onWindowStartingActionMode(
2618             android.view.ActionMode.Callback callback, int type) {
2619             if (isHandleNativeActionModesEnabled()) {
2620                 switch (type) {
2621                     case android.view.ActionMode.TYPE_PRIMARY:
2622                         // We only take over if the type is TYPE_PRIMARY
2623                         return startAsSupportActionMode(callback);
2624                 }
2625             }
2626             // Else, let the call fall through to the wrapped callback
2627             return super.onWindowStartingActionMode(callback, type);
2628         }
2629 
2630         @Override
2631         @RequiresApi(24)
2632         public void onProvideKeyboardShortcuts(
2633             List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
2634             final PanelFeatureState panel = getPanelState(Window.FEATURE_OPTIONS_PANEL, true);
2635             if (panel != null && panel.menu != null) {
2636                 // The menu provided is one created by PhoneWindow which we don't actually use.
2637                 // Instead we'll pass through our own...
2638                 super.onProvideKeyboardShortcuts(data, panel.menu, deviceId);
2639             } else {
2640                 // If we don't have a menu, jump pass through the original instead
2641                 super.onProvideKeyboardShortcuts(data, menu, deviceId);
2642             }
2643         }
2644     }
2645 
2646     @VisibleForTesting
2647     final class AutoNightModeManager {
2648         private TwilightManager mTwilightManager;
2649         private boolean mIsNight;
2650 
2651         private BroadcastReceiver mAutoTimeChangeReceiver;
2652         private IntentFilter mAutoTimeChangeReceiverFilter;
2653 
2654         AutoNightModeManager(@NonNull TwilightManager twilightManager) {
2655             mTwilightManager = twilightManager;
2656             mIsNight = twilightManager.isNight();
2657         }
2658 
2659         @ApplyableNightMode
2660         int getApplyableNightMode() {
2661             mIsNight = mTwilightManager.isNight();
2662             return mIsNight ? MODE_NIGHT_YES : MODE_NIGHT_NO;
2663         }
2664 
2665         void dispatchTimeChanged() {
2666             final boolean isNight = mTwilightManager.isNight();
2667             if (isNight != mIsNight) {
2668                 mIsNight = isNight;
2669                 applyDayNight();
2670             }
2671         }
2672 
2673         void setup() {
2674             cleanup();
2675 
2676             // If we're set to AUTO, we register a receiver to be notified on time changes. The
2677             // system only sends the tick out every minute, but that's enough fidelity for our use
2678             // case
2679             if (mAutoTimeChangeReceiver == null) {
2680                 mAutoTimeChangeReceiver = new BroadcastReceiver() {
2681                     @Override
2682                     public void onReceive(Context context, Intent intent) {
2683                         if (DEBUG) {
2684                             Log.d("AutoTimeChangeReceiver", "onReceive | Intent: " + intent);
2685                         }
2686                         dispatchTimeChanged();
2687                     }
2688                 };
2689             }
2690             if (mAutoTimeChangeReceiverFilter == null) {
2691                 mAutoTimeChangeReceiverFilter = new IntentFilter();
2692                 mAutoTimeChangeReceiverFilter.addAction(Intent.ACTION_TIME_CHANGED);
2693                 mAutoTimeChangeReceiverFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
2694                 mAutoTimeChangeReceiverFilter.addAction(Intent.ACTION_TIME_TICK);
2695             }
2696             mContext.registerReceiver(mAutoTimeChangeReceiver, mAutoTimeChangeReceiverFilter);
2697         }
2698 
2699         void cleanup() {
2700             if (mAutoTimeChangeReceiver != null) {
2701                 mContext.unregisterReceiver(mAutoTimeChangeReceiver);
2702                 mAutoTimeChangeReceiver = null;
2703             }
2704         }
2705     }
2706 
2707     @Override
2708     public final ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
2709         return new ActionBarDrawableToggleImpl();
2710     }
2711 
2712     private class ActionBarDrawableToggleImpl implements ActionBarDrawerToggle.Delegate {
2713         ActionBarDrawableToggleImpl() {
2714         }
2715 
2716         @Override
2717         public Drawable getThemeUpIndicator() {
2718             final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
2719                     getActionBarThemedContext(), null, new int[]{ R.attr.homeAsUpIndicator });
2720             final Drawable result = a.getDrawable(0);
2721             a.recycle();
2722             return result;
2723         }
2724 
2725         @Override
2726         public Context getActionBarThemedContext() {
2727             return AppCompatDelegateImpl.this.getActionBarThemedContext();
2728         }
2729 
2730         @Override
2731         public boolean isNavigationVisible() {
2732             final ActionBar ab = getSupportActionBar();
2733             return ab != null && (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
2734         }
2735 
2736         @Override
2737         public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
2738             ActionBar ab = getSupportActionBar();
2739             if (ab != null) {
2740                 ab.setHomeAsUpIndicator(upDrawable);
2741                 ab.setHomeActionContentDescription(contentDescRes);
2742             }
2743         }
2744 
2745         @Override
2746         public void setActionBarDescription(int contentDescRes) {
2747             ActionBar ab = getSupportActionBar();
2748             if (ab != null) {
2749                 ab.setHomeActionContentDescription(contentDescRes);
2750             }
2751         }
2752     }
2753 }
2754