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