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