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