AppCompatDelegateImplV7.java revision 862ce3b5a8789281e0510da9195bc8ea22581706
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
17package android.support.v7.app;
18
19import android.app.Activity;
20import android.app.Dialog;
21import android.content.Context;
22import android.content.res.Configuration;
23import android.content.res.Resources;
24import android.content.res.TypedArray;
25import android.graphics.PixelFormat;
26import android.graphics.Rect;
27import android.media.AudioManager;
28import android.os.Build;
29import android.os.Bundle;
30import android.os.Parcel;
31import android.os.Parcelable;
32import android.support.annotation.IdRes;
33import android.support.annotation.NonNull;
34import android.support.annotation.Nullable;
35import android.support.v4.app.NavUtils;
36import android.support.v4.os.ParcelableCompat;
37import android.support.v4.os.ParcelableCompatCreatorCallbacks;
38import android.support.v4.view.LayoutInflaterCompat;
39import android.support.v4.view.LayoutInflaterFactory;
40import android.support.v4.view.OnApplyWindowInsetsListener;
41import android.support.v4.view.ViewCompat;
42import android.support.v4.view.ViewConfigurationCompat;
43import android.support.v4.view.ViewPropertyAnimatorCompat;
44import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
45import android.support.v4.view.WindowCompat;
46import android.support.v4.view.WindowInsetsCompat;
47import android.support.v4.widget.PopupWindowCompat;
48import android.support.v7.appcompat.R;
49import android.support.v7.view.ActionMode;
50import android.support.v7.view.ContextThemeWrapper;
51import android.support.v7.view.StandaloneActionMode;
52import android.support.v7.view.menu.ListMenuPresenter;
53import android.support.v7.view.menu.MenuBuilder;
54import android.support.v7.view.menu.MenuPresenter;
55import android.support.v7.view.menu.MenuView;
56import android.support.v7.widget.ActionBarContextView;
57import android.support.v7.widget.AppCompatDrawableManager;
58import android.support.v7.widget.ContentFrameLayout;
59import android.support.v7.widget.DecorContentParent;
60import android.support.v7.widget.FitWindowsViewGroup;
61import android.support.v7.widget.Toolbar;
62import android.support.v7.widget.ViewStubCompat;
63import android.support.v7.widget.ViewUtils;
64import android.text.TextUtils;
65import android.util.AndroidRuntimeException;
66import android.util.AttributeSet;
67import android.util.Log;
68import android.util.TypedValue;
69import android.view.Gravity;
70import android.view.KeyCharacterMap;
71import android.view.KeyEvent;
72import android.view.LayoutInflater;
73import android.view.Menu;
74import android.view.MenuItem;
75import android.view.MotionEvent;
76import android.view.View;
77import android.view.ViewConfiguration;
78import android.view.ViewGroup;
79import android.view.ViewParent;
80import android.view.Window;
81import android.view.WindowManager;
82import android.view.accessibility.AccessibilityEvent;
83import android.widget.FrameLayout;
84import android.widget.PopupWindow;
85import android.widget.TextView;
86
87import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
88import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
89import static android.view.Window.FEATURE_OPTIONS_PANEL;
90
91class AppCompatDelegateImplV7 extends AppCompatDelegateImplBase
92        implements MenuBuilder.Callback, LayoutInflaterFactory {
93
94    private DecorContentParent mDecorContentParent;
95    private ActionMenuPresenterCallback mActionMenuPresenterCallback;
96    private PanelMenuPresenterCallback mPanelMenuPresenterCallback;
97
98    ActionMode mActionMode;
99    ActionBarContextView mActionModeView;
100    PopupWindow mActionModePopup;
101    Runnable mShowActionModePopup;
102    ViewPropertyAnimatorCompat mFadeAnim = null;
103
104    // true if we have installed a window sub-decor layout.
105    private boolean mSubDecorInstalled;
106    private ViewGroup mSubDecor;
107
108    private TextView mTitleView;
109    private View mStatusGuard;
110
111    // Used to keep track of Progress Bar Window features
112    private boolean mFeatureProgress, mFeatureIndeterminateProgress;
113
114    // Used for emulating PanelFeatureState
115    private boolean mClosingActionMenu;
116    private PanelFeatureState[] mPanels;
117    private PanelFeatureState mPreparedPanel;
118
119    private boolean mLongPressBackDown;
120
121    private boolean mInvalidatePanelMenuPosted;
122    private int mInvalidatePanelMenuFeatures;
123    private final Runnable mInvalidatePanelMenuRunnable = new Runnable() {
124        @Override
125        public void run() {
126            if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_OPTIONS_PANEL) != 0) {
127                doInvalidatePanelMenu(FEATURE_OPTIONS_PANEL);
128            }
129            if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_SUPPORT_ACTION_BAR) != 0) {
130                doInvalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
131            }
132            mInvalidatePanelMenuPosted = false;
133            mInvalidatePanelMenuFeatures = 0;
134        }
135    };
136
137    private boolean mEnableDefaultActionBarUp;
138
139    private Rect mTempRect1;
140    private Rect mTempRect2;
141
142    private AppCompatViewInflater mAppCompatViewInflater;
143
144    AppCompatDelegateImplV7(Context context, Window window, AppCompatCallback callback) {
145        super(context, window, callback);
146    }
147
148    @Override
149    public void onCreate(Bundle savedInstanceState) {
150        if (mOriginalWindowCallback instanceof Activity) {
151            if (NavUtils.getParentActivityName((Activity) mOriginalWindowCallback) != null) {
152                // Peek at the Action Bar and update it if it already exists
153                ActionBar ab = peekSupportActionBar();
154                if (ab == null) {
155                    mEnableDefaultActionBarUp = true;
156                } else {
157                    ab.setDefaultDisplayHomeAsUpEnabled(true);
158                }
159            }
160        }
161    }
162
163    @Override
164    public void onPostCreate(Bundle savedInstanceState) {
165        // Make sure that the sub decor is installed
166        ensureSubDecor();
167    }
168
169    @Override
170    public void initWindowDecorActionBar() {
171        ensureSubDecor();
172
173        if (!mHasActionBar || mActionBar != null) {
174            return;
175        }
176
177        if (mOriginalWindowCallback instanceof Activity) {
178            mActionBar = new WindowDecorActionBar((Activity) mOriginalWindowCallback,
179                    mOverlayActionBar);
180        } else if (mOriginalWindowCallback instanceof Dialog) {
181            mActionBar = new WindowDecorActionBar((Dialog) mOriginalWindowCallback);
182        }
183        if (mActionBar != null) {
184            mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
185        }
186    }
187
188    @Override
189    public void setSupportActionBar(Toolbar toolbar) {
190        if (!(mOriginalWindowCallback instanceof Activity)) {
191            // Only Activities support custom Action Bars
192            return;
193        }
194
195        final ActionBar ab = getSupportActionBar();
196        if (ab instanceof WindowDecorActionBar) {
197            throw new IllegalStateException("This Activity already has an action bar supplied " +
198                    "by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set " +
199                    "windowActionBar to false in your theme to use a Toolbar instead.");
200        }
201
202        // If we reach here then we're setting a new action bar
203        // First clear out the MenuInflater to make sure that it is valid for the new Action Bar
204        mMenuInflater = null;
205
206        // If we have an action bar currently, destroy it
207        if (ab != null) {
208            ab.onDestroy();
209        }
210
211        if (toolbar != null) {
212            final ToolbarActionBar tbab = new ToolbarActionBar(toolbar,
213                    ((Activity) mContext).getTitle(), mAppCompatWindowCallback);
214            mActionBar = tbab;
215            mWindow.setCallback(tbab.getWrappedWindowCallback());
216        } else {
217            mActionBar = null;
218            // Re-set the original window callback since we may have already set a Toolbar wrapper
219            mWindow.setCallback(mAppCompatWindowCallback);
220        }
221
222        invalidateOptionsMenu();
223    }
224
225    @Nullable
226    @Override
227    public View findViewById(@IdRes int id) {
228        ensureSubDecor();
229        return mWindow.findViewById(id);
230    }
231
232    @Override
233    public void onConfigurationChanged(Configuration newConfig) {
234        // If this is called before sub-decor is installed, ActionBar will not
235        // be properly initialized.
236        if (mHasActionBar && mSubDecorInstalled) {
237            // Note: The action bar will need to access
238            // view changes from superclass.
239            ActionBar ab = getSupportActionBar();
240            if (ab != null) {
241                ab.onConfigurationChanged(newConfig);
242            }
243        }
244
245        // Re-apply Day/Night to the new configuration
246        applyDayNight();
247    }
248
249    @Override
250    public void onStop() {
251        ActionBar ab = getSupportActionBar();
252        if (ab != null) {
253            ab.setShowHideAnimationEnabled(false);
254        }
255    }
256
257    @Override
258    public void onPostResume() {
259        ActionBar ab = getSupportActionBar();
260        if (ab != null) {
261            ab.setShowHideAnimationEnabled(true);
262        }
263    }
264
265    @Override
266    public void setContentView(View v) {
267        ensureSubDecor();
268        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
269        contentParent.removeAllViews();
270        contentParent.addView(v);
271        mOriginalWindowCallback.onContentChanged();
272    }
273
274    @Override
275    public void setContentView(int resId) {
276        ensureSubDecor();
277        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
278        contentParent.removeAllViews();
279        LayoutInflater.from(mContext).inflate(resId, contentParent);
280        mOriginalWindowCallback.onContentChanged();
281    }
282
283    @Override
284    public void setContentView(View v, ViewGroup.LayoutParams lp) {
285        ensureSubDecor();
286        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
287        contentParent.removeAllViews();
288        contentParent.addView(v, lp);
289        mOriginalWindowCallback.onContentChanged();
290    }
291
292    @Override
293    public void addContentView(View v, ViewGroup.LayoutParams lp) {
294        ensureSubDecor();
295        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
296        contentParent.addView(v, lp);
297        mOriginalWindowCallback.onContentChanged();
298    }
299
300    @Override
301    public void onDestroy() {
302        super.onDestroy();
303
304        if (mActionBar != null) {
305            mActionBar.onDestroy();
306        }
307    }
308
309    private void ensureSubDecor() {
310        if (!mSubDecorInstalled) {
311            mSubDecor = createSubDecor();
312
313            // If a title was set before we installed the decor, propogate it now
314            CharSequence title = getTitle();
315            if (!TextUtils.isEmpty(title)) {
316                onTitleChanged(title);
317            }
318
319            applyFixedSizeWindow();
320
321            onSubDecorInstalled(mSubDecor);
322
323            mSubDecorInstalled = true;
324
325            // Invalidate if the panel menu hasn't been created before this.
326            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
327            // being called in the middle of onCreate or similar.
328            // A pending invalidation will typically be resolved before the posted message
329            // would run normally in order to satisfy instance state restoration.
330            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
331            if (!isDestroyed() && (st == null || st.menu == null)) {
332                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
333            }
334        }
335    }
336
337    private ViewGroup createSubDecor() {
338        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
339
340        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
341            a.recycle();
342            throw new IllegalStateException(
343                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
344        }
345
346        if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
347            requestWindowFeature(Window.FEATURE_NO_TITLE);
348        } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
349            // Don't allow an action bar if there is no title.
350            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
351        }
352        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
353            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
354        }
355        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
356            requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
357        }
358        mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
359        a.recycle();
360
361        final LayoutInflater inflater = LayoutInflater.from(mContext);
362        ViewGroup subDecor = null;
363
364
365        if (!mWindowNoTitle) {
366            if (mIsFloating) {
367                // If we're floating, inflate the dialog title decor
368                subDecor = (ViewGroup) inflater.inflate(
369                        R.layout.abc_dialog_title_material, null);
370
371                // Floating windows can never have an action bar, reset the flags
372                mHasActionBar = mOverlayActionBar = false;
373            } else if (mHasActionBar) {
374                /**
375                 * This needs some explanation. As we can not use the android:theme attribute
376                 * pre-L, we emulate it by manually creating a LayoutInflater using a
377                 * ContextThemeWrapper pointing to actionBarTheme.
378                 */
379                TypedValue outValue = new TypedValue();
380                mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
381
382                Context themedContext;
383                if (outValue.resourceId != 0) {
384                    themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
385                } else {
386                    themedContext = mContext;
387                }
388
389                // Now inflate the view using the themed context and set it as the content view
390                subDecor = (ViewGroup) LayoutInflater.from(themedContext)
391                        .inflate(R.layout.abc_screen_toolbar, null);
392
393                mDecorContentParent = (DecorContentParent) subDecor
394                        .findViewById(R.id.decor_content_parent);
395                mDecorContentParent.setWindowCallback(getWindowCallback());
396
397                /**
398                 * Propagate features to DecorContentParent
399                 */
400                if (mOverlayActionBar) {
401                    mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
402                }
403                if (mFeatureProgress) {
404                    mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
405                }
406                if (mFeatureIndeterminateProgress) {
407                    mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
408                }
409            }
410        } else {
411            if (mOverlayActionMode) {
412                subDecor = (ViewGroup) inflater.inflate(
413                        R.layout.abc_screen_simple_overlay_action_mode, null);
414            } else {
415                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
416            }
417
418            if (Build.VERSION.SDK_INT >= 21) {
419                // If we're running on L or above, we can rely on ViewCompat's
420                // setOnApplyWindowInsetsListener
421                ViewCompat.setOnApplyWindowInsetsListener(subDecor,
422                        new OnApplyWindowInsetsListener() {
423                            @Override
424                            public WindowInsetsCompat onApplyWindowInsets(View v,
425                                    WindowInsetsCompat insets) {
426                                final int top = insets.getSystemWindowInsetTop();
427                                final int newTop = updateStatusGuard(top);
428
429                                if (top != newTop) {
430                                    insets = insets.replaceSystemWindowInsets(
431                                            insets.getSystemWindowInsetLeft(),
432                                            newTop,
433                                            insets.getSystemWindowInsetRight(),
434                                            insets.getSystemWindowInsetBottom());
435                                }
436
437                                // Now apply the insets on our view
438                                return ViewCompat.onApplyWindowInsets(v, insets);
439                            }
440                        });
441            } else {
442                // Else, we need to use our own FitWindowsViewGroup handling
443                ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
444                        new FitWindowsViewGroup.OnFitSystemWindowsListener() {
445                            @Override
446                            public void onFitSystemWindows(Rect insets) {
447                                insets.top = updateStatusGuard(insets.top);
448                            }
449                        });
450            }
451        }
452
453        if (subDecor == null) {
454            throw new IllegalArgumentException(
455                    "AppCompat does not support the current theme features: { "
456                            + "windowActionBar: " + mHasActionBar
457                            + ", windowActionBarOverlay: "+ mOverlayActionBar
458                            + ", android:windowIsFloating: " + mIsFloating
459                            + ", windowActionModeOverlay: " + mOverlayActionMode
460                            + ", windowNoTitle: " + mWindowNoTitle
461                            + " }");
462        }
463
464        if (mDecorContentParent == null) {
465            mTitleView = (TextView) subDecor.findViewById(R.id.title);
466        }
467
468        // Make the decor optionally fit system windows, like the window's decor
469        ViewUtils.makeOptionalFitsSystemWindows(subDecor);
470
471        final ViewGroup decorContent = (ViewGroup) mWindow.findViewById(android.R.id.content);
472        final ContentFrameLayout abcContent = (ContentFrameLayout) subDecor.findViewById(
473                R.id.action_bar_activity_content);
474
475        // There might be Views already added to the Window's content view so we need to
476        // migrate them to our content view
477        while (decorContent.getChildCount() > 0) {
478            final View child = decorContent.getChildAt(0);
479            decorContent.removeViewAt(0);
480            abcContent.addView(child);
481        }
482
483        // Now set the Window's content view with the decor
484        mWindow.setContentView(subDecor);
485
486        // Change our content FrameLayout to use the android.R.id.content id.
487        // Useful for fragments.
488        decorContent.setId(View.NO_ID);
489        abcContent.setId(android.R.id.content);
490
491        // The decorContent may have a foreground drawable set (windowContentOverlay).
492        // Remove this as we handle it ourselves
493        if (decorContent instanceof FrameLayout) {
494            ((FrameLayout) decorContent).setForeground(null);
495        }
496
497        abcContent.setAttachListener(new ContentFrameLayout.OnAttachListener() {
498            @Override
499            public void onAttachedFromWindow() {}
500
501            @Override
502            public void onDetachedFromWindow() {
503                dismissPopups();
504            }
505        });
506
507        return subDecor;
508    }
509
510    void onSubDecorInstalled(ViewGroup subDecor) {}
511
512    private void applyFixedSizeWindow() {
513        ContentFrameLayout cfl = (ContentFrameLayout) mSubDecor.findViewById(android.R.id.content);
514
515        // This is a bit weird. In the framework, the window sizing attributes control
516        // the decor view's size, meaning that any padding is inset for the min/max widths below.
517        // We don't control measurement at that level, so we need to workaround it by making sure
518        // that the decor view's padding is taken into account.
519        final View windowDecor = mWindow.getDecorView();
520        cfl.setDecorPadding(windowDecor.getPaddingLeft(),
521                windowDecor.getPaddingTop(), windowDecor.getPaddingRight(),
522                windowDecor.getPaddingBottom());
523
524        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
525        a.getValue(R.styleable.AppCompatTheme_windowMinWidthMajor, cfl.getMinWidthMajor());
526        a.getValue(R.styleable.AppCompatTheme_windowMinWidthMinor, cfl.getMinWidthMinor());
527
528        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedWidthMajor)) {
529            a.getValue(R.styleable.AppCompatTheme_windowFixedWidthMajor,
530                    cfl.getFixedWidthMajor());
531        }
532        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedWidthMinor)) {
533            a.getValue(R.styleable.AppCompatTheme_windowFixedWidthMinor,
534                    cfl.getFixedWidthMinor());
535        }
536        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedHeightMajor)) {
537            a.getValue(R.styleable.AppCompatTheme_windowFixedHeightMajor,
538                    cfl.getFixedHeightMajor());
539        }
540        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedHeightMinor)) {
541            a.getValue(R.styleable.AppCompatTheme_windowFixedHeightMinor,
542                    cfl.getFixedHeightMinor());
543        }
544        a.recycle();
545
546        cfl.requestLayout();
547    }
548
549    @Override
550    public boolean requestWindowFeature(int featureId) {
551        featureId = sanitizeWindowFeatureId(featureId);
552
553        if (mWindowNoTitle && featureId == FEATURE_SUPPORT_ACTION_BAR) {
554            return false; // Ignore. No title dominates.
555        }
556        if (mHasActionBar && featureId == Window.FEATURE_NO_TITLE) {
557            // Remove the action bar feature if we have no title. No title dominates.
558            mHasActionBar = false;
559        }
560
561        switch (featureId) {
562            case FEATURE_SUPPORT_ACTION_BAR:
563                throwFeatureRequestIfSubDecorInstalled();
564                mHasActionBar = true;
565                return true;
566            case FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
567                throwFeatureRequestIfSubDecorInstalled();
568                mOverlayActionBar = true;
569                return true;
570            case FEATURE_ACTION_MODE_OVERLAY:
571                throwFeatureRequestIfSubDecorInstalled();
572                mOverlayActionMode = true;
573                return true;
574            case Window.FEATURE_PROGRESS:
575                throwFeatureRequestIfSubDecorInstalled();
576                mFeatureProgress = true;
577                return true;
578            case Window.FEATURE_INDETERMINATE_PROGRESS:
579                throwFeatureRequestIfSubDecorInstalled();
580                mFeatureIndeterminateProgress = true;
581                return true;
582            case Window.FEATURE_NO_TITLE:
583                throwFeatureRequestIfSubDecorInstalled();
584                mWindowNoTitle = true;
585                return true;
586        }
587
588        return mWindow.requestFeature(featureId);
589    }
590
591    @Override
592    public boolean hasWindowFeature(int featureId) {
593        featureId = sanitizeWindowFeatureId(featureId);
594        switch (featureId) {
595            case FEATURE_SUPPORT_ACTION_BAR:
596                return mHasActionBar;
597            case FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
598                return mOverlayActionBar;
599            case FEATURE_ACTION_MODE_OVERLAY:
600                return mOverlayActionMode;
601            case Window.FEATURE_PROGRESS:
602                return mFeatureProgress;
603            case Window.FEATURE_INDETERMINATE_PROGRESS:
604                return mFeatureIndeterminateProgress;
605            case Window.FEATURE_NO_TITLE:
606                return mWindowNoTitle;
607        }
608        return mWindow.hasFeature(featureId);
609    }
610
611    @Override
612    void onTitleChanged(CharSequence title) {
613        if (mDecorContentParent != null) {
614            mDecorContentParent.setWindowTitle(title);
615        } else if (peekSupportActionBar() != null) {
616            peekSupportActionBar().setWindowTitle(title);
617        } else if (mTitleView != null) {
618            mTitleView.setText(title);
619        }
620    }
621
622    @Override
623    void onPanelClosed(final int featureId, Menu menu) {
624        if (featureId == FEATURE_SUPPORT_ACTION_BAR) {
625            ActionBar ab = getSupportActionBar();
626            if (ab != null) {
627                ab.dispatchMenuVisibilityChanged(false);
628            }
629        } else if (featureId == FEATURE_OPTIONS_PANEL) {
630            // Make sure that the options panel is closed. This is mainly used when we're using a
631            // ToolbarActionBar
632            PanelFeatureState st = getPanelState(featureId, true);
633            if (st.isOpen) {
634                closePanel(st, false);
635            }
636        }
637    }
638
639    @Override
640    boolean onMenuOpened(final int featureId, Menu menu) {
641        if (featureId == FEATURE_SUPPORT_ACTION_BAR) {
642            ActionBar ab = getSupportActionBar();
643            if (ab != null) {
644                ab.dispatchMenuVisibilityChanged(true);
645            }
646            return true;
647        }
648        return false;
649    }
650
651    @Override
652    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
653        final Window.Callback cb = getWindowCallback();
654        if (cb != null && !isDestroyed()) {
655            final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
656            if (panel != null) {
657                return cb.onMenuItemSelected(panel.featureId, item);
658            }
659        }
660        return false;
661    }
662
663    @Override
664    public void onMenuModeChange(MenuBuilder menu) {
665        reopenMenu(menu, true);
666    }
667
668    @Override
669    public ActionMode startSupportActionMode(ActionMode.Callback callback) {
670        if (callback == null) {
671            throw new IllegalArgumentException("ActionMode callback can not be null.");
672        }
673
674        if (mActionMode != null) {
675            mActionMode.finish();
676        }
677
678        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapperV7(callback);
679
680        ActionBar ab = getSupportActionBar();
681        if (ab != null) {
682            mActionMode = ab.startActionMode(wrappedCallback);
683            if (mActionMode != null && mAppCompatCallback != null) {
684                mAppCompatCallback.onSupportActionModeStarted(mActionMode);
685            }
686        }
687
688        if (mActionMode == null) {
689            // If the action bar didn't provide an action mode, start the emulated window one
690            mActionMode = startSupportActionModeFromWindow(wrappedCallback);
691        }
692
693        return mActionMode;
694    }
695
696    @Override
697    public void invalidateOptionsMenu() {
698        final ActionBar ab = getSupportActionBar();
699        if (ab != null && ab.invalidateOptionsMenu()) return;
700
701        invalidatePanelMenu(FEATURE_OPTIONS_PANEL);
702    }
703
704    @Override
705    ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback) {
706        endOnGoingFadeAnimation();
707        if (mActionMode != null) {
708            mActionMode.finish();
709        }
710
711        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapperV7(callback);
712        ActionMode mode = null;
713        if (mAppCompatCallback != null && !isDestroyed()) {
714            try {
715                mode = mAppCompatCallback.onWindowStartingSupportActionMode(wrappedCallback);
716            } catch (AbstractMethodError ame) {
717                // Older apps might not implement this callback method.
718            }
719        }
720
721        if (mode != null) {
722            mActionMode = mode;
723        } else {
724            if (mActionModeView == null) {
725                if (mIsFloating) {
726                    // Use the action bar theme.
727                    final TypedValue outValue = new TypedValue();
728                    final Resources.Theme baseTheme = mContext.getTheme();
729                    baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
730
731                    final Context actionBarContext;
732                    if (outValue.resourceId != 0) {
733                        final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
734                        actionBarTheme.setTo(baseTheme);
735                        actionBarTheme.applyStyle(outValue.resourceId, true);
736
737                        actionBarContext = new ContextThemeWrapper(mContext, 0);
738                        actionBarContext.getTheme().setTo(actionBarTheme);
739                    } else {
740                        actionBarContext = mContext;
741                    }
742
743                    mActionModeView = new ActionBarContextView(actionBarContext);
744                    mActionModePopup = new PopupWindow(actionBarContext, null,
745                            R.attr.actionModePopupWindowStyle);
746                    PopupWindowCompat.setWindowLayoutType(mActionModePopup,
747                            WindowManager.LayoutParams.TYPE_APPLICATION);
748                    mActionModePopup.setContentView(mActionModeView);
749                    mActionModePopup.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
750
751                    actionBarContext.getTheme().resolveAttribute(
752                            R.attr.actionBarSize, outValue, true);
753                    final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
754                            actionBarContext.getResources().getDisplayMetrics());
755                    mActionModeView.setContentHeight(height);
756                    mActionModePopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
757                    mShowActionModePopup = new Runnable() {
758                        public void run() {
759                            mActionModePopup.showAtLocation(
760                                    mActionModeView,
761                                    Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
762                            endOnGoingFadeAnimation();
763                            ViewCompat.setAlpha(mActionModeView, 0f);
764                            mFadeAnim = ViewCompat.animate(mActionModeView).alpha(1f);
765                            mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
766                                @Override
767                                public void onAnimationEnd(View view) {
768                                    ViewCompat.setAlpha(mActionModeView, 1f);
769                                    mFadeAnim.setListener(null);
770                                    mFadeAnim = null;
771                                }
772
773                                @Override
774                                public void onAnimationStart(View view) {
775                                    mActionModeView.setVisibility(View.VISIBLE);
776                                }
777                            });
778                        }
779                    };
780                } else {
781                    ViewStubCompat stub = (ViewStubCompat) mSubDecor
782                            .findViewById(R.id.action_mode_bar_stub);
783                    if (stub != null) {
784                        // Set the layout inflater so that it is inflated with the action bar's context
785                        stub.setLayoutInflater(LayoutInflater.from(getActionBarThemedContext()));
786                        mActionModeView = (ActionBarContextView) stub.inflate();
787                    }
788                }
789            }
790
791            if (mActionModeView != null) {
792                endOnGoingFadeAnimation();
793                mActionModeView.killMode();
794                mode = new StandaloneActionMode(mActionModeView.getContext(), mActionModeView,
795                        wrappedCallback, mActionModePopup == null);
796                if (callback.onCreateActionMode(mode, mode.getMenu())) {
797                    mode.invalidate();
798                    mActionModeView.initForMode(mode);
799                    mActionMode = mode;
800                    ViewCompat.setAlpha(mActionModeView, 0f);
801                    mFadeAnim = ViewCompat.animate(mActionModeView).alpha(1f);
802                    mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
803                        @Override
804                        public void onAnimationEnd(View view) {
805                            ViewCompat.setAlpha(mActionModeView, 1f);
806                            mFadeAnim.setListener(null);
807                            mFadeAnim = null;
808                        }
809
810                        @Override
811                        public void onAnimationStart(View view) {
812                            mActionModeView.setVisibility(View.VISIBLE);
813                            mActionModeView.sendAccessibilityEvent(
814                                    AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
815                            if (mActionModeView.getParent() != null) {
816                                ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
817                            }
818                        }
819                    });
820                    if (mActionModePopup != null) {
821                        mWindow.getDecorView().post(mShowActionModePopup);
822                    }
823                } else {
824                    mActionMode = null;
825                }
826            }
827        }
828        if (mActionMode != null && mAppCompatCallback != null) {
829            mAppCompatCallback.onSupportActionModeStarted(mActionMode);
830        }
831        return mActionMode;
832    }
833
834    private void endOnGoingFadeAnimation() {
835        if (mFadeAnim != null) {
836            mFadeAnim.cancel();
837        }
838    }
839
840    boolean onBackPressed() {
841        // Back cancels action modes first.
842        if (mActionMode != null) {
843            mActionMode.finish();
844            return true;
845        }
846
847        // Next collapse any expanded action views.
848        ActionBar ab = getSupportActionBar();
849        if (ab != null && ab.collapseActionView()) {
850            return true;
851        }
852
853        // Let the call through...
854        return false;
855    }
856
857    @Override
858    boolean onKeyShortcut(int keyCode, KeyEvent ev) {
859        // Let the Action Bar have a chance at handling the shortcut
860        ActionBar ab = getSupportActionBar();
861        if (ab != null && ab.onKeyShortcut(keyCode, ev)) {
862            return true;
863        }
864
865        // If the panel is already prepared, then perform the shortcut using it.
866        boolean handled;
867        if (mPreparedPanel != null) {
868            handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev,
869                    Menu.FLAG_PERFORM_NO_CLOSE);
870            if (handled) {
871                if (mPreparedPanel != null) {
872                    mPreparedPanel.isHandled = true;
873                }
874                return true;
875            }
876        }
877
878        // If the panel is not prepared, then we may be trying to handle a shortcut key
879        // combination such as Control+C.  Temporarily prepare the panel then mark it
880        // unprepared again when finished to ensure that the panel will again be prepared
881        // the next time it is shown for real.
882        if (mPreparedPanel == null) {
883            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
884            preparePanel(st, ev);
885            handled = performPanelShortcut(st, ev.getKeyCode(), ev, Menu.FLAG_PERFORM_NO_CLOSE);
886            st.isPrepared = false;
887            if (handled) {
888                return true;
889            }
890        }
891        return false;
892    }
893
894    @Override
895    boolean dispatchKeyEvent(KeyEvent event) {
896        if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) {
897            // If this is a MENU event, let the Activity have a go.
898            if (mOriginalWindowCallback.dispatchKeyEvent(event)) {
899                return true;
900            }
901        }
902
903        final int keyCode = event.getKeyCode();
904        final int action = event.getAction();
905        final boolean isDown = action == KeyEvent.ACTION_DOWN;
906
907        return isDown ? onKeyDown(keyCode, event) : onKeyUp(keyCode, event);
908    }
909
910    boolean onKeyUp(int keyCode, KeyEvent event) {
911        switch (keyCode) {
912            case KeyEvent.KEYCODE_MENU:
913                onKeyUpPanel(Window.FEATURE_OPTIONS_PANEL, event);
914                return true;
915            case KeyEvent.KEYCODE_BACK:
916                final boolean wasLongPressBackDown = mLongPressBackDown;
917                mLongPressBackDown = false;
918
919                PanelFeatureState st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
920                if (st != null && st.isOpen) {
921                    if (!wasLongPressBackDown) {
922                        // Certain devices allow opening the options menu via a long press of the
923                        // back button. We should only close the open options menu if it wasn't
924                        // opened via a long press gesture.
925                        closePanel(st, true);
926                    }
927                    return true;
928                }
929                if (onBackPressed()) {
930                    return true;
931                }
932                break;
933        }
934        return false;
935    }
936
937    boolean onKeyDown(int keyCode, KeyEvent event) {
938        switch (keyCode) {
939            case KeyEvent.KEYCODE_MENU:
940                onKeyDownPanel(Window.FEATURE_OPTIONS_PANEL, event);
941                // We need to return true here and not let it bubble up to the Window.
942                // For empty menus, PhoneWindow's KEYCODE_BACK handling will steals all events,
943                // not allowing the Activity to call onBackPressed().
944                return true;
945            case KeyEvent.KEYCODE_BACK:
946                // Certain devices allow opening the options menu via a long press of the back
947                // button. We keep a record of whether the last event is from a long press.
948                mLongPressBackDown = (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
949                break;
950        }
951
952        // On API v7-10 we need to manually call onKeyShortcut() as this is not called
953        // from the Activity
954        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
955            // We do not return true here otherwise dispatchKeyEvent will not reach the Activity
956            // (which results in the back button not working)
957            onKeyShortcut(keyCode, event);
958        }
959        return false;
960    }
961
962    @Override
963    public View createView(View parent, final String name, @NonNull Context context,
964            @NonNull AttributeSet attrs) {
965        final boolean isPre21 = Build.VERSION.SDK_INT < 21;
966
967        if (mAppCompatViewInflater == null) {
968            mAppCompatViewInflater = new AppCompatViewInflater();
969        }
970
971        // We only want the View to inherit its context if we're running pre-v21
972        final boolean inheritContext = isPre21 && shouldInheritContext((ViewParent) parent);
973
974        return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
975                isPre21, /* Only read android:theme pre-L (L+ handles this anyway) */
976                true /* Read read app:theme as a fallback at all times for legacy reasons */
977        );
978    }
979
980    private boolean shouldInheritContext(ViewParent parent) {
981        if (parent == null) {
982            // The initial parent is null so just return false
983            return false;
984        }
985        final View windowDecor = mWindow.getDecorView();
986        while (true) {
987            if (parent == null) {
988                // Bingo. We've hit a view which has a null parent before being terminated from
989                // the loop. This is (most probably) because it's the root view in an inflation
990                // call, therefore we should inherit. This works as the inflated layout is only
991                // added to the hierarchy at the end of the inflate() call.
992                return true;
993            } else if (parent == windowDecor || !(parent instanceof View)
994                    || ViewCompat.isAttachedToWindow((View) parent)) {
995                // We have either hit the window's decor view, a parent which isn't a View
996                // (i.e. ViewRootImpl), or an attached view, so we know that the original parent
997                // is currently added to the view hierarchy. This means that it has not be
998                // inflated in the current inflate() call and we should not inherit the context.
999                return false;
1000            }
1001            parent = parent.getParent();
1002        }
1003    }
1004
1005    @Override
1006    public void installViewFactory() {
1007        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
1008        if (layoutInflater.getFactory() == null) {
1009            LayoutInflaterCompat.setFactory(layoutInflater, this);
1010        } else {
1011            if (!(LayoutInflaterCompat.getFactory(layoutInflater)
1012                    instanceof AppCompatDelegateImplV7)) {
1013                Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
1014                        + " so we can not install AppCompat's");
1015            }
1016        }
1017    }
1018
1019    /**
1020     * From {@link android.support.v4.view.LayoutInflaterFactory}
1021     */
1022    @Override
1023    public final View onCreateView(View parent, String name,
1024            Context context, AttributeSet attrs) {
1025        // First let the Activity's Factory try and inflate the view
1026        final View view = callActivityOnCreateView(parent, name, context, attrs);
1027        if (view != null) {
1028            return view;
1029        }
1030
1031        // If the Factory didn't handle it, let our createView() method try
1032        return createView(parent, name, context, attrs);
1033    }
1034
1035    View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
1036        // Let the Activity's LayoutInflater.Factory try and handle it
1037        if (mOriginalWindowCallback instanceof LayoutInflater.Factory) {
1038            final View result = ((LayoutInflater.Factory) mOriginalWindowCallback)
1039                    .onCreateView(name, context, attrs);
1040            if (result != null) {
1041                return result;
1042            }
1043        }
1044        return null;
1045    }
1046
1047    private void openPanel(final PanelFeatureState st, KeyEvent event) {
1048        // Already open, return
1049        if (st.isOpen || isDestroyed()) {
1050            return;
1051        }
1052
1053        // Don't open an options panel for honeycomb apps on xlarge devices.
1054        // (The app should be using an action bar for menu items.)
1055        if (st.featureId == FEATURE_OPTIONS_PANEL) {
1056            Context context = mContext;
1057            Configuration config = context.getResources().getConfiguration();
1058            boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) ==
1059                    Configuration.SCREENLAYOUT_SIZE_XLARGE;
1060            boolean isHoneycombApp = context.getApplicationInfo().targetSdkVersion >=
1061                    android.os.Build.VERSION_CODES.HONEYCOMB;
1062
1063            if (isXLarge && isHoneycombApp) {
1064                return;
1065            }
1066        }
1067
1068        Window.Callback cb = getWindowCallback();
1069        if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) {
1070            // Callback doesn't want the menu to open, reset any state
1071            closePanel(st, true);
1072            return;
1073        }
1074
1075        final WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
1076        if (wm == null) {
1077            return;
1078        }
1079
1080        // Prepare panel (should have been done before, but just in case)
1081        if (!preparePanel(st, event)) {
1082            return;
1083        }
1084
1085        int width = WRAP_CONTENT;
1086        if (st.decorView == null || st.refreshDecorView) {
1087            if (st.decorView == null) {
1088                // Initialize the panel decor, this will populate st.decorView
1089                if (!initializePanelDecor(st) || (st.decorView == null))
1090                    return;
1091            } else if (st.refreshDecorView && (st.decorView.getChildCount() > 0)) {
1092                // Decor needs refreshing, so remove its views
1093                st.decorView.removeAllViews();
1094            }
1095
1096            // This will populate st.shownPanelView
1097            if (!initializePanelContent(st) || !st.hasPanelItems()) {
1098                return;
1099            }
1100
1101            ViewGroup.LayoutParams lp = st.shownPanelView.getLayoutParams();
1102            if (lp == null) {
1103                lp = new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
1104            }
1105
1106            int backgroundResId = st.background;
1107            st.decorView.setBackgroundResource(backgroundResId);
1108
1109            ViewParent shownPanelParent = st.shownPanelView.getParent();
1110            if (shownPanelParent != null && shownPanelParent instanceof ViewGroup) {
1111                ((ViewGroup) shownPanelParent).removeView(st.shownPanelView);
1112            }
1113            st.decorView.addView(st.shownPanelView, lp);
1114
1115            /*
1116             * Give focus to the view, if it or one of its children does not
1117             * already have it.
1118             */
1119            if (!st.shownPanelView.hasFocus()) {
1120                st.shownPanelView.requestFocus();
1121            }
1122        } else if (st.createdPanelView != null) {
1123            // If we already had a panel view, carry width=MATCH_PARENT through
1124            // as we did above when it was created.
1125            ViewGroup.LayoutParams lp = st.createdPanelView.getLayoutParams();
1126            if (lp != null && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
1127                width = MATCH_PARENT;
1128            }
1129        }
1130
1131        st.isHandled = false;
1132
1133        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1134                width, WRAP_CONTENT,
1135                st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
1136                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1137                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1138                PixelFormat.TRANSLUCENT);
1139
1140        lp.gravity = st.gravity;
1141        lp.windowAnimations = st.windowAnimations;
1142
1143        wm.addView(st.decorView, lp);
1144        st.isOpen = true;
1145    }
1146
1147    private boolean initializePanelDecor(PanelFeatureState st) {
1148        st.setStyle(getActionBarThemedContext());
1149        st.decorView = new ListMenuDecorView(st.listPresenterContext);
1150        st.gravity = Gravity.CENTER | Gravity.BOTTOM;
1151        return true;
1152    }
1153
1154    private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
1155        if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() &&
1156                (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mContext)) ||
1157                        mDecorContentParent.isOverflowMenuShowPending())) {
1158
1159            final Window.Callback cb = getWindowCallback();
1160
1161            if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {
1162                if (cb != null && !isDestroyed()) {
1163                    // If we have a menu invalidation pending, do it now.
1164                    if (mInvalidatePanelMenuPosted &&
1165                            (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) {
1166                        mWindow.getDecorView().removeCallbacks(mInvalidatePanelMenuRunnable);
1167                        mInvalidatePanelMenuRunnable.run();
1168                    }
1169
1170                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1171
1172                    // If we don't have a menu or we're waiting for a full content refresh,
1173                    // forget it. This is a lingering event that no longer matters.
1174                    if (st.menu != null && !st.refreshMenuContent &&
1175                            cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
1176                        cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, st.menu);
1177                        mDecorContentParent.showOverflowMenu();
1178                    }
1179                }
1180            } else {
1181                mDecorContentParent.hideOverflowMenu();
1182                if (!isDestroyed()) {
1183                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1184                    cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, st.menu);
1185                }
1186            }
1187            return;
1188        }
1189
1190        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
1191
1192        st.refreshDecorView = true;
1193        closePanel(st, false);
1194
1195        openPanel(st, null);
1196    }
1197
1198    private boolean initializePanelMenu(final PanelFeatureState st) {
1199        Context context = mContext;
1200
1201        // If we have an action bar, initialize the menu with the right theme.
1202        if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_SUPPORT_ACTION_BAR) &&
1203                mDecorContentParent != null) {
1204            final TypedValue outValue = new TypedValue();
1205            final Resources.Theme baseTheme = context.getTheme();
1206            baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1207
1208            Resources.Theme widgetTheme = null;
1209            if (outValue.resourceId != 0) {
1210                widgetTheme = context.getResources().newTheme();
1211                widgetTheme.setTo(baseTheme);
1212                widgetTheme.applyStyle(outValue.resourceId, true);
1213                widgetTheme.resolveAttribute(
1214                        R.attr.actionBarWidgetTheme, outValue, true);
1215            } else {
1216                baseTheme.resolveAttribute(
1217                        R.attr.actionBarWidgetTheme, outValue, true);
1218            }
1219
1220            if (outValue.resourceId != 0) {
1221                if (widgetTheme == null) {
1222                    widgetTheme = context.getResources().newTheme();
1223                    widgetTheme.setTo(baseTheme);
1224                }
1225                widgetTheme.applyStyle(outValue.resourceId, true);
1226            }
1227
1228            if (widgetTheme != null) {
1229                context = new ContextThemeWrapper(context, 0);
1230                context.getTheme().setTo(widgetTheme);
1231            }
1232        }
1233
1234        final MenuBuilder menu = new MenuBuilder(context);
1235        menu.setCallback(this);
1236        st.setMenu(menu);
1237
1238        return true;
1239    }
1240
1241    private boolean initializePanelContent(PanelFeatureState st) {
1242        if (st.createdPanelView != null) {
1243            st.shownPanelView = st.createdPanelView;
1244            return true;
1245        }
1246
1247        if (st.menu == null) {
1248            return false;
1249        }
1250
1251        if (mPanelMenuPresenterCallback == null) {
1252            mPanelMenuPresenterCallback = new PanelMenuPresenterCallback();
1253        }
1254
1255        MenuView menuView = st.getListMenuView(mPanelMenuPresenterCallback);
1256
1257        st.shownPanelView = (View) menuView;
1258
1259        return st.shownPanelView != null;
1260    }
1261
1262    private boolean preparePanel(PanelFeatureState st, KeyEvent event) {
1263        if (isDestroyed()) {
1264            return false;
1265        }
1266
1267        // Already prepared (isPrepared will be reset to false later)
1268        if (st.isPrepared) {
1269            return true;
1270        }
1271
1272        if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
1273            // Another Panel is prepared and possibly open, so close it
1274            closePanel(mPreparedPanel, false);
1275        }
1276
1277        final Window.Callback cb = getWindowCallback();
1278
1279        if (cb != null) {
1280            st.createdPanelView = cb.onCreatePanelView(st.featureId);
1281        }
1282
1283        final boolean isActionBarMenu =
1284                (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_SUPPORT_ACTION_BAR);
1285
1286        if (isActionBarMenu && mDecorContentParent != null) {
1287            // Enforce ordering guarantees around events so that the action bar never
1288            // dispatches menu-related events before the panel is prepared.
1289            mDecorContentParent.setMenuPrepared();
1290        }
1291
1292        if (st.createdPanelView == null &&
1293                (!isActionBarMenu || !(peekSupportActionBar() instanceof ToolbarActionBar))) {
1294            // Since ToolbarActionBar handles the list options menu itself, we only want to
1295            // init this menu panel if we're not using a TAB.
1296            if (st.menu == null || st.refreshMenuContent) {
1297                if (st.menu == null) {
1298                    if (!initializePanelMenu(st) || (st.menu == null)) {
1299                        return false;
1300                    }
1301                }
1302
1303                if (isActionBarMenu && mDecorContentParent != null) {
1304                    if (mActionMenuPresenterCallback == null) {
1305                        mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
1306                    }
1307                    mDecorContentParent.setMenu(st.menu, mActionMenuPresenterCallback);
1308                }
1309
1310                // Creating the panel menu will involve a lot of manipulation;
1311                // don't dispatch change events to presenters until we're done.
1312                st.menu.stopDispatchingItemsChanged();
1313                if (!cb.onCreatePanelMenu(st.featureId, st.menu)) {
1314                    // Ditch the menu created above
1315                    st.setMenu(null);
1316
1317                    if (isActionBarMenu && mDecorContentParent != null) {
1318                        // Don't show it in the action bar either
1319                        mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
1320                    }
1321
1322                    return false;
1323                }
1324
1325                st.refreshMenuContent = false;
1326            }
1327
1328            // Preparing the panel menu can involve a lot of manipulation;
1329            // don't dispatch change events to presenters until we're done.
1330            st.menu.stopDispatchingItemsChanged();
1331
1332            // Restore action view state before we prepare. This gives apps
1333            // an opportunity to override frozen/restored state in onPrepare.
1334            if (st.frozenActionViewState != null) {
1335                st.menu.restoreActionViewStates(st.frozenActionViewState);
1336                st.frozenActionViewState = null;
1337            }
1338
1339            // Callback and return if the callback does not want to show the menu
1340            if (!cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
1341                if (isActionBarMenu && mDecorContentParent != null) {
1342                    // The app didn't want to show the menu for now but it still exists.
1343                    // Clear it out of the action bar.
1344                    mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
1345                }
1346                st.menu.startDispatchingItemsChanged();
1347                return false;
1348            }
1349
1350            // Set the proper keymap
1351            KeyCharacterMap kmap = KeyCharacterMap.load(
1352                    event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
1353            st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;
1354            st.menu.setQwertyMode(st.qwertyMode);
1355            st.menu.startDispatchingItemsChanged();
1356        }
1357
1358        // Set other state
1359        st.isPrepared = true;
1360        st.isHandled = false;
1361        mPreparedPanel = st;
1362
1363        return true;
1364    }
1365
1366    private void checkCloseActionMenu(MenuBuilder menu) {
1367        if (mClosingActionMenu) {
1368            return;
1369        }
1370
1371        mClosingActionMenu = true;
1372        mDecorContentParent.dismissPopups();
1373        Window.Callback cb = getWindowCallback();
1374        if (cb != null && !isDestroyed()) {
1375            cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, menu);
1376        }
1377        mClosingActionMenu = false;
1378    }
1379
1380    private void closePanel(int featureId) {
1381        closePanel(getPanelState(featureId, true), true);
1382    }
1383
1384    private void closePanel(PanelFeatureState st, boolean doCallback) {
1385        if (doCallback && st.featureId == FEATURE_OPTIONS_PANEL &&
1386                mDecorContentParent != null && mDecorContentParent.isOverflowMenuShowing()) {
1387            checkCloseActionMenu(st.menu);
1388            return;
1389        }
1390
1391        final WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
1392        if (wm != null && st.isOpen && st.decorView != null) {
1393            wm.removeView(st.decorView);
1394
1395            if (doCallback) {
1396                callOnPanelClosed(st.featureId, st, null);
1397            }
1398        }
1399
1400        st.isPrepared = false;
1401        st.isHandled = false;
1402        st.isOpen = false;
1403
1404        // This view is no longer shown, so null it out
1405        st.shownPanelView = null;
1406
1407        // Next time the menu opens, it should not be in expanded mode, so
1408        // force a refresh of the decor
1409        st.refreshDecorView = true;
1410
1411        if (mPreparedPanel == st) {
1412            mPreparedPanel = null;
1413        }
1414    }
1415
1416    private boolean onKeyDownPanel(int featureId, KeyEvent event) {
1417        if (event.getRepeatCount() == 0) {
1418            PanelFeatureState st = getPanelState(featureId, true);
1419            if (!st.isOpen) {
1420                return preparePanel(st, event);
1421            }
1422        }
1423
1424        return false;
1425    }
1426
1427    private boolean onKeyUpPanel(int featureId, KeyEvent event) {
1428        if (mActionMode != null) {
1429            return false;
1430        }
1431
1432        boolean handled = false;
1433        final PanelFeatureState st = getPanelState(featureId, true);
1434        if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
1435                mDecorContentParent.canShowOverflowMenu() &&
1436                !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mContext))) {
1437            if (!mDecorContentParent.isOverflowMenuShowing()) {
1438                if (!isDestroyed() && preparePanel(st, event)) {
1439                    handled = mDecorContentParent.showOverflowMenu();
1440                }
1441            } else {
1442                handled = mDecorContentParent.hideOverflowMenu();
1443            }
1444        } else {
1445            if (st.isOpen || st.isHandled) {
1446                // Play the sound effect if the user closed an open menu (and not if
1447                // they just released a menu shortcut)
1448                handled = st.isOpen;
1449                // Close menu
1450                closePanel(st, true);
1451            } else if (st.isPrepared) {
1452                boolean show = true;
1453                if (st.refreshMenuContent) {
1454                    // Something may have invalidated the menu since we prepared it.
1455                    // Re-prepare it to refresh.
1456                    st.isPrepared = false;
1457                    show = preparePanel(st, event);
1458                }
1459
1460                if (show) {
1461                    // Show menu
1462                    openPanel(st, event);
1463                    handled = true;
1464                }
1465            }
1466        }
1467
1468        if (handled) {
1469            AudioManager audioManager = (AudioManager) mContext.getSystemService(
1470                    Context.AUDIO_SERVICE);
1471            if (audioManager != null) {
1472                audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
1473            } else {
1474                Log.w(TAG, "Couldn't get audio manager");
1475            }
1476        }
1477        return handled;
1478    }
1479
1480    private void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) {
1481        // Try to get a menu
1482        if (menu == null) {
1483            // Need a panel to grab the menu, so try to get that
1484            if (panel == null) {
1485                if ((featureId >= 0) && (featureId < mPanels.length)) {
1486                    panel = mPanels[featureId];
1487                }
1488            }
1489
1490            if (panel != null) {
1491                // menu still may be null, which is okay--we tried our best
1492                menu = panel.menu;
1493            }
1494        }
1495
1496        // If the panel is not open, do not callback
1497        if ((panel != null) && (!panel.isOpen))
1498            return;
1499
1500        if (!isDestroyed()) {
1501            // We need to be careful which callback we dispatch the call to. We can not dispatch
1502            // this to the Window's callback since that will call back into this method and cause a
1503            // crash. Instead we need to dispatch down to the original Activity/Dialog/etc.
1504            mOriginalWindowCallback.onPanelClosed(featureId, menu);
1505        }
1506    }
1507
1508    private PanelFeatureState findMenuPanel(Menu menu) {
1509        final PanelFeatureState[] panels = mPanels;
1510        final int N = panels != null ? panels.length : 0;
1511        for (int i = 0; i < N; i++) {
1512            final PanelFeatureState panel = panels[i];
1513            if (panel != null && panel.menu == menu) {
1514                return panel;
1515            }
1516        }
1517        return null;
1518    }
1519
1520    private PanelFeatureState getPanelState(int featureId, boolean required) {
1521        PanelFeatureState[] ar;
1522        if ((ar = mPanels) == null || ar.length <= featureId) {
1523            PanelFeatureState[] nar = new PanelFeatureState[featureId + 1];
1524            if (ar != null) {
1525                System.arraycopy(ar, 0, nar, 0, ar.length);
1526            }
1527            mPanels = ar = nar;
1528        }
1529
1530        PanelFeatureState st = ar[featureId];
1531        if (st == null) {
1532            ar[featureId] = st = new PanelFeatureState(featureId);
1533        }
1534        return st;
1535    }
1536
1537    private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
1538            int flags) {
1539        if (event.isSystem()) {
1540            return false;
1541        }
1542
1543        boolean handled = false;
1544
1545        // Only try to perform menu shortcuts if preparePanel returned true (possible false
1546        // return value from application not wanting to show the menu).
1547        if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) {
1548            // The menu is prepared now, perform the shortcut on it
1549            handled = st.menu.performShortcut(keyCode, event, flags);
1550        }
1551
1552        if (handled) {
1553            // Only close down the menu if we don't have an action bar keeping it open.
1554            if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) {
1555                closePanel(st, true);
1556            }
1557        }
1558
1559        return handled;
1560    }
1561
1562    private void invalidatePanelMenu(int featureId) {
1563        mInvalidatePanelMenuFeatures |= 1 << featureId;
1564
1565        if (!mInvalidatePanelMenuPosted) {
1566            ViewCompat.postOnAnimation(mWindow.getDecorView(), mInvalidatePanelMenuRunnable);
1567            mInvalidatePanelMenuPosted = true;
1568        }
1569    }
1570
1571    private void doInvalidatePanelMenu(int featureId) {
1572        PanelFeatureState st = getPanelState(featureId, true);
1573        Bundle savedActionViewStates = null;
1574        if (st.menu != null) {
1575            savedActionViewStates = new Bundle();
1576            st.menu.saveActionViewStates(savedActionViewStates);
1577            if (savedActionViewStates.size() > 0) {
1578                st.frozenActionViewState = savedActionViewStates;
1579            }
1580            // This will be started again when the panel is prepared.
1581            st.menu.stopDispatchingItemsChanged();
1582            st.menu.clear();
1583        }
1584        st.refreshMenuContent = true;
1585        st.refreshDecorView = true;
1586
1587        // Prepare the options panel if we have an action bar
1588        if ((featureId == FEATURE_SUPPORT_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)
1589                && mDecorContentParent != null) {
1590            st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1591            if (st != null) {
1592                st.isPrepared = false;
1593                preparePanel(st, null);
1594            }
1595        }
1596    }
1597
1598    /**
1599     * Updates the status bar guard
1600     *
1601     * @param insetTop the current top system window inset
1602     * @return the new top system window inset
1603     */
1604    private int updateStatusGuard(int insetTop) {
1605        boolean showStatusGuard = false;
1606        // Show the status guard when the non-overlay contextual action bar is showing
1607        if (mActionModeView != null) {
1608            if (mActionModeView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
1609                ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)
1610                        mActionModeView.getLayoutParams();
1611                boolean mlpChanged = false;
1612
1613                if (mActionModeView.isShown()) {
1614                    if (mTempRect1 == null) {
1615                        mTempRect1 = new Rect();
1616                        mTempRect2 = new Rect();
1617                    }
1618                    final Rect insets = mTempRect1;
1619                    final Rect localInsets = mTempRect2;
1620                    insets.set(0, insetTop, 0, 0);
1621
1622                    ViewUtils.computeFitSystemWindows(mSubDecor, insets, localInsets);
1623                    final int newMargin = localInsets.top == 0 ? insetTop : 0;
1624                    if (mlp.topMargin != newMargin) {
1625                        mlpChanged = true;
1626                        mlp.topMargin = insetTop;
1627
1628                        if (mStatusGuard == null) {
1629                            mStatusGuard = new View(mContext);
1630                            mStatusGuard.setBackgroundColor(mContext.getResources()
1631                                    .getColor(R.color.abc_input_method_navigation_guard));
1632                            mSubDecor.addView(mStatusGuard, -1,
1633                                    new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1634                                            insetTop));
1635                        } else {
1636                            ViewGroup.LayoutParams lp = mStatusGuard.getLayoutParams();
1637                            if (lp.height != insetTop) {
1638                                lp.height = insetTop;
1639                                mStatusGuard.setLayoutParams(lp);
1640                            }
1641                        }
1642                    }
1643
1644                    // The action mode's theme may differ from the app, so
1645                    // always show the status guard above it.
1646                    showStatusGuard = mStatusGuard != null;
1647
1648                    // We only need to consume the insets if the action
1649                    // mode is overlaid on the app content (e.g. it's
1650                    // sitting in a FrameLayout, see
1651                    // screen_simple_overlay_action_mode.xml).
1652                    if (!mOverlayActionMode && showStatusGuard) {
1653                        insetTop = 0;
1654                    }
1655                } else {
1656                    // reset top margin
1657                    if (mlp.topMargin != 0) {
1658                        mlpChanged = true;
1659                        mlp.topMargin = 0;
1660                    }
1661                }
1662                if (mlpChanged) {
1663                    mActionModeView.setLayoutParams(mlp);
1664                }
1665            }
1666        }
1667        if (mStatusGuard != null) {
1668            mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
1669        }
1670
1671        return insetTop;
1672    }
1673
1674    private void throwFeatureRequestIfSubDecorInstalled() {
1675        if (mSubDecorInstalled) {
1676            throw new AndroidRuntimeException(
1677                    "Window feature must be requested before adding content");
1678        }
1679    }
1680
1681    private int sanitizeWindowFeatureId(int featureId) {
1682        if (featureId == WindowCompat.FEATURE_ACTION_BAR) {
1683            Log.i(TAG, "You should now use the AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR"
1684                    + " id when requesting this feature.");
1685            return FEATURE_SUPPORT_ACTION_BAR;
1686        } else if (featureId == WindowCompat.FEATURE_ACTION_BAR_OVERLAY) {
1687            Log.i(TAG, "You should now use the AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR_OVERLAY"
1688                    + " id when requesting this feature.");
1689            return FEATURE_SUPPORT_ACTION_BAR_OVERLAY;
1690        }
1691        // Else we'll just return the original id
1692        return featureId;
1693    }
1694
1695    ViewGroup getSubDecor() {
1696        return mSubDecor;
1697    }
1698
1699    private void dismissPopups() {
1700        if (mDecorContentParent != null) {
1701            mDecorContentParent.dismissPopups();
1702        }
1703
1704        if (mActionModePopup != null) {
1705            mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
1706            if (mActionModePopup.isShowing()) {
1707                try {
1708                    mActionModePopup.dismiss();
1709                } catch (IllegalArgumentException e) {
1710                    // Pre-v18, there are times when the Window will remove the popup before us.
1711                    // In these cases we need to swallow the resulting exception.
1712                }
1713            }
1714            mActionModePopup = null;
1715        }
1716        endOnGoingFadeAnimation();
1717
1718        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
1719        if (st != null && st.menu != null) {
1720            st.menu.close();
1721        }
1722    }
1723
1724    /**
1725     * Clears out internal reference when the action mode is destroyed.
1726     */
1727    class ActionModeCallbackWrapperV7 implements ActionMode.Callback {
1728        private ActionMode.Callback mWrapped;
1729
1730        public ActionModeCallbackWrapperV7(ActionMode.Callback wrapped) {
1731            mWrapped = wrapped;
1732        }
1733
1734        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
1735            return mWrapped.onCreateActionMode(mode, menu);
1736        }
1737
1738        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
1739            return mWrapped.onPrepareActionMode(mode, menu);
1740        }
1741
1742        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
1743            return mWrapped.onActionItemClicked(mode, item);
1744        }
1745
1746        public void onDestroyActionMode(ActionMode mode) {
1747            mWrapped.onDestroyActionMode(mode);
1748            if (mActionModePopup != null) {
1749                mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
1750            }
1751
1752            if (mActionModeView != null) {
1753                endOnGoingFadeAnimation();
1754                mFadeAnim = ViewCompat.animate(mActionModeView).alpha(0f);
1755                mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
1756                    @Override
1757                    public void onAnimationEnd(View view) {
1758                        mActionModeView.setVisibility(View.GONE);
1759                        if (mActionModePopup != null) {
1760                            mActionModePopup.dismiss();
1761                        } else if (mActionModeView.getParent() instanceof View) {
1762                            ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
1763                        }
1764                        mActionModeView.removeAllViews();
1765                        mFadeAnim.setListener(null);
1766                        mFadeAnim = null;
1767                    }
1768                });
1769            }
1770            if (mAppCompatCallback != null) {
1771                mAppCompatCallback.onSupportActionModeFinished(mActionMode);
1772            }
1773            mActionMode = null;
1774        }
1775    }
1776
1777    private final class PanelMenuPresenterCallback implements MenuPresenter.Callback {
1778        @Override
1779        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1780            final Menu parentMenu = menu.getRootMenu();
1781            final boolean isSubMenu = parentMenu != menu;
1782            final PanelFeatureState panel = findMenuPanel(isSubMenu ? parentMenu : menu);
1783            if (panel != null) {
1784                if (isSubMenu) {
1785                    callOnPanelClosed(panel.featureId, panel, parentMenu);
1786                    closePanel(panel, true);
1787                } else {
1788                    // Close the panel and only do the callback if the menu is being
1789                    // closed completely, not if opening a sub menu
1790                    closePanel(panel, allMenusAreClosing);
1791                }
1792            }
1793        }
1794
1795        @Override
1796        public boolean onOpenSubMenu(MenuBuilder subMenu) {
1797            if (subMenu == null && mHasActionBar) {
1798                Window.Callback cb = getWindowCallback();
1799                if (cb != null && !isDestroyed()) {
1800                    cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, subMenu);
1801                }
1802            }
1803            return true;
1804        }
1805    }
1806
1807    private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
1808        @Override
1809        public boolean onOpenSubMenu(MenuBuilder subMenu) {
1810            Window.Callback cb = getWindowCallback();
1811            if (cb != null) {
1812                cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, subMenu);
1813            }
1814            return true;
1815        }
1816
1817        @Override
1818        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1819            checkCloseActionMenu(menu);
1820        }
1821    }
1822
1823    private static final class PanelFeatureState {
1824
1825        /** Feature ID for this panel. */
1826        int featureId;
1827
1828        int background;
1829
1830        int gravity;
1831
1832        int x;
1833
1834        int y;
1835
1836        int windowAnimations;
1837
1838        /** Dynamic state of the panel. */
1839        ViewGroup decorView;
1840
1841        /** The panel that we are actually showing. */
1842        View shownPanelView;
1843
1844        /** The panel that was returned by onCreatePanelView(). */
1845        View createdPanelView;
1846
1847        /** Use {@link #setMenu} to set this. */
1848        MenuBuilder menu;
1849
1850        ListMenuPresenter listMenuPresenter;
1851
1852        Context listPresenterContext;
1853
1854        /**
1855         * Whether the panel has been prepared (see
1856         * {@link #preparePanel}).
1857         */
1858        boolean isPrepared;
1859
1860        /**
1861         * Whether an item's action has been performed. This happens in obvious
1862         * scenarios (user clicks on menu item), but can also happen with
1863         * chording menu+(shortcut key).
1864         */
1865        boolean isHandled;
1866
1867        boolean isOpen;
1868
1869        public boolean qwertyMode;
1870
1871        boolean refreshDecorView;
1872
1873        boolean refreshMenuContent;
1874
1875        boolean wasLastOpen;
1876
1877        /**
1878         * Contains the state of the menu when told to freeze.
1879         */
1880        Bundle frozenMenuState;
1881
1882        /**
1883         * Contains the state of associated action views when told to freeze.
1884         * These are saved across invalidations.
1885         */
1886        Bundle frozenActionViewState;
1887
1888        PanelFeatureState(int featureId) {
1889            this.featureId = featureId;
1890
1891            refreshDecorView = false;
1892        }
1893
1894        public boolean hasPanelItems() {
1895            if (shownPanelView == null) return false;
1896            if (createdPanelView != null) return true;
1897
1898            return listMenuPresenter.getAdapter().getCount() > 0;
1899        }
1900
1901        /**
1902         * Unregister and free attached MenuPresenters. They will be recreated as needed.
1903         */
1904        public void clearMenuPresenters() {
1905            if (menu != null) {
1906                menu.removeMenuPresenter(listMenuPresenter);
1907            }
1908            listMenuPresenter = null;
1909        }
1910
1911        void setStyle(Context context) {
1912            final TypedValue outValue = new TypedValue();
1913            final Resources.Theme widgetTheme = context.getResources().newTheme();
1914            widgetTheme.setTo(context.getTheme());
1915
1916            // First apply the actionBarPopupTheme
1917            widgetTheme.resolveAttribute(R.attr.actionBarPopupTheme, outValue, true);
1918            if (outValue.resourceId != 0) {
1919                widgetTheme.applyStyle(outValue.resourceId, true);
1920            }
1921
1922            // Now apply the panelMenuListTheme
1923            widgetTheme.resolveAttribute(R.attr.panelMenuListTheme, outValue, true);
1924            if (outValue.resourceId != 0) {
1925                widgetTheme.applyStyle(outValue.resourceId, true);
1926            } else {
1927                widgetTheme.applyStyle(R.style.Theme_AppCompat_CompactMenu, true);
1928            }
1929
1930            context = new ContextThemeWrapper(context, 0);
1931            context.getTheme().setTo(widgetTheme);
1932
1933            listPresenterContext = context;
1934
1935            TypedArray a = context.obtainStyledAttributes(R.styleable.AppCompatTheme);
1936            background = a.getResourceId(
1937                    R.styleable.AppCompatTheme_panelBackground, 0);
1938            windowAnimations = a.getResourceId(
1939                    R.styleable.AppCompatTheme_android_windowAnimationStyle, 0);
1940            a.recycle();
1941        }
1942
1943        void setMenu(MenuBuilder menu) {
1944            if (menu == this.menu) return;
1945
1946            if (this.menu != null) {
1947                this.menu.removeMenuPresenter(listMenuPresenter);
1948            }
1949            this.menu = menu;
1950            if (menu != null) {
1951                if (listMenuPresenter != null) menu.addMenuPresenter(listMenuPresenter);
1952            }
1953        }
1954
1955        MenuView getListMenuView(MenuPresenter.Callback cb) {
1956            if (menu == null) return null;
1957
1958            if (listMenuPresenter == null) {
1959                listMenuPresenter = new ListMenuPresenter(listPresenterContext,
1960                        R.layout.abc_list_menu_item_layout);
1961                listMenuPresenter.setCallback(cb);
1962                menu.addMenuPresenter(listMenuPresenter);
1963            }
1964
1965            MenuView result = listMenuPresenter.getMenuView(decorView);
1966
1967            return result;
1968        }
1969
1970        Parcelable onSaveInstanceState() {
1971            SavedState savedState = new SavedState();
1972            savedState.featureId = featureId;
1973            savedState.isOpen = isOpen;
1974
1975            if (menu != null) {
1976                savedState.menuState = new Bundle();
1977                menu.savePresenterStates(savedState.menuState);
1978            }
1979
1980            return savedState;
1981        }
1982
1983        void onRestoreInstanceState(Parcelable state) {
1984            SavedState savedState = (SavedState) state;
1985            featureId = savedState.featureId;
1986            wasLastOpen = savedState.isOpen;
1987            frozenMenuState = savedState.menuState;
1988
1989            shownPanelView = null;
1990            decorView = null;
1991        }
1992
1993        void applyFrozenState() {
1994            if (menu != null && frozenMenuState != null) {
1995                menu.restorePresenterStates(frozenMenuState);
1996                frozenMenuState = null;
1997            }
1998        }
1999
2000        private static class SavedState implements Parcelable {
2001            int featureId;
2002            boolean isOpen;
2003            Bundle menuState;
2004
2005            public int describeContents() {
2006                return 0;
2007            }
2008
2009            public void writeToParcel(Parcel dest, int flags) {
2010                dest.writeInt(featureId);
2011                dest.writeInt(isOpen ? 1 : 0);
2012
2013                if (isOpen) {
2014                    dest.writeBundle(menuState);
2015                }
2016            }
2017
2018            private static SavedState readFromParcel(Parcel source, ClassLoader loader) {
2019                SavedState savedState = new SavedState();
2020                savedState.featureId = source.readInt();
2021                savedState.isOpen = source.readInt() == 1;
2022
2023                if (savedState.isOpen) {
2024                    savedState.menuState = source.readBundle(loader);
2025                }
2026
2027                return savedState;
2028            }
2029
2030            public static final Parcelable.Creator<SavedState> CREATOR
2031                    = ParcelableCompat.newCreator(
2032                    new ParcelableCompatCreatorCallbacks<SavedState>() {
2033                        @Override
2034                        public SavedState createFromParcel(Parcel in, ClassLoader loader) {
2035                            return readFromParcel(in, loader);
2036                        }
2037
2038                        @Override
2039                        public SavedState[] newArray(int size) {
2040                            return new SavedState[size];
2041                        }
2042                    });
2043        }
2044    }
2045
2046    private class ListMenuDecorView extends ContentFrameLayout {
2047        public ListMenuDecorView(Context context) {
2048            super(context);
2049        }
2050
2051        @Override
2052        public boolean dispatchKeyEvent(KeyEvent event) {
2053            return AppCompatDelegateImplV7.this.dispatchKeyEvent(event)
2054                    || super.dispatchKeyEvent(event);
2055        }
2056
2057        @Override
2058        public boolean onInterceptTouchEvent(MotionEvent event) {
2059            int action = event.getAction();
2060            if (action == MotionEvent.ACTION_DOWN) {
2061                int x = (int) event.getX();
2062                int y = (int) event.getY();
2063                if (isOutOfBounds(x, y)) {
2064                    closePanel(Window.FEATURE_OPTIONS_PANEL);
2065                    return true;
2066                }
2067            }
2068            return super.onInterceptTouchEvent(event);
2069        }
2070
2071        @Override
2072        public void setBackgroundResource(int resid) {
2073            setBackgroundDrawable(AppCompatDrawableManager.get().getDrawable(getContext(), resid));
2074        }
2075
2076        private boolean isOutOfBounds(int x, int y) {
2077            return x < -5 || y < -5 || x > (getWidth() + 5) || y > (getHeight() + 5);
2078        }
2079    }
2080}
2081