Home | History | Annotate | Download | only in app
      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