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