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