[go: nahoru, domu]

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