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