ActionBarActivityDelegateBase.java revision 7efc56b68ff933dfeeac81dd2d8a32096b90f0f4
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.content.Context;
20import android.content.res.Configuration;
21import android.content.res.Resources;
22import android.content.res.TypedArray;
23import android.graphics.Rect;
24import android.os.Build;
25import android.os.Bundle;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.support.annotation.NonNull;
29import android.support.v4.app.NavUtils;
30import android.support.v4.view.OnApplyWindowInsetsListener;
31import android.support.v4.view.ViewCompat;
32import android.support.v4.view.ViewConfigurationCompat;
33import android.support.v4.view.WindowInsetsCompat;
34import android.support.v7.appcompat.R;
35import android.support.v7.internal.VersionUtils;
36import android.support.v7.internal.app.ToolbarActionBar;
37import android.support.v7.internal.app.WindowCallback;
38import android.support.v7.internal.app.WindowDecorActionBar;
39import android.support.v7.internal.view.StandaloneActionMode;
40import android.support.v7.internal.view.menu.ListMenuPresenter;
41import android.support.v7.internal.view.menu.MenuBuilder;
42import android.support.v7.internal.view.menu.MenuPresenter;
43import android.support.v7.internal.view.menu.MenuView;
44import android.support.v7.internal.widget.ActionBarContextView;
45import android.support.v7.internal.widget.DecorContentParent;
46import android.support.v7.internal.widget.FitWindowsViewGroup;
47import android.support.v7.internal.widget.ProgressBarCompat;
48import android.support.v7.internal.widget.TintCheckBox;
49import android.support.v7.internal.widget.TintCheckedTextView;
50import android.support.v7.internal.widget.TintEditText;
51import android.support.v7.internal.widget.TintRadioButton;
52import android.support.v7.internal.widget.TintSpinner;
53import android.support.v7.internal.widget.ViewStubCompat;
54import android.support.v7.internal.widget.ViewUtils;
55import android.support.v7.view.ActionMode;
56import android.support.v7.widget.Toolbar;
57import android.util.AttributeSet;
58import android.util.DisplayMetrics;
59import android.util.TypedValue;
60import android.view.ContextThemeWrapper;
61import android.view.Gravity;
62import android.view.KeyCharacterMap;
63import android.view.KeyEvent;
64import android.view.LayoutInflater;
65import android.view.Menu;
66import android.view.MenuItem;
67import android.view.View;
68import android.view.ViewConfiguration;
69import android.view.ViewGroup;
70import android.view.Window;
71import android.view.accessibility.AccessibilityEvent;
72import android.widget.FrameLayout;
73import android.widget.PopupWindow;
74
75import static android.support.v4.view.WindowCompat.FEATURE_ACTION_BAR;
76import static android.support.v4.view.WindowCompat.FEATURE_ACTION_BAR_OVERLAY;
77import static android.support.v4.view.WindowCompat.FEATURE_ACTION_MODE_OVERLAY;
78import static android.view.Window.FEATURE_OPTIONS_PANEL;
79
80class ActionBarActivityDelegateBase extends ActionBarActivityDelegate
81        implements MenuBuilder.Callback {
82    private static final String TAG = "ActionBarActivityDelegateBase";
83
84    private DecorContentParent mDecorContentParent;
85    private ActionMenuPresenterCallback mActionMenuPresenterCallback;
86    private PanelMenuPresenterCallback mPanelMenuPresenterCallback;
87
88    ActionMode mActionMode;
89    ActionBarContextView mActionModeView;
90    PopupWindow mActionModePopup;
91    Runnable mShowActionModePopup;
92
93    // true if we have installed a window sub-decor layout.
94    private boolean mSubDecorInstalled;
95    private ViewGroup mWindowDecor;
96    private ViewGroup mSubDecor;
97
98    private View mStatusGuard;
99
100    private CharSequence mTitleToSet;
101
102    // Used to keep track of Progress Bar Window features
103    private boolean mFeatureProgress, mFeatureIndeterminateProgress;
104
105    // Used for emulating PanelFeatureState
106    private boolean mClosingActionMenu;
107    private PanelFeatureState[] mPanels;
108    private PanelFeatureState mPreparedPanel;
109
110    private boolean mInvalidatePanelMenuPosted;
111    private int mInvalidatePanelMenuFeatures;
112    private final Runnable mInvalidatePanelMenuRunnable = new Runnable() {
113        @Override
114        public void run() {
115            if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_OPTIONS_PANEL) != 0) {
116                doInvalidatePanelMenu(FEATURE_OPTIONS_PANEL);
117            }
118            if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_ACTION_BAR) != 0) {
119                doInvalidatePanelMenu(FEATURE_ACTION_BAR);
120            }
121            mInvalidatePanelMenuPosted = false;
122            mInvalidatePanelMenuFeatures = 0;
123        }
124    };
125
126    private boolean mEnableDefaultActionBarUp;
127
128    private ListMenuPresenter mToolbarListMenuPresenter;
129
130    private Rect mTempRect1;
131    private Rect mTempRect2;
132
133    ActionBarActivityDelegateBase(ActionBarActivity activity) {
134        super(activity);
135    }
136
137    @Override
138    void onCreate(Bundle savedInstanceState) {
139        super.onCreate(savedInstanceState);
140
141        mWindowDecor = (ViewGroup) mActivity.getWindow().getDecorView();
142
143        if (NavUtils.getParentActivityName(mActivity) != null) {
144            ActionBar ab = getSupportActionBar();
145            if (ab == null) {
146                mEnableDefaultActionBarUp = true;
147            } else {
148                ab.setDefaultDisplayHomeAsUpEnabled(true);
149            }
150        }
151    }
152
153    @Override
154    public ActionBar createSupportActionBar() {
155        ensureSubDecor();
156        ActionBar ab = new WindowDecorActionBar(mActivity, mOverlayActionBar);
157        ab.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
158        return ab;
159    }
160
161    @Override
162    void setSupportActionBar(Toolbar toolbar) {
163        final ActionBar ab = getSupportActionBar();
164        if (ab instanceof WindowDecorActionBar) {
165            throw new IllegalStateException("This Activity already has an action bar supplied " +
166                    "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " +
167                    "windowActionBar to false in your theme to use a Toolbar instead.");
168        } else if (ab instanceof ToolbarActionBar) {
169            // Make sure we reset the old toolbar AB's list menu presenter
170            ((ToolbarActionBar) ab).setListMenuPresenter(null);
171        }
172
173        // Need to make sure we give the action bar the default window callback. Otherwise multiple
174        // setSupportActionBar() calls lead to memory leaks
175        ToolbarActionBar tbab = new ToolbarActionBar(toolbar, mActivity.getTitle(),
176                mActivity.getWindow(), mDefaultWindowCallback);
177        ensureToolbarListMenuPresenter();
178        tbab.setListMenuPresenter(mToolbarListMenuPresenter);
179        setSupportActionBar(tbab);
180        setWindowCallback(tbab.getWrappedWindowCallback());
181        tbab.invalidateOptionsMenu();
182    }
183
184    @Override
185    public void onConfigurationChanged(Configuration newConfig) {
186        // If this is called before sub-decor is installed, ActionBar will not
187        // be properly initialized.
188        if (mHasActionBar && mSubDecorInstalled) {
189            // Note: The action bar will need to access
190            // view changes from superclass.
191            ActionBar ab = getSupportActionBar();
192            if (ab != null) {
193                ab.onConfigurationChanged(newConfig);
194            }
195        }
196    }
197
198    @Override
199    public void onStop() {
200        ActionBar ab = getSupportActionBar();
201        if (ab != null) {
202            ab.setShowHideAnimationEnabled(false);
203        }
204    }
205
206    @Override
207    public void onPostResume() {
208        ActionBar ab = getSupportActionBar();
209        if (ab != null) {
210            ab.setShowHideAnimationEnabled(true);
211        }
212    }
213
214    @Override
215    public void setContentView(View v) {
216        ensureSubDecor();
217        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
218        contentParent.removeAllViews();
219        contentParent.addView(v);
220        mActivity.onSupportContentChanged();
221    }
222
223    @Override
224    public void setContentView(int resId) {
225        ensureSubDecor();
226        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
227        contentParent.removeAllViews();
228        mActivity.getLayoutInflater().inflate(resId, contentParent);
229        mActivity.onSupportContentChanged();
230    }
231
232    @Override
233    public void setContentView(View v, ViewGroup.LayoutParams lp) {
234        ensureSubDecor();
235        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
236        contentParent.removeAllViews();
237        contentParent.addView(v, lp);
238        mActivity.onSupportContentChanged();
239    }
240
241    @Override
242    public void addContentView(View v, ViewGroup.LayoutParams lp) {
243        ensureSubDecor();
244        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
245        contentParent.addView(v, lp);
246        mActivity.onSupportContentChanged();
247    }
248
249    @Override
250    public void onContentChanged() {
251        // Ignore all calls to this method as we call onSupportContentChanged manually above
252    }
253
254    final void ensureSubDecor() {
255        if (!mSubDecorInstalled) {
256            if (mHasActionBar) {
257                /**
258                 * This needs some explanation. As we can not use the android:theme attribute
259                 * pre-L, we emulate it by manually creating a LayoutInflater using a
260                 * ContextThemeWrapper pointing to actionBarTheme.
261                 */
262                TypedValue outValue = new TypedValue();
263                mActivity.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
264
265                Context themedContext;
266                if (outValue.resourceId != 0) {
267                    themedContext = new ContextThemeWrapper(mActivity, outValue.resourceId);
268                } else {
269                    themedContext = mActivity;
270                }
271
272                // Now inflate the view using the themed context and set it as the content view
273                mSubDecor = (ViewGroup) LayoutInflater.from(themedContext)
274                        .inflate(R.layout.abc_screen_toolbar, null);
275
276                mDecorContentParent = (DecorContentParent) mSubDecor
277                        .findViewById(R.id.decor_content_parent);
278                mDecorContentParent.setWindowCallback(getWindowCallback());
279
280                /**
281                 * Propagate features to DecorContentParent
282                 */
283                if (mOverlayActionBar) {
284                    mDecorContentParent.initFeature(FEATURE_ACTION_BAR_OVERLAY);
285                }
286                if (mFeatureProgress) {
287                    mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
288                }
289                if (mFeatureIndeterminateProgress) {
290                    mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
291                }
292            } else {
293                if (mOverlayActionMode) {
294                    mSubDecor = (ViewGroup) LayoutInflater.from(mActivity)
295                            .inflate(R.layout.abc_screen_simple_overlay_action_mode, null);
296                } else {
297                    mSubDecor = (ViewGroup) LayoutInflater.from(mActivity)
298                            .inflate(R.layout.abc_screen_simple, null);
299                }
300
301                if (Build.VERSION.SDK_INT >= 21) {
302                    // If we're running on L or above, we can rely on ViewCompat's
303                    // setOnApplyWindowInsetsListener
304                    ViewCompat.setOnApplyWindowInsetsListener(mSubDecor,
305                            new OnApplyWindowInsetsListener() {
306                                @Override
307                                public WindowInsetsCompat onApplyWindowInsets(View v,
308                                        WindowInsetsCompat insets) {
309                                    final int top = insets.getSystemWindowInsetTop();
310                                    final int newTop = updateStatusGuard(top);
311
312                                    if (top != newTop) {
313                                        return insets.replaceSystemWindowInsets(
314                                                insets.getSystemWindowInsetLeft(),
315                                                newTop,
316                                                insets.getSystemWindowInsetRight(),
317                                                insets.getSystemWindowInsetBottom());
318                                    } else {
319                                        return insets;
320                                    }
321                                }
322                            });
323                } else {
324                    // Else, we need to use our own FitWindowsViewGroup handling
325                    ((FitWindowsViewGroup) mSubDecor).setOnFitSystemWindowsListener(
326                            new FitWindowsViewGroup.OnFitSystemWindowsListener() {
327                                @Override
328                                public void onFitSystemWindows(Rect insets) {
329                                    insets.top = updateStatusGuard(insets.top);
330                                }
331                            });
332                }
333            }
334
335            // Make the decor optionally fit system windows, like the window's decor
336            ViewUtils.makeOptionalFitsSystemWindows(mSubDecor);
337
338            // Now set the Activity's content view with the decor
339            mActivity.superSetContentView(mSubDecor);
340
341            // Change our content FrameLayout to use the android.R.id.content id.
342            // Useful for fragments.
343            final View decorContent = mActivity.findViewById(android.R.id.content);
344            decorContent.setId(View.NO_ID);
345            View abcContent = mActivity.findViewById(R.id.action_bar_activity_content);
346            abcContent.setId(android.R.id.content);
347
348            // The decorContent may have a foreground drawable set (windowContentOverlay).
349            // Remove this as we handle it ourselves
350            if (decorContent instanceof FrameLayout) {
351                ((FrameLayout) decorContent).setForeground(null);
352            }
353
354            // A title was set before we've install the decor so set it now.
355            if (mTitleToSet != null && mDecorContentParent != null) {
356                mDecorContentParent.setWindowTitle(mTitleToSet);
357                mTitleToSet = null;
358            }
359
360            applyFixedSizeWindow();
361
362            onSubDecorInstalled();
363
364            mSubDecorInstalled = true;
365
366            // Invalidate if the panel menu hasn't been created before this.
367            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
368            // being called in the middle of onCreate or similar.
369            // A pending invalidation will typically be resolved before the posted message
370            // would run normally in order to satisfy instance state restoration.
371            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
372            if (!isDestroyed() && (st == null || st.menu == null)) {
373                invalidatePanelMenu(FEATURE_ACTION_BAR);
374            }
375        }
376    }
377
378    void onSubDecorInstalled() {}
379
380    private void applyFixedSizeWindow() {
381        TypedArray a = mActivity.obtainStyledAttributes(R.styleable.Theme);
382
383        TypedValue mFixedWidthMajor = null;
384        TypedValue mFixedWidthMinor = null;
385        TypedValue mFixedHeightMajor = null;
386        TypedValue mFixedHeightMinor = null;
387
388        if (a.hasValue(R.styleable.Theme_windowFixedWidthMajor)) {
389            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
390            a.getValue(R.styleable.Theme_windowFixedWidthMajor, mFixedWidthMajor);
391        }
392        if (a.hasValue(R.styleable.Theme_windowFixedWidthMinor)) {
393            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
394            a.getValue(R.styleable.Theme_windowFixedWidthMinor, mFixedWidthMinor);
395        }
396        if (a.hasValue(R.styleable.Theme_windowFixedHeightMajor)) {
397            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
398            a.getValue(R.styleable.Theme_windowFixedHeightMajor, mFixedHeightMajor);
399        }
400        if (a.hasValue(R.styleable.Theme_windowFixedHeightMinor)) {
401            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
402            a.getValue(R.styleable.Theme_windowFixedHeightMinor, mFixedHeightMinor);
403        }
404
405        final DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics();
406        final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
407        int w = ViewGroup.LayoutParams.MATCH_PARENT;
408        int h = ViewGroup.LayoutParams.MATCH_PARENT;
409
410        final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
411        if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
412            if (tvw.type == TypedValue.TYPE_DIMENSION) {
413                w = (int) tvw.getDimension(metrics);
414            } else if (tvw.type == TypedValue.TYPE_FRACTION) {
415                w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
416            }
417        }
418
419        final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
420        if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
421            if (tvh.type == TypedValue.TYPE_DIMENSION) {
422                h = (int) tvh.getDimension(metrics);
423            } else if (tvh.type == TypedValue.TYPE_FRACTION) {
424                h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
425            }
426        }
427
428        if (w != ViewGroup.LayoutParams.MATCH_PARENT || h != ViewGroup.LayoutParams.MATCH_PARENT) {
429            mActivity.getWindow().setLayout(w, h);
430        }
431
432        a.recycle();
433    }
434
435    @Override
436    public boolean supportRequestWindowFeature(int featureId) {
437        switch (featureId) {
438            case FEATURE_ACTION_BAR:
439                mHasActionBar = true;
440                return true;
441            case FEATURE_ACTION_BAR_OVERLAY:
442                mOverlayActionBar = true;
443                return true;
444            case FEATURE_ACTION_MODE_OVERLAY:
445                mOverlayActionMode = true;
446                return true;
447            case Window.FEATURE_PROGRESS:
448                mFeatureProgress = true;
449                return true;
450            case Window.FEATURE_INDETERMINATE_PROGRESS:
451                mFeatureIndeterminateProgress = true;
452                return true;
453            default:
454                return mActivity.requestWindowFeature(featureId);
455        }
456    }
457
458    @Override
459    public void onTitleChanged(CharSequence title) {
460        if (mDecorContentParent != null) {
461            mDecorContentParent.setWindowTitle(title);
462        } else if (getSupportActionBar() != null) {
463            getSupportActionBar().setWindowTitle(title);
464        } else {
465            mTitleToSet = title;
466        }
467    }
468
469    @Override
470    public View onCreatePanelView(int featureId) {
471        View panelView = null;
472
473        // If there isn't an action mode currently being displayed
474        if (mActionMode == null) {
475            // Let our window callback try first
476            WindowCallback callback = getWindowCallback();
477            if (callback != null) {
478                panelView = callback.onCreatePanelView(featureId);
479            }
480
481            if (panelView == null) {
482                // If the callback didn't return a view, check our panels
483                PanelFeatureState st = getPanelState(featureId, true);
484                openPanel(st, null);
485                if (st.isOpen) {
486                    panelView = st.shownPanelView;
487                }
488            }
489        }
490        return panelView;
491    }
492
493    @Override
494    public boolean onCreatePanelMenu(int featureId, Menu menu) {
495        if (featureId != Window.FEATURE_OPTIONS_PANEL) {
496            return getWindowCallback().onCreatePanelMenu(featureId, menu);
497        }
498        return false;
499    }
500
501    @Override
502    public boolean onPreparePanel(int featureId, View view, Menu menu) {
503        if (featureId != Window.FEATURE_OPTIONS_PANEL) {
504            return getWindowCallback().onPreparePanel(featureId, view, menu);
505        }
506        return false;
507    }
508
509    @Override
510    public void onPanelClosed(final int featureId, Menu menu) {
511        PanelFeatureState st = getPanelState(featureId, false);
512        if (st != null) {
513            // If we know about the feature id, update it's state
514            closePanel(st, false);
515        }
516
517        if (featureId == FEATURE_ACTION_BAR) {
518            ActionBar ab = getSupportActionBar();
519            if (ab != null) {
520                ab.dispatchMenuVisibilityChanged(false);
521            }
522        } else if (!isDestroyed()) {
523            // Only pass it through to the Activity's super impl if it's not ACTION_BAR. This is
524            // because ICS+ will try and create a framework action bar due to this call
525            mActivity.superOnPanelClosed(featureId, menu);
526        }
527    }
528
529    @Override
530    boolean onMenuOpened(final int featureId, Menu menu) {
531        if (featureId == FEATURE_ACTION_BAR) {
532            ActionBar ab = getSupportActionBar();
533            if (ab != null) {
534                ab.dispatchMenuVisibilityChanged(true);
535            }
536            return true;
537        } else {
538            return mActivity.superOnMenuOpened(featureId, menu);
539        }
540    }
541
542    @Override
543    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
544        final WindowCallback cb = getWindowCallback();
545        if (cb != null && !isDestroyed()) {
546            final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
547            if (panel != null) {
548                return cb.onMenuItemSelected(panel.featureId, item);
549            }
550        }
551        return false;
552    }
553
554    @Override
555    public void onMenuModeChange(MenuBuilder menu) {
556        reopenMenu(menu, true);
557    }
558
559    @Override
560    public ActionMode startSupportActionMode(ActionMode.Callback callback) {
561        if (callback == null) {
562            throw new IllegalArgumentException("ActionMode callback can not be null.");
563        }
564
565        if (mActionMode != null) {
566            mActionMode.finish();
567        }
568
569        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
570
571        ActionBar ab = getSupportActionBar();
572        if (ab != null) {
573            mActionMode = ab.startActionMode(wrappedCallback);
574            if (mActionMode != null) {
575                mActivity.onSupportActionModeStarted(mActionMode);
576            }
577        }
578
579        if (mActionMode == null) {
580            // If the action bar didn't provide an action mode, start the emulated window one
581            mActionMode = startSupportActionModeFromWindow(wrappedCallback);
582        }
583
584        return mActionMode;
585    }
586
587    @Override
588    public void supportInvalidateOptionsMenu() {
589        final ActionBar ab = getSupportActionBar();
590        if (ab != null && ab.invalidateOptionsMenu()) return;
591
592        invalidatePanelMenu(FEATURE_OPTIONS_PANEL);
593    }
594
595    @Override
596    ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback) {
597        if (mActionMode != null) {
598            mActionMode.finish();
599        }
600
601        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
602        final Context context = getActionBarThemedContext();
603
604        if (mActionModeView == null) {
605            if (mIsFloating) {
606                mActionModeView = new ActionBarContextView(context);
607                mActionModePopup = new PopupWindow(context, null,
608                        R.attr.actionModePopupWindowStyle);
609                mActionModePopup.setContentView(mActionModeView);
610                mActionModePopup.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
611
612                TypedValue heightValue = new TypedValue();
613                mActivity.getTheme().resolveAttribute(R.attr.actionBarSize, heightValue, true);
614                final int height = TypedValue.complexToDimensionPixelSize(heightValue.data,
615                        mActivity.getResources().getDisplayMetrics());
616                mActionModeView.setContentHeight(height);
617                mActionModePopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
618                mShowActionModePopup = new Runnable() {
619                    public void run() {
620                        mActionModePopup.showAtLocation(
621                                mActionModeView,
622                                Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
623                    }
624                };
625            } else {
626                ViewStubCompat stub = (ViewStubCompat) mActivity
627                        .findViewById(R.id.action_mode_bar_stub);
628                if (stub != null) {
629                    // Set the layout inflater so that it is inflated with the action bar's context
630                    stub.setLayoutInflater(LayoutInflater.from(context));
631                    mActionModeView = (ActionBarContextView) stub.inflate();
632                }
633            }
634        }
635
636        if (mActionModeView != null) {
637            mActionModeView.killMode();
638            ActionMode mode = new StandaloneActionMode(context, mActionModeView, wrappedCallback,
639                    mActionModePopup == null);
640            if (callback.onCreateActionMode(mode, mode.getMenu())) {
641                mode.invalidate();
642                mActionModeView.initForMode(mode);
643                mActionModeView.setVisibility(View.VISIBLE);
644                mActionMode = mode;
645                if (mActionModePopup != null) {
646                    mActivity.getWindow().getDecorView().post(mShowActionModePopup);
647                }
648                mActionModeView.sendAccessibilityEvent(
649                        AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
650
651                if (mActionModeView.getParent() != null) {
652                    ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
653                }
654            } else {
655                mActionMode = null;
656            }
657        }
658        if (mActionMode != null && mActivity != null) {
659            mActivity.onSupportActionModeStarted(mActionMode);
660        }
661        return mActionMode;
662    }
663
664    @Override
665    public boolean onBackPressed() {
666        // Back cancels action modes first.
667        if (mActionMode != null) {
668            mActionMode.finish();
669            return true;
670        }
671
672        // Next collapse any expanded action views.
673        ActionBar ab = getSupportActionBar();
674        if (ab != null && ab.collapseActionView()) {
675            return true;
676        }
677
678        return false;
679    }
680
681    @Override
682    void setSupportProgressBarVisibility(boolean visible) {
683        updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
684                Window.PROGRESS_VISIBILITY_OFF);
685    }
686
687    @Override
688    void setSupportProgressBarIndeterminateVisibility(boolean visible) {
689        updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
690                Window.PROGRESS_VISIBILITY_OFF);
691    }
692
693    @Override
694    void setSupportProgressBarIndeterminate(boolean indeterminate) {
695        updateProgressBars(indeterminate ? Window.PROGRESS_INDETERMINATE_ON
696                : Window.PROGRESS_INDETERMINATE_OFF);
697    }
698
699    @Override
700    void setSupportProgress(int progress) {
701        updateProgressBars(Window.PROGRESS_START + progress);
702    }
703
704    @Override
705    int getHomeAsUpIndicatorAttrId() {
706        return R.attr.homeAsUpIndicator;
707    }
708
709    @Override
710    boolean onKeyShortcut(int keyCode, KeyEvent ev) {
711        // If the panel is already prepared, then perform the shortcut using it.
712        boolean handled;
713        if (mPreparedPanel != null) {
714            handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev,
715                    Menu.FLAG_PERFORM_NO_CLOSE);
716            if (handled) {
717                if (mPreparedPanel != null) {
718                    mPreparedPanel.isHandled = true;
719                }
720                return true;
721            }
722        }
723
724        // If the panel is not prepared, then we may be trying to handle a shortcut key
725        // combination such as Control+C.  Temporarily prepare the panel then mark it
726        // unprepared again when finished to ensure that the panel will again be prepared
727        // the next time it is shown for real.
728        if (mPreparedPanel == null) {
729            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
730            preparePanel(st, ev);
731            handled = performPanelShortcut(st, ev.getKeyCode(), ev, Menu.FLAG_PERFORM_NO_CLOSE);
732            st.isPrepared = false;
733            if (handled) {
734                return true;
735            }
736        }
737        return false;
738    }
739
740    @Override
741    boolean onKeyDown(int keyCode, KeyEvent event) {
742        // On API v7-10 we need to manually call onKeyShortcut() as this is not called
743        // from the Activity
744        return onKeyShortcut(keyCode, event);
745    }
746
747    @Override
748    View createView(final String name, @NonNull AttributeSet attrs) {
749        if (Build.VERSION.SDK_INT < 21) {
750            // If we're running pre-L, we need to 'inject' our tint aware Views in place of the
751            // standard framework versions
752            switch (name) {
753                case "EditText":
754                    return new TintEditText(mActivity, attrs);
755                case "Spinner":
756                    return new TintSpinner(mActivity, attrs);
757                case "CheckBox":
758                    return new TintCheckBox(mActivity, attrs);
759                case "RadioButton":
760                    return new TintRadioButton(mActivity, attrs);
761                case "CheckedTextView":
762                    return new TintCheckedTextView(mActivity, attrs);
763            }
764        }
765        return null;
766    }
767
768    /**
769     * Progress Bar function. Mostly extracted from PhoneWindow.java
770     */
771    private void updateProgressBars(int value) {
772        ProgressBarCompat circularProgressBar = getCircularProgressBar();
773        ProgressBarCompat horizontalProgressBar = getHorizontalProgressBar();
774
775        if (value == Window.PROGRESS_VISIBILITY_ON) {
776            if (mFeatureProgress) {
777                int level = horizontalProgressBar.getProgress();
778                int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
779                        View.VISIBLE : View.INVISIBLE;
780                horizontalProgressBar.setVisibility(visibility);
781            }
782            if (mFeatureIndeterminateProgress) {
783                circularProgressBar.setVisibility(View.VISIBLE);
784            }
785        } else if (value == Window.PROGRESS_VISIBILITY_OFF) {
786            if (mFeatureProgress) {
787                horizontalProgressBar.setVisibility(View.GONE);
788            }
789            if (mFeatureIndeterminateProgress) {
790                circularProgressBar.setVisibility(View.GONE);
791            }
792        } else if (value == Window.PROGRESS_INDETERMINATE_ON) {
793            horizontalProgressBar.setIndeterminate(true);
794        } else if (value == Window.PROGRESS_INDETERMINATE_OFF) {
795            horizontalProgressBar.setIndeterminate(false);
796        } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) {
797            // We want to set the progress value before testing for visibility
798            // so that when the progress bar becomes visible again, it has the
799            // correct level.
800            horizontalProgressBar.setProgress(value - Window.PROGRESS_START);
801
802            if (value < Window.PROGRESS_END) {
803                showProgressBars(horizontalProgressBar, circularProgressBar);
804            } else {
805                hideProgressBars(horizontalProgressBar, circularProgressBar);
806            }
807        }
808    }
809
810    private void openPanel(int featureId, KeyEvent event) {
811        if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
812                mDecorContentParent.canShowOverflowMenu() &&
813                !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity))) {
814            mDecorContentParent.showOverflowMenu();
815        } else {
816            openPanel(getPanelState(featureId, true), event);
817        }
818    }
819
820    private void openPanel(final PanelFeatureState st, KeyEvent event) {
821        // Already open, return
822        if (st.isOpen || isDestroyed()) {
823            return;
824        }
825
826        // Don't open an options panel for honeycomb apps on xlarge devices.
827        // (The app should be using an action bar for menu items.)
828        if (st.featureId == FEATURE_OPTIONS_PANEL) {
829            Context context = mActivity;
830            Configuration config = context.getResources().getConfiguration();
831            boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) ==
832                    Configuration.SCREENLAYOUT_SIZE_XLARGE;
833            boolean isHoneycombApp = context.getApplicationInfo().targetSdkVersion >=
834                    android.os.Build.VERSION_CODES.HONEYCOMB;
835
836            if (isXLarge && isHoneycombApp) {
837                return;
838            }
839        }
840
841        WindowCallback cb = getWindowCallback();
842        if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) {
843            // Callback doesn't want the menu to open, reset any state
844            closePanel(st, true);
845            return;
846        }
847
848        // Prepare panel (should have been done before, but just in case)
849        if (!preparePanel(st, event)) {
850            return;
851        }
852
853        if (st.decorView == null || st.refreshDecorView) {
854            initializePanelDecor(st);
855        }
856
857        // This will populate st.shownPanelView
858        if (!initializePanelContent(st) || !st.hasPanelItems()) {
859            return;
860        }
861
862        st.isHandled = false;
863        st.isOpen = true;
864    }
865
866    private void initializePanelDecor(PanelFeatureState st) {
867        st.decorView = mWindowDecor;
868        st.setStyle(getActionBarThemedContext());
869    }
870
871    private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
872        if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() &&
873                (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity)) ||
874                        mDecorContentParent.isOverflowMenuShowPending())) {
875
876            WindowCallback cb = getWindowCallback();
877
878            if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {
879                if (cb != null && !isDestroyed()) {
880                    // If we have a menu invalidation pending, do it now.
881                    if (mInvalidatePanelMenuPosted &&
882                            (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) {
883                        mWindowDecor.removeCallbacks(mInvalidatePanelMenuRunnable);
884                        mInvalidatePanelMenuRunnable.run();
885                    }
886
887                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
888
889                    // If we don't have a menu or we're waiting for a full content refresh,
890                    // forget it. This is a lingering event that no longer matters.
891                    if (st.menu != null && !st.refreshMenuContent &&
892                            cb.onPreparePanel(FEATURE_OPTIONS_PANEL, null, st.menu)) {
893                        cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);
894                        mDecorContentParent.showOverflowMenu();
895                    }
896                }
897            } else {
898                mDecorContentParent.hideOverflowMenu();
899                if (!isDestroyed()) {
900                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
901                    mActivity.onPanelClosed(FEATURE_ACTION_BAR, st.menu);
902                }
903            }
904            return;
905        }
906
907        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
908
909        st.refreshDecorView = true;
910        closePanel(st, false);
911
912        openPanel(st, null);
913    }
914
915    private void showProgressBars(ProgressBarCompat horizontalProgressBar,
916            ProgressBarCompat spinnyProgressBar) {
917        if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.INVISIBLE) {
918            spinnyProgressBar.setVisibility(View.VISIBLE);
919        }
920        // Only show the progress bars if the primary progress is not complete
921        if (mFeatureProgress && horizontalProgressBar.getProgress() < 10000) {
922            horizontalProgressBar.setVisibility(View.VISIBLE);
923        }
924    }
925
926    private void hideProgressBars(ProgressBarCompat horizontalProgressBar,
927            ProgressBarCompat spinnyProgressBar) {
928        if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.VISIBLE) {
929            spinnyProgressBar.setVisibility(View.INVISIBLE);
930        }
931        if (mFeatureProgress && horizontalProgressBar.getVisibility() == View.VISIBLE) {
932            horizontalProgressBar.setVisibility(View.INVISIBLE);
933        }
934    }
935
936    private ProgressBarCompat getCircularProgressBar() {
937        ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_circular);
938        if (pb != null) {
939            pb.setVisibility(View.INVISIBLE);
940        }
941        return pb;
942    }
943
944    private ProgressBarCompat getHorizontalProgressBar() {
945        ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_horizontal);
946        if (pb != null) {
947            pb.setVisibility(View.INVISIBLE);
948        }
949        return pb;
950    }
951
952    private boolean initializePanelMenu(final PanelFeatureState st) {
953        Context context = mActivity;
954
955        // If we have an action bar, initialize the menu with the right theme.
956        if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR) &&
957                mDecorContentParent != null) {
958            final TypedValue outValue = new TypedValue();
959            final Resources.Theme baseTheme = context.getTheme();
960            baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
961
962            Resources.Theme widgetTheme = null;
963            if (outValue.resourceId != 0) {
964                widgetTheme = context.getResources().newTheme();
965                widgetTheme.setTo(baseTheme);
966                widgetTheme.applyStyle(outValue.resourceId, true);
967                widgetTheme.resolveAttribute(
968                        R.attr.actionBarWidgetTheme, outValue, true);
969            } else {
970                baseTheme.resolveAttribute(
971                        R.attr.actionBarWidgetTheme, outValue, true);
972            }
973
974            if (outValue.resourceId != 0) {
975                if (widgetTheme == null) {
976                    widgetTheme = context.getResources().newTheme();
977                    widgetTheme.setTo(baseTheme);
978                }
979                widgetTheme.applyStyle(outValue.resourceId, true);
980            }
981
982            if (widgetTheme != null) {
983                context = new ContextThemeWrapper(context, 0);
984                context.getTheme().setTo(widgetTheme);
985            }
986        }
987
988        final MenuBuilder menu = new MenuBuilder(context);
989        menu.setCallback(this);
990        st.setMenu(menu);
991
992        return true;
993    }
994
995    private boolean initializePanelContent(PanelFeatureState st) {
996        if (st.menu == null) {
997            return false;
998        }
999
1000        if (mPanelMenuPresenterCallback == null) {
1001            mPanelMenuPresenterCallback = new PanelMenuPresenterCallback();
1002        }
1003
1004        MenuView menuView = st.getListMenuView(mPanelMenuPresenterCallback);
1005
1006        st.shownPanelView = (View) menuView;
1007
1008        return st.shownPanelView != null;
1009    }
1010
1011    private boolean preparePanel(PanelFeatureState st, KeyEvent event) {
1012        if (isDestroyed()) {
1013            return false;
1014        }
1015
1016        // Already prepared (isPrepared will be reset to false later)
1017        if (st.isPrepared) {
1018            return true;
1019        }
1020
1021        if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
1022            // Another Panel is prepared and possibly open, so close it
1023            closePanel(mPreparedPanel, false);
1024        }
1025
1026        final boolean isActionBarMenu =
1027                (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR);
1028
1029        if (isActionBarMenu && mDecorContentParent != null) {
1030            // Enforce ordering guarantees around events so that the action bar never
1031            // dispatches menu-related events before the panel is prepared.
1032            mDecorContentParent.setMenuPrepared();
1033        }
1034
1035        // Init the panel state's menu--return false if init failed
1036        if (st.menu == null || st.refreshMenuContent) {
1037            if (st.menu == null) {
1038                if (!initializePanelMenu(st) || (st.menu == null)) {
1039                    return false;
1040                }
1041            }
1042
1043            if (isActionBarMenu && mDecorContentParent != null) {
1044                if (mActionMenuPresenterCallback == null) {
1045                    mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
1046                }
1047                mDecorContentParent.setMenu(st.menu, mActionMenuPresenterCallback);
1048            }
1049
1050            // Creating the panel menu will involve a lot of manipulation;
1051            // don't dispatch change events to presenters until we're done.
1052            st.menu.stopDispatchingItemsChanged();
1053            if (!getWindowCallback().onCreatePanelMenu(st.featureId, st.menu)) {
1054                // Ditch the menu created above
1055                st.setMenu(null);
1056
1057                if (isActionBarMenu && mDecorContentParent != null) {
1058                    // Don't show it in the action bar either
1059                    mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
1060                }
1061
1062                return false;
1063            }
1064
1065            st.refreshMenuContent = false;
1066        }
1067
1068        // Preparing the panel menu can involve a lot of manipulation;
1069        // don't dispatch change events to presenters until we're done.
1070        st.menu.stopDispatchingItemsChanged();
1071
1072        // Restore action view state before we prepare. This gives apps
1073        // an opportunity to override frozen/restored state in onPrepare.
1074        if (st.frozenActionViewState != null) {
1075            st.menu.restoreActionViewStates(st.frozenActionViewState);
1076            st.frozenActionViewState = null;
1077        }
1078
1079        // Callback and return if the callback does not want to show the menu
1080        if (!getWindowCallback().onPreparePanel(FEATURE_OPTIONS_PANEL, null, st.menu)) {
1081            if (isActionBarMenu && mDecorContentParent != null) {
1082                // The app didn't want to show the menu for now but it still exists.
1083                // Clear it out of the action bar.
1084                mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
1085            }
1086            st.menu.startDispatchingItemsChanged();
1087            return false;
1088        }
1089
1090        // Set the proper keymap
1091        KeyCharacterMap kmap = KeyCharacterMap.load(
1092                event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
1093        st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;
1094        st.menu.setQwertyMode(st.qwertyMode);
1095        st.menu.startDispatchingItemsChanged();
1096
1097        // Set other state
1098        st.isPrepared = true;
1099        st.isHandled = false;
1100        mPreparedPanel = st;
1101
1102        return true;
1103    }
1104
1105    private void checkCloseActionMenu(MenuBuilder menu) {
1106        if (mClosingActionMenu) {
1107            return;
1108        }
1109
1110        mClosingActionMenu = true;
1111        mDecorContentParent.dismissPopups();
1112        WindowCallback cb = getWindowCallback();
1113        if (cb != null && !isDestroyed()) {
1114            cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
1115        }
1116        mClosingActionMenu = false;
1117    }
1118
1119    private void closePanel(PanelFeatureState st, boolean doCallback) {
1120        if (doCallback && st.featureId == FEATURE_OPTIONS_PANEL &&
1121                mDecorContentParent != null && mDecorContentParent.isOverflowMenuShowing()) {
1122            checkCloseActionMenu(st.menu);
1123            return;
1124        }
1125
1126        if (st.isOpen) {
1127            if (doCallback) {
1128                callOnPanelClosed(st.featureId, st, null);
1129            }
1130        }
1131
1132        st.isPrepared = false;
1133        st.isHandled = false;
1134        st.isOpen = false;
1135
1136        // This view is no longer shown, so null it out
1137        st.shownPanelView = null;
1138
1139        // Next time the menu opens, it should not be in expanded mode, so
1140        // force a refresh of the decor
1141        st.refreshDecorView = true;
1142
1143        if (mPreparedPanel == st) {
1144            mPreparedPanel = null;
1145        }
1146    }
1147
1148    private void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) {
1149        // Try to get a menu
1150        if (menu == null) {
1151            // Need a panel to grab the menu, so try to get that
1152            if (panel == null) {
1153                if ((featureId >= 0) && (featureId < mPanels.length)) {
1154                    panel = mPanels[featureId];
1155                }
1156            }
1157
1158            if (panel != null) {
1159                // menu still may be null, which is okay--we tried our best
1160                menu = panel.menu;
1161            }
1162        }
1163
1164        // If the panel is not open, do not callback
1165        if ((panel != null) && (!panel.isOpen))
1166            return;
1167
1168        getWindowCallback().onPanelClosed(featureId, menu);
1169    }
1170
1171    private PanelFeatureState findMenuPanel(Menu menu) {
1172        final PanelFeatureState[] panels = mPanels;
1173        final int N = panels != null ? panels.length : 0;
1174        for (int i = 0; i < N; i++) {
1175            final PanelFeatureState panel = panels[i];
1176            if (panel != null && panel.menu == menu) {
1177                return panel;
1178            }
1179        }
1180        return null;
1181    }
1182
1183    private PanelFeatureState getPanelState(int featureId, boolean required) {
1184        PanelFeatureState[] ar;
1185        if ((ar = mPanels) == null || ar.length <= featureId) {
1186            PanelFeatureState[] nar = new PanelFeatureState[featureId + 1];
1187            if (ar != null) {
1188                System.arraycopy(ar, 0, nar, 0, ar.length);
1189            }
1190            mPanels = ar = nar;
1191        }
1192
1193        PanelFeatureState st = ar[featureId];
1194        if (st == null) {
1195            ar[featureId] = st = new PanelFeatureState(featureId);
1196        }
1197        return st;
1198    }
1199
1200    final boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
1201            int flags) {
1202        if (event.isSystem()) {
1203            return false;
1204        }
1205
1206        boolean handled = false;
1207
1208        // Only try to perform menu shortcuts if preparePanel returned true (possible false
1209        // return value from application not wanting to show the menu).
1210        if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) {
1211            // The menu is prepared now, perform the shortcut on it
1212            handled = st.menu.performShortcut(keyCode, event, flags);
1213        }
1214
1215        if (handled) {
1216            // Only close down the menu if we don't have an action bar keeping it open.
1217            if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) {
1218                closePanel(st, true);
1219            }
1220        }
1221
1222        return handled;
1223    }
1224
1225    private void invalidatePanelMenu(int featureId) {
1226        mInvalidatePanelMenuFeatures |= 1 << featureId;
1227
1228        if (!mInvalidatePanelMenuPosted && mWindowDecor != null) {
1229            ViewCompat.postOnAnimation(mWindowDecor, mInvalidatePanelMenuRunnable);
1230            mInvalidatePanelMenuPosted = true;
1231        }
1232    }
1233
1234    private void doInvalidatePanelMenu(int featureId) {
1235        PanelFeatureState st = getPanelState(featureId, true);
1236        Bundle savedActionViewStates = null;
1237        if (st.menu != null) {
1238            savedActionViewStates = new Bundle();
1239            st.menu.saveActionViewStates(savedActionViewStates);
1240            if (savedActionViewStates.size() > 0) {
1241                st.frozenActionViewState = savedActionViewStates;
1242            }
1243            // This will be started again when the panel is prepared.
1244            st.menu.stopDispatchingItemsChanged();
1245            st.menu.clear();
1246        }
1247        st.refreshMenuContent = true;
1248        st.refreshDecorView = true;
1249
1250        // Prepare the options panel if we have an action bar
1251        if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)
1252                && mDecorContentParent != null) {
1253            st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1254            if (st != null) {
1255                st.isPrepared = false;
1256                preparePanel(st, null);
1257            }
1258        }
1259    }
1260
1261    /**
1262     * Updates the status bar guard
1263     *
1264     * @param insetTop the current top system window inset
1265     * @return the new top system window inset
1266     */
1267    private int updateStatusGuard(int insetTop) {
1268        boolean showStatusGuard = false;
1269        // Show the status guard when the non-overlay contextual action bar is showing
1270        if (mActionModeView != null) {
1271            if (mActionModeView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
1272                ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)
1273                        mActionModeView.getLayoutParams();
1274                boolean mlpChanged = false;
1275
1276                if (mActionModeView.isShown()) {
1277                    if (mTempRect1 == null) {
1278                        mTempRect1 = new Rect();
1279                        mTempRect2 = new Rect();
1280                    }
1281                    final Rect insets = mTempRect1;
1282                    final Rect localInsets = mTempRect2;
1283                    insets.set(0, insetTop, 0, 0);
1284
1285                    ViewUtils.computeFitSystemWindows(mSubDecor, insets, localInsets);
1286                    final int newMargin = localInsets.top == 0 ? insetTop : 0;
1287                    if (mlp.topMargin != newMargin) {
1288                        mlpChanged = true;
1289                        mlp.topMargin = insetTop;
1290
1291                        if (mStatusGuard == null) {
1292                            mStatusGuard = new View(mActivity);
1293                            mStatusGuard.setBackgroundColor(mActivity.getResources()
1294                                    .getColor(R.color.abc_input_method_navigation_guard));
1295                            mSubDecor.addView(mStatusGuard, -1,
1296                                    new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1297                                            insetTop));
1298                        } else {
1299                            ViewGroup.LayoutParams lp = mStatusGuard.getLayoutParams();
1300                            if (lp.height != insetTop) {
1301                                lp.height = insetTop;
1302                                mStatusGuard.setLayoutParams(lp);
1303                            }
1304                        }
1305                    }
1306
1307                    // We only need to consume the insets if the action
1308                    // mode is overlaid on the app content (e.g. it's
1309                    // sitting in a FrameLayout, see
1310                    // screen_simple_overlay_action_mode.xml).
1311                    if (!mOverlayActionMode) {
1312                        insetTop = 0;
1313                    }
1314
1315                    // The action mode's theme may differ from the app, so
1316                    // always show the status guard above it.
1317                    showStatusGuard = true;
1318                } else {
1319                    // reset top margin
1320                    if (mlp.topMargin != 0) {
1321                        mlpChanged = true;
1322                        mlp.topMargin = 0;
1323                    }
1324                }
1325                if (mlpChanged) {
1326                    mActionModeView.setLayoutParams(mlp);
1327                }
1328            }
1329        }
1330        if (mStatusGuard != null) {
1331            mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
1332        }
1333
1334        return insetTop;
1335    }
1336
1337    private void ensureToolbarListMenuPresenter() {
1338        if (mToolbarListMenuPresenter == null) {
1339            // First resolve panelMenuListTheme
1340            TypedValue outValue = new TypedValue();
1341            mActivity.getTheme().resolveAttribute(R.attr.panelMenuListTheme, outValue, true);
1342
1343            Context context = new ContextThemeWrapper(mActivity,
1344                    outValue.resourceId != 0
1345                            ? outValue.resourceId
1346                            : R.style.Theme_AppCompat_CompactMenu);
1347
1348            mToolbarListMenuPresenter = new ListMenuPresenter(context,
1349                    R.layout.abc_list_menu_item_layout);
1350        }
1351    }
1352
1353    /**
1354     * Clears out internal reference when the action mode is destroyed.
1355     */
1356    private class ActionModeCallbackWrapper implements ActionMode.Callback {
1357        private ActionMode.Callback mWrapped;
1358
1359        public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
1360            mWrapped = wrapped;
1361        }
1362
1363        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
1364            return mWrapped.onCreateActionMode(mode, menu);
1365        }
1366
1367        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
1368            return mWrapped.onPrepareActionMode(mode, menu);
1369        }
1370
1371        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
1372            return mWrapped.onActionItemClicked(mode, item);
1373        }
1374
1375        public void onDestroyActionMode(ActionMode mode) {
1376            mWrapped.onDestroyActionMode(mode);
1377            if (mActionModePopup != null) {
1378                mActivity.getWindow().getDecorView().removeCallbacks(mShowActionModePopup);
1379                mActionModePopup.dismiss();
1380            } else if (mActionModeView != null) {
1381                mActionModeView.setVisibility(View.GONE);
1382                if (mActionModeView.getParent() != null) {
1383                    ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
1384                }
1385            }
1386            if (mActionModeView != null) {
1387                mActionModeView.removeAllViews();
1388            }
1389            if (mActivity != null) {
1390                try {
1391                    mActivity.onSupportActionModeFinished(mActionMode);
1392                } catch (AbstractMethodError ame) {
1393                    // Older apps might not implement this callback method.
1394                }
1395            }
1396            mActionMode = null;
1397        }
1398    }
1399
1400    private final class PanelMenuPresenterCallback implements MenuPresenter.Callback {
1401        @Override
1402        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1403            final Menu parentMenu = menu.getRootMenu();
1404            final boolean isSubMenu = parentMenu != menu;
1405            final PanelFeatureState panel = findMenuPanel(isSubMenu ? parentMenu : menu);
1406            if (panel != null) {
1407                if (isSubMenu) {
1408                    callOnPanelClosed(panel.featureId, panel, parentMenu);
1409                    closePanel(panel, true);
1410                } else {
1411                    // Close the panel and only do the callback if the menu is being
1412                    // closed completely, not if opening a sub menu
1413                    mActivity.closeOptionsMenu();
1414                    closePanel(panel, allMenusAreClosing);
1415                }
1416            }
1417        }
1418
1419        @Override
1420        public boolean onOpenSubMenu(MenuBuilder subMenu) {
1421            if (subMenu == null && mHasActionBar) {
1422                WindowCallback cb = getWindowCallback();
1423                if (cb != null && !isDestroyed()) {
1424                    cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
1425                }
1426            }
1427            return true;
1428        }
1429    }
1430
1431    private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
1432        @Override
1433        public boolean onOpenSubMenu(MenuBuilder subMenu) {
1434            WindowCallback cb = getWindowCallback();
1435            if (cb != null) {
1436                cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
1437            }
1438            return true;
1439        }
1440
1441        @Override
1442        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1443            checkCloseActionMenu(menu);
1444        }
1445    }
1446
1447    private static final class PanelFeatureState {
1448
1449        /** Feature ID for this panel. */
1450        int featureId;
1451
1452        /** Dynamic state of the panel. */
1453        ViewGroup decorView;
1454
1455        /** The panel that we are actually showing. */
1456        View shownPanelView;
1457
1458        /** Use {@link #setMenu} to set this. */
1459        MenuBuilder menu;
1460
1461        ListMenuPresenter listMenuPresenter;
1462
1463        Context listPresenterContext;
1464
1465        /**
1466         * Whether the panel has been prepared (see
1467         * {@link #preparePanel}).
1468         */
1469        boolean isPrepared;
1470
1471        /**
1472         * Whether an item's action has been performed. This happens in obvious
1473         * scenarios (user clicks on menu item), but can also happen with
1474         * chording menu+(shortcut key).
1475         */
1476        boolean isHandled;
1477
1478        boolean isOpen;
1479
1480        public boolean qwertyMode;
1481
1482        boolean refreshDecorView;
1483
1484        boolean refreshMenuContent;
1485
1486        boolean wasLastOpen;
1487
1488        /**
1489         * Contains the state of the menu when told to freeze.
1490         */
1491        Bundle frozenMenuState;
1492
1493        /**
1494         * Contains the state of associated action views when told to freeze.
1495         * These are saved across invalidations.
1496         */
1497        Bundle frozenActionViewState;
1498
1499        PanelFeatureState(int featureId) {
1500            this.featureId = featureId;
1501
1502            refreshDecorView = false;
1503        }
1504
1505        public boolean hasPanelItems() {
1506            if (shownPanelView == null) return false;
1507
1508            return listMenuPresenter.getAdapter().getCount() > 0;
1509        }
1510
1511        /**
1512         * Unregister and free attached MenuPresenters. They will be recreated as needed.
1513         */
1514        public void clearMenuPresenters() {
1515            if (menu != null) {
1516                menu.removeMenuPresenter(listMenuPresenter);
1517            }
1518            listMenuPresenter = null;
1519        }
1520
1521        void setStyle(Context context) {
1522            final TypedValue outValue = new TypedValue();
1523            final Resources.Theme widgetTheme = context.getResources().newTheme();
1524            widgetTheme.setTo(context.getTheme());
1525
1526            // First apply the actionBarPopupTheme
1527            widgetTheme.resolveAttribute(R.attr.actionBarPopupTheme, outValue, true);
1528            if (outValue.resourceId != 0) {
1529                widgetTheme.applyStyle(outValue.resourceId, true);
1530            }
1531
1532            // Now apply the panelMenuListTheme
1533            widgetTheme.resolveAttribute(R.attr.panelMenuListTheme, outValue, true);
1534            if (outValue.resourceId != 0) {
1535                widgetTheme.applyStyle(outValue.resourceId, true);
1536            } else {
1537                widgetTheme.applyStyle(R.style.Theme_AppCompat_CompactMenu, true);
1538            }
1539
1540            context = new ContextThemeWrapper(context, 0);
1541            context.getTheme().setTo(widgetTheme);
1542
1543            listPresenterContext = context;
1544        }
1545
1546        void setMenu(MenuBuilder menu) {
1547            if (menu == this.menu) return;
1548
1549            if (this.menu != null) {
1550                this.menu.removeMenuPresenter(listMenuPresenter);
1551            }
1552            this.menu = menu;
1553            if (menu != null) {
1554                if (listMenuPresenter != null) menu.addMenuPresenter(listMenuPresenter);
1555            }
1556        }
1557
1558        MenuView getListMenuView(MenuPresenter.Callback cb) {
1559            if (menu == null) return null;
1560
1561            if (listMenuPresenter == null) {
1562                listMenuPresenter = new ListMenuPresenter(listPresenterContext,
1563                        R.layout.abc_list_menu_item_layout);
1564                listMenuPresenter.setCallback(cb);
1565                menu.addMenuPresenter(listMenuPresenter);
1566            }
1567
1568            MenuView result = listMenuPresenter.getMenuView(decorView);
1569
1570            return result;
1571        }
1572
1573        Parcelable onSaveInstanceState() {
1574            SavedState savedState = new SavedState();
1575            savedState.featureId = featureId;
1576            savedState.isOpen = isOpen;
1577
1578            if (menu != null) {
1579                savedState.menuState = new Bundle();
1580                menu.savePresenterStates(savedState.menuState);
1581            }
1582
1583            return savedState;
1584        }
1585
1586        void onRestoreInstanceState(Parcelable state) {
1587            SavedState savedState = (SavedState) state;
1588            featureId = savedState.featureId;
1589            wasLastOpen = savedState.isOpen;
1590            frozenMenuState = savedState.menuState;
1591
1592            shownPanelView = null;
1593            decorView = null;
1594        }
1595
1596        void applyFrozenState() {
1597            if (menu != null && frozenMenuState != null) {
1598                menu.restorePresenterStates(frozenMenuState);
1599                frozenMenuState = null;
1600            }
1601        }
1602
1603        private static class SavedState implements Parcelable {
1604            int featureId;
1605            boolean isOpen;
1606            Bundle menuState;
1607
1608            public int describeContents() {
1609                return 0;
1610            }
1611
1612            public void writeToParcel(Parcel dest, int flags) {
1613                dest.writeInt(featureId);
1614                dest.writeInt(isOpen ? 1 : 0);
1615
1616                if (isOpen) {
1617                    dest.writeBundle(menuState);
1618                }
1619            }
1620
1621            private static SavedState readFromParcel(Parcel source) {
1622                SavedState savedState = new SavedState();
1623                savedState.featureId = source.readInt();
1624                savedState.isOpen = source.readInt() == 1;
1625
1626                if (savedState.isOpen) {
1627                    savedState.menuState = source.readBundle();
1628                }
1629
1630                return savedState;
1631            }
1632
1633            public static final Parcelable.Creator<SavedState> CREATOR
1634                    = new Parcelable.Creator<SavedState>() {
1635                public SavedState createFromParcel(Parcel in) {
1636                    return readFromParcel(in);
1637                }
1638
1639                public SavedState[] newArray(int size) {
1640                    return new SavedState[size];
1641                }
1642            };
1643        }
1644    }
1645
1646}
1647