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