ActionBarActivityDelegateBase.java revision dea1122145ed85590d298a3a9d88ba9f9e2f5f46
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.Bundle;
24import android.support.v4.app.NavUtils;
25import android.support.v4.view.ViewCompat;
26import android.support.v4.view.ViewConfigurationCompat;
27import android.support.v4.view.WindowCompat;
28import android.support.v7.appcompat.R;
29import android.support.v7.internal.app.ToolbarActionBar;
30import android.support.v7.internal.app.WindowDecorActionBar;
31import android.support.v7.internal.view.StandaloneActionMode;
32import android.support.v7.internal.view.menu.ListMenuPresenter;
33import android.support.v7.internal.view.menu.MenuBuilder;
34import android.support.v7.internal.view.menu.MenuPresenter;
35import android.support.v7.internal.view.menu.MenuView;
36import android.support.v7.internal.widget.ActionBarContextView;
37import android.support.v7.internal.widget.DecorContentParent;
38import android.support.v7.internal.widget.ProgressBarCompat;
39import android.support.v7.view.ActionMode;
40import android.support.v7.widget.Toolbar;
41import android.util.DisplayMetrics;
42import android.util.TypedValue;
43import android.view.ContextThemeWrapper;
44import android.view.Gravity;
45import android.view.KeyEvent;
46import android.view.LayoutInflater;
47import android.view.Menu;
48import android.view.MenuItem;
49import android.view.View;
50import android.view.ViewConfiguration;
51import android.view.ViewGroup;
52import android.view.ViewStub;
53import android.view.Window;
54import android.view.accessibility.AccessibilityEvent;
55import android.widget.FrameLayout;
56import android.widget.PopupWindow;
57
58class ActionBarActivityDelegateBase extends ActionBarActivityDelegate
59        implements MenuBuilder.Callback {
60    private static final String TAG = "ActionBarActivityDelegateBase";
61
62    private DecorContentParent mDecorContentParent;
63    private ActionMenuPresenterCallback mActionMenuPresenterCallback;
64    private PanelMenuPresenterCallback mPanelMenuPresenterCallback;
65
66    private ListMenuPresenter mListMenuPresenter;
67    private MenuBuilder mMenu;
68
69    ActionMode mActionMode;
70    ActionBarContextView mActionModeView;
71    PopupWindow mActionModePopup;
72    Runnable mShowActionModePopup;
73
74    // true if we have installed a window sub-decor layout.
75    private boolean mSubDecorInstalled;
76    private ViewGroup mWindowDecor;
77
78    private CharSequence mTitleToSet;
79
80    // Used to keep track of Progress Bar Window features
81    private boolean mFeatureProgress, mFeatureIndeterminateProgress;
82
83    // Used for emulating PanelFeatureState
84    private boolean mClosingActionMenu;
85    private boolean mPanelIsPrepared;
86    private boolean mPanelRefreshMenuContent;
87    private Bundle mPanelFrozenActionViewState;
88
89    private boolean mInvalidatePanelMenuPosted;
90    private final Runnable mInvalidatePanelMenuRunnable = new Runnable() {
91        @Override
92        public void run() {
93            supportInvalidateOptionsMenu();
94        }
95    };
96
97    private boolean mEnableDefaultActionBarUp;
98
99    ActionBarActivityDelegateBase(ActionBarActivity activity) {
100        super(activity);
101    }
102
103    @Override
104    void onCreate(Bundle savedInstanceState) {
105        super.onCreate(savedInstanceState);
106
107        mWindowDecor = (ViewGroup) mActivity.getWindow().getDecorView();
108
109        if (NavUtils.getParentActivityName(mActivity) != null) {
110            ActionBar ab = getSupportActionBar();
111            if (ab == null) {
112                mEnableDefaultActionBarUp = true;
113            } else {
114                ab.setDefaultDisplayHomeAsUpEnabled(true);
115            }
116        }
117    }
118
119    @Override
120    public ActionBar createSupportActionBar() {
121        ensureSubDecor();
122        ActionBar ab = new WindowDecorActionBar(mActivity, mOverlayActionBar);
123        ab.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
124        return ab;
125    }
126
127    @Override
128    void setSupportActionBar(Toolbar toolbar) {
129        if (getSupportActionBar() instanceof WindowDecorActionBar) {
130            throw new IllegalStateException("This Activity already has an action bar supplied " +
131                    "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " +
132                    "windowActionBar to false in your theme to use a Toolbar instead.");
133        }
134        // Need to make sure we give the action bar the default window callback. Otherwise multiple
135        // setSupportActionBar() calls lead to memory leaks
136        ToolbarActionBar tbab = new ToolbarActionBar(toolbar, mActivity.getTitle(),
137                mDefaultWindowCallback);
138        setSupportActionBar(tbab);
139        setWindowCallback(tbab.getWrappedWindowCallback());
140        tbab.invalidateOptionsMenu();
141    }
142
143    @Override
144    public void onConfigurationChanged(Configuration newConfig) {
145        // If this is called before sub-decor is installed, ActionBar will not
146        // be properly initialized.
147        if (mHasActionBar && mSubDecorInstalled) {
148            // Note: The action bar will need to access
149            // view changes from superclass.
150            ActionBar ab = getSupportActionBar();
151            if (ab != null) {
152                ab.onConfigurationChanged(newConfig);
153            }
154        }
155    }
156
157    @Override
158    public void onStop() {
159        ActionBar ab = getSupportActionBar();
160        if (ab != null) {
161            ab.setShowHideAnimationEnabled(false);
162        }
163    }
164
165    @Override
166    public void onPostResume() {
167        ActionBar ab = getSupportActionBar();
168        if (ab != null) {
169            ab.setShowHideAnimationEnabled(true);
170        }
171    }
172
173    @Override
174    public void setContentView(View v) {
175        ensureSubDecor();
176        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
177        contentParent.removeAllViews();
178        contentParent.addView(v);
179        mActivity.onSupportContentChanged();
180    }
181
182    @Override
183    public void setContentView(int resId) {
184        ensureSubDecor();
185        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
186        contentParent.removeAllViews();
187        mActivity.getLayoutInflater().inflate(resId, contentParent);
188        mActivity.onSupportContentChanged();
189    }
190
191    @Override
192    public void setContentView(View v, ViewGroup.LayoutParams lp) {
193        ensureSubDecor();
194        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
195        contentParent.removeAllViews();
196        contentParent.addView(v, lp);
197        mActivity.onSupportContentChanged();
198    }
199
200    @Override
201    public void addContentView(View v, ViewGroup.LayoutParams lp) {
202        ensureSubDecor();
203        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
204        contentParent.addView(v, lp);
205        mActivity.onSupportContentChanged();
206    }
207
208    @Override
209    public void onContentChanged() {
210        // Ignore all calls to this method as we call onSupportContentChanged manually above
211    }
212
213    final void ensureSubDecor() {
214        if (!mSubDecorInstalled) {
215            if (mHasActionBar) {
216                /**
217                 * This needs some explanation. As we can not use the android:theme attribute
218                 * pre-L, we emulate it by manually creating a LayoutInflater using a
219                 * ContextThemeWrapper pointing to actionBarTheme.
220                 */
221                TypedValue outValue = new TypedValue();
222                mActivity.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
223
224                Context themedContext;
225                if (outValue.resourceId != 0) {
226                    themedContext = new ContextThemeWrapper(mActivity, outValue.resourceId);
227                } else {
228                    themedContext = mActivity;
229                }
230
231                // Now inflate the view using the themed context and set it as the content view
232                View decor = LayoutInflater.from(themedContext)
233                        .inflate(R.layout.abc_screen_toolbar, null);
234                mActivity.superSetContentView(decor);
235
236                mDecorContentParent = (DecorContentParent) mActivity
237                        .findViewById(R.id.decor_content_parent);
238                mDecorContentParent.setWindowCallback(getWindowCallback());
239
240                /**
241                 * Propagate features to DecorContentParent
242                 */
243                if (mOverlayActionBar) {
244                    mDecorContentParent.initFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY);
245                }
246                if (mFeatureProgress) {
247                    mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
248                }
249                if (mFeatureIndeterminateProgress) {
250                    mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
251                }
252            } else if (mOverlayActionMode) {
253                mActivity.superSetContentView(R.layout.abc_screen_simple_overlay_action_mode);
254            } else {
255                mActivity.superSetContentView(R.layout.abc_screen_simple);
256            }
257
258            // Change our content FrameLayout to use the android.R.id.content id.
259            // Useful for fragments.
260            final View decorContent = mActivity.findViewById(android.R.id.content);
261            decorContent.setId(View.NO_ID);
262            View abcContent = mActivity.findViewById(R.id.action_bar_activity_content);
263            abcContent.setId(android.R.id.content);
264
265            // The decorContent may have a foreground drawable set (windowContentOverlay).
266            // Remove this as we handle it ourselves
267            if (decorContent instanceof FrameLayout) {
268                ((FrameLayout) decorContent).setForeground(null);
269            }
270
271            // A title was set before we've install the decor so set it now.
272            if (mTitleToSet != null && mDecorContentParent != null) {
273                mDecorContentParent.setWindowTitle(mTitleToSet);
274                mTitleToSet = null;
275            }
276
277            applyFixedSizeWindow();
278
279            onSubDecorInstalled();
280
281            mSubDecorInstalled = true;
282
283            invalidatePanelMenu();
284        }
285    }
286
287    void onSubDecorInstalled() {}
288
289    private void applyFixedSizeWindow() {
290        TypedArray a = mActivity.obtainStyledAttributes(R.styleable.Theme);
291
292        TypedValue mFixedWidthMajor = null;
293        TypedValue mFixedWidthMinor = null;
294        TypedValue mFixedHeightMajor = null;
295        TypedValue mFixedHeightMinor = null;
296
297        if (a.hasValue(R.styleable.Theme_windowFixedWidthMajor)) {
298            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
299            a.getValue(R.styleable.Theme_windowFixedWidthMajor, mFixedWidthMajor);
300        }
301        if (a.hasValue(R.styleable.Theme_windowFixedWidthMinor)) {
302            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
303            a.getValue(R.styleable.Theme_windowFixedWidthMinor, mFixedWidthMinor);
304        }
305        if (a.hasValue(R.styleable.Theme_windowFixedHeightMajor)) {
306            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
307            a.getValue(R.styleable.Theme_windowFixedHeightMajor, mFixedHeightMajor);
308        }
309        if (a.hasValue(R.styleable.Theme_windowFixedHeightMinor)) {
310            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
311            a.getValue(R.styleable.Theme_windowFixedHeightMinor, mFixedHeightMinor);
312        }
313
314        final DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics();
315        final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
316        int w = ViewGroup.LayoutParams.MATCH_PARENT;
317        int h = ViewGroup.LayoutParams.MATCH_PARENT;
318
319        final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
320        if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
321            if (tvw.type == TypedValue.TYPE_DIMENSION) {
322                w = (int) tvw.getDimension(metrics);
323            } else if (tvw.type == TypedValue.TYPE_FRACTION) {
324                w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
325            }
326        }
327
328        final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
329        if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
330            if (tvh.type == TypedValue.TYPE_DIMENSION) {
331                h = (int) tvh.getDimension(metrics);
332            } else if (tvh.type == TypedValue.TYPE_FRACTION) {
333                h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
334            }
335        }
336
337        if (w != ViewGroup.LayoutParams.MATCH_PARENT || h != ViewGroup.LayoutParams.MATCH_PARENT) {
338            mActivity.getWindow().setLayout(w, h);
339        }
340
341        a.recycle();
342    }
343
344    @Override
345    public boolean supportRequestWindowFeature(int featureId) {
346        switch (featureId) {
347            case WindowCompat.FEATURE_ACTION_BAR:
348                mHasActionBar = true;
349                return true;
350            case WindowCompat.FEATURE_ACTION_BAR_OVERLAY:
351                mOverlayActionBar = true;
352                return true;
353            case WindowCompat.FEATURE_ACTION_MODE_OVERLAY:
354                mOverlayActionMode = true;
355                return true;
356            case Window.FEATURE_PROGRESS:
357                mFeatureProgress = true;
358                return true;
359            case Window.FEATURE_INDETERMINATE_PROGRESS:
360                mFeatureIndeterminateProgress = true;
361                return true;
362            default:
363                return mActivity.requestWindowFeature(featureId);
364        }
365    }
366
367    @Override
368    public void onTitleChanged(CharSequence title) {
369        if (mDecorContentParent != null) {
370            mDecorContentParent.setWindowTitle(title);
371        } else if (getSupportActionBar() != null) {
372            getSupportActionBar().setWindowTitle(title);
373        } else {
374            mTitleToSet = title;
375        }
376    }
377
378    @Override
379    public View onCreatePanelView(int featureId) {
380        View createdPanelView = null;
381
382        if (featureId == Window.FEATURE_OPTIONS_PANEL && preparePanel()) {
383            createdPanelView = (View) getListMenuView(mActivity);
384        }
385
386        return createdPanelView;
387    }
388
389    @Override
390    public boolean onCreatePanelMenu(int featureId, Menu menu) {
391        if (featureId != Window.FEATURE_OPTIONS_PANEL) {
392            return getWindowCallback().onCreatePanelMenu(featureId, menu);
393        }
394        return false;
395    }
396
397    @Override
398    public boolean onPreparePanel(int featureId, View view, Menu menu) {
399        if (featureId != Window.FEATURE_OPTIONS_PANEL) {
400            return getWindowCallback().onPreparePanel(featureId, view, menu);
401        }
402        return false;
403    }
404
405    @Override
406    public void onPanelClosed(int featureId, Menu menu) {
407        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
408            mPanelIsPrepared = false;
409        }
410    }
411
412    @Override
413    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
414        return mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
415    }
416
417    @Override
418    public void onMenuModeChange(MenuBuilder menu) {
419        reopenMenu(menu, true);
420    }
421
422    @Override
423    public ActionMode startSupportActionMode(ActionMode.Callback callback) {
424        if (callback == null) {
425            throw new IllegalArgumentException("ActionMode callback can not be null.");
426        }
427
428        if (mActionMode != null) {
429            mActionMode.finish();
430        }
431
432        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
433
434        ActionBar ab = getSupportActionBar();
435        if (ab != null) {
436            mActionMode = ab.startActionMode(wrappedCallback);
437            if (mActionMode != null) {
438                mActivity.onSupportActionModeStarted(mActionMode);
439            }
440        }
441
442        if (mActionMode == null) {
443            // If the action bar didn't provide an action mode, start the emulated window one
444            mActionMode = startSupportActionModeFromWindow(wrappedCallback);
445        }
446
447        return mActionMode;
448    }
449
450    @Override
451    public void supportInvalidateOptionsMenu() {
452        final ActionBar ab = getSupportActionBar();
453        if (ab != null && ab.invalidateOptionsMenu()) return;
454
455        if (mMenu != null) {
456            Bundle savedActionViewStates = new Bundle();
457            mMenu.saveActionViewStates(savedActionViewStates);
458            if (savedActionViewStates.size() > 0) {
459                mPanelFrozenActionViewState = savedActionViewStates;
460            }
461            // This will be started again when the panel is prepared.
462            mMenu.stopDispatchingItemsChanged();
463            mMenu.clear();
464        }
465        mPanelRefreshMenuContent = true;
466
467        // Prepare the options panel if we have an action bar
468        if (mDecorContentParent != null) {
469            mPanelIsPrepared = false;
470            preparePanel();
471        }
472    }
473
474    @Override
475    ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback) {
476        if (mActionMode != null) {
477            mActionMode.finish();
478        }
479
480        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
481        ActionMode mode = null;
482
483        if (mActionModeView == null) {
484            if (mIsFloating) {
485                mActionModeView = new ActionBarContextView(mActivity);
486                mActionModePopup = new PopupWindow(mActivity, null,
487                        R.attr.actionModePopupWindowStyle);
488                mActionModePopup.setContentView(mActionModeView);
489                mActionModePopup.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
490
491                TypedValue heightValue = new TypedValue();
492                mActivity.getTheme().resolveAttribute(R.attr.actionBarSize, heightValue, true);
493                final int height = TypedValue.complexToDimensionPixelSize(heightValue.data,
494                        mActivity.getResources().getDisplayMetrics());
495                mActionModeView.setContentHeight(height);
496                mActionModePopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
497                mShowActionModePopup = new Runnable() {
498                    public void run() {
499                        mActionModePopup.showAtLocation(
500                                mActionModeView,
501                                Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
502                    }
503                };
504            } else {
505                ViewStub stub = (ViewStub) mActivity.findViewById(R.id.action_mode_bar_stub);
506                if (stub != null) {
507                    mActionModeView = (ActionBarContextView) stub.inflate();
508                }
509            }
510        }
511
512        if (mActionModeView != null) {
513            mActionModeView.killMode();
514            mode = new StandaloneActionMode(mActivity, mActionModeView, wrappedCallback,
515                    mActionModePopup == null);
516            if (callback.onCreateActionMode(mode, mode.getMenu())) {
517                mode.invalidate();
518                mActionModeView.initForMode(mode);
519                mActionModeView.setVisibility(View.VISIBLE);
520                mActionMode = mode;
521                if (mActionModePopup != null) {
522                    mActivity.getWindow().getDecorView().post(mShowActionModePopup);
523                }
524                mActionModeView.sendAccessibilityEvent(
525                        AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
526            } else {
527                mActionMode = null;
528            }
529        }
530        if (mActionMode != null && mActivity != null) {
531            mActivity.onSupportActionModeStarted(mActionMode);
532        }
533        return mActionMode;
534    }
535
536    private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
537        if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() &&
538                (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity)) ||
539                        mDecorContentParent.isOverflowMenuShowPending())) {
540            if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {
541
542                // If we have a menu invalidation pending, do it now.
543                if (mInvalidatePanelMenuPosted) {
544                    mWindowDecor.removeCallbacks(mInvalidatePanelMenuRunnable);
545                    mInvalidatePanelMenuRunnable.run();
546                }
547
548                // If we don't have a menu or we're waiting for a full content refresh,
549                // forget it. This is a lingering event that no longer matters.
550                if (mMenu != null && !mPanelRefreshMenuContent && preparePanel()) {
551                    mDecorContentParent.showOverflowMenu();
552                }
553            } else {
554                mDecorContentParent.hideOverflowMenu();
555            }
556            return;
557        }
558    }
559
560    private MenuView getListMenuView(Context context) {
561        if (mMenu == null) {
562            return null;
563        }
564
565        if (mPanelMenuPresenterCallback == null) {
566            mPanelMenuPresenterCallback = new PanelMenuPresenterCallback();
567        }
568
569        if (mListMenuPresenter == null) {
570            TypedArray a = context.obtainStyledAttributes(R.styleable.Theme);
571            final int listPresenterTheme = a.getResourceId(
572                    R.styleable.Theme_panelMenuListTheme,
573                    R.style.Theme_AppCompat_CompactMenu);
574            a.recycle();
575
576            mListMenuPresenter = new ListMenuPresenter(
577                    R.layout.abc_list_menu_item_layout, listPresenterTheme);
578            mListMenuPresenter.setCallback(mPanelMenuPresenterCallback);
579            mMenu.addMenuPresenter(mListMenuPresenter, mActivity);
580        } else {
581            // Make sure we update the ListView
582            mListMenuPresenter.updateMenuView(false);
583        }
584
585        if (mListMenuPresenter.getAdapter().isEmpty()) {
586            return null;
587        }
588
589        return mListMenuPresenter.getMenuView(mWindowDecor);
590    }
591
592    @Override
593    public boolean onBackPressed() {
594        // Back cancels action modes first.
595        if (mActionMode != null) {
596            mActionMode.finish();
597            return true;
598        }
599
600        // Next collapse any expanded action views.
601        ActionBar ab = getSupportActionBar();
602        if (ab != null && ab.collapseActionView()) {
603            return true;
604        }
605
606        return false;
607    }
608
609    @Override
610    void setSupportProgressBarVisibility(boolean visible) {
611        updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
612                Window.PROGRESS_VISIBILITY_OFF);
613    }
614
615    @Override
616    void setSupportProgressBarIndeterminateVisibility(boolean visible) {
617        updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
618                Window.PROGRESS_VISIBILITY_OFF);
619    }
620
621    @Override
622    void setSupportProgressBarIndeterminate(boolean indeterminate) {
623        updateProgressBars(indeterminate ? Window.PROGRESS_INDETERMINATE_ON
624                : Window.PROGRESS_INDETERMINATE_OFF);
625    }
626
627    @Override
628    void setSupportProgress(int progress) {
629        updateProgressBars(Window.PROGRESS_START + progress);
630    }
631
632    @Override
633    int getHomeAsUpIndicatorAttrId() {
634        return R.attr.homeAsUpIndicator;
635    }
636
637    @Override
638    boolean onKeyShortcut(int keyCode, KeyEvent event) {
639        // If the panel is already prepared, then perform the shortcut using it.
640        return performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, event.getKeyCode(), event,
641                Menu.FLAG_PERFORM_NO_CLOSE);
642    }
643
644    @Override
645    boolean onKeyDown(int keyCode, KeyEvent event) {
646        // On API v7-10 we need to manually call onKeyShortcut() as this is not called
647        // from the Activity
648        return onKeyShortcut(keyCode, event);
649    }
650
651    /**
652     * Progress Bar function. Mostly extracted from PhoneWindow.java
653     */
654    private void updateProgressBars(int value) {
655        ProgressBarCompat circularProgressBar = getCircularProgressBar();
656        ProgressBarCompat horizontalProgressBar = getHorizontalProgressBar();
657
658        if (value == Window.PROGRESS_VISIBILITY_ON) {
659            if (mFeatureProgress) {
660                int level = horizontalProgressBar.getProgress();
661                int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
662                        View.VISIBLE : View.INVISIBLE;
663                horizontalProgressBar.setVisibility(visibility);
664            }
665            if (mFeatureIndeterminateProgress) {
666                circularProgressBar.setVisibility(View.VISIBLE);
667            }
668        } else if (value == Window.PROGRESS_VISIBILITY_OFF) {
669            if (mFeatureProgress) {
670                horizontalProgressBar.setVisibility(View.GONE);
671            }
672            if (mFeatureIndeterminateProgress) {
673                circularProgressBar.setVisibility(View.GONE);
674            }
675        } else if (value == Window.PROGRESS_INDETERMINATE_ON) {
676            horizontalProgressBar.setIndeterminate(true);
677        } else if (value == Window.PROGRESS_INDETERMINATE_OFF) {
678            horizontalProgressBar.setIndeterminate(false);
679        } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) {
680            // We want to set the progress value before testing for visibility
681            // so that when the progress bar becomes visible again, it has the
682            // correct level.
683            horizontalProgressBar.setProgress(value - Window.PROGRESS_START);
684
685            if (value < Window.PROGRESS_END) {
686                showProgressBars(horizontalProgressBar, circularProgressBar);
687            } else {
688                hideProgressBars(horizontalProgressBar, circularProgressBar);
689            }
690        }
691    }
692
693    private void showProgressBars(ProgressBarCompat horizontalProgressBar,
694            ProgressBarCompat spinnyProgressBar) {
695        if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.INVISIBLE) {
696            spinnyProgressBar.setVisibility(View.VISIBLE);
697        }
698        // Only show the progress bars if the primary progress is not complete
699        if (mFeatureProgress && horizontalProgressBar.getProgress() < 10000) {
700            horizontalProgressBar.setVisibility(View.VISIBLE);
701        }
702    }
703
704    private void hideProgressBars(ProgressBarCompat horizontalProgressBar,
705            ProgressBarCompat spinnyProgressBar) {
706        if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.VISIBLE) {
707            spinnyProgressBar.setVisibility(View.INVISIBLE);
708        }
709        if (mFeatureProgress && horizontalProgressBar.getVisibility() == View.VISIBLE) {
710            horizontalProgressBar.setVisibility(View.INVISIBLE);
711        }
712    }
713
714    private ProgressBarCompat getCircularProgressBar() {
715        ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_circular);
716        if (pb != null) {
717            pb.setVisibility(View.INVISIBLE);
718        }
719        return pb;
720    }
721
722    private ProgressBarCompat getHorizontalProgressBar() {
723        ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_horizontal);
724        if (pb != null) {
725            pb.setVisibility(View.INVISIBLE);
726        }
727        return pb;
728    }
729
730    private boolean initializePanelMenu() {
731        Context context = mActivity;
732
733        if (mDecorContentParent != null) {
734            final TypedValue outValue = new TypedValue();
735            final Resources.Theme baseTheme = context.getTheme();
736            baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
737
738            Resources.Theme widgetTheme = null;
739            if (outValue.resourceId != 0) {
740                widgetTheme = context.getResources().newTheme();
741                widgetTheme.setTo(baseTheme);
742                widgetTheme.applyStyle(outValue.resourceId, true);
743                widgetTheme.resolveAttribute(
744                        R.attr.actionBarWidgetTheme, outValue, true);
745            } else {
746                baseTheme.resolveAttribute(
747                        R.attr.actionBarWidgetTheme, outValue, true);
748            }
749
750            if (outValue.resourceId != 0) {
751                if (widgetTheme == null) {
752                    widgetTheme = context.getResources().newTheme();
753                    widgetTheme.setTo(baseTheme);
754                }
755                widgetTheme.applyStyle(outValue.resourceId, true);
756            }
757
758            if (widgetTheme != null) {
759                context = new ContextThemeWrapper(context, 0);
760                context.getTheme().setTo(widgetTheme);
761            }
762        }
763
764        mMenu = new MenuBuilder(context);
765        mMenu.setCallback(this);
766
767        return true;
768    }
769
770    private boolean preparePanel() {
771        // Already prepared (isPrepared will be reset to false later)
772        if (mPanelIsPrepared) {
773            return true;
774        }
775
776        if (mDecorContentParent != null) {
777            // Enforce ordering guarantees around events so that the action bar never
778            // dispatches menu-related events before the panel is prepared.
779            mDecorContentParent.setMenuPrepared();
780        }
781
782        // Init the panel state's menu--return false if init failed
783        if (mMenu == null || mPanelRefreshMenuContent) {
784            if (mMenu == null) {
785                if (!initializePanelMenu() || (mMenu == null)) {
786                    return false;
787                }
788            }
789
790            if (mDecorContentParent != null) {
791                if (mActionMenuPresenterCallback == null) {
792                    mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
793                }
794                mDecorContentParent.setMenu(mMenu, mActionMenuPresenterCallback);
795            }
796
797            // Creating the panel menu will involve a lot of manipulation;
798            // don't dispatch change events to presenters until we're done.
799            mMenu.stopDispatchingItemsChanged();
800
801            // Call callback, and return if it doesn't want to display menu.
802            if (!getWindowCallback().onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, mMenu)) {
803                // Ditch the menu created above
804                mMenu = null;
805
806                if (mDecorContentParent != null) {
807                    // Don't show it in the action bar either
808                    mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
809                }
810
811                return false;
812            }
813
814            mPanelRefreshMenuContent = false;
815        }
816
817        // Preparing the panel menu can involve a lot of manipulation;
818        // don't dispatch change events to presenters until we're done.
819        mMenu.stopDispatchingItemsChanged();
820
821        // Restore action view state before we prepare. This gives apps
822        // an opportunity to override frozen/restored state in onPrepare.
823        if (mPanelFrozenActionViewState != null) {
824            mMenu.restoreActionViewStates(mPanelFrozenActionViewState);
825            mPanelFrozenActionViewState = null;
826        }
827
828        // Callback and return if the callback does not want to show the menu
829        if (!getWindowCallback().onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, mMenu)) {
830            if (mDecorContentParent != null) {
831                // The app didn't want to show the menu for now but it still exists.
832                // Clear it out of the action bar.
833                mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
834            }
835            mMenu.startDispatchingItemsChanged();
836            return false;
837        }
838
839        mMenu.startDispatchingItemsChanged();
840
841        // Set other state
842        mPanelIsPrepared = true;
843
844        return true;
845    }
846
847    private void checkCloseActionMenu() {
848        if (mClosingActionMenu) {
849            return;
850        }
851
852        mClosingActionMenu = true;
853        mDecorContentParent.dismissPopups();
854        mClosingActionMenu = false;
855    }
856
857    private void closePanel(int featureId) {
858        if (featureId == Window.FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
859                mDecorContentParent.canShowOverflowMenu() &&
860                !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity))) {
861            mDecorContentParent.hideOverflowMenu();
862        } else {
863            mActivity.closeOptionsMenu();
864            mPanelIsPrepared = false;
865        }
866    }
867
868    private void invalidatePanelMenu() {
869        if (!mInvalidatePanelMenuPosted && mWindowDecor != null) {
870            ViewCompat.postOnAnimation(mWindowDecor, mInvalidatePanelMenuRunnable);
871            mInvalidatePanelMenuPosted = true;
872        }
873    }
874
875    final boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
876        if (event.isSystem()) {
877            return false;
878        }
879
880        boolean handled = false;
881
882        // Only try to perform menu shortcuts if preparePanel returned true (possible false
883        // return value from application not wanting to show the menu).
884        if ((mPanelIsPrepared || preparePanel()) && mMenu != null) {
885            // The menu is prepared now, perform the shortcut on it
886            handled = mMenu.performShortcut(keyCode, event, flags);
887        }
888
889        if (handled) {
890            // Only close down the menu if we don't have an action bar keeping it open.
891            if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) {
892                closePanel(featureId);
893            }
894        }
895
896        return handled;
897    }
898
899    /**
900     * Clears out internal reference when the action mode is destroyed.
901     */
902    private class ActionModeCallbackWrapper implements ActionMode.Callback {
903        private ActionMode.Callback mWrapped;
904
905        public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
906            mWrapped = wrapped;
907        }
908
909        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
910            return mWrapped.onCreateActionMode(mode, menu);
911        }
912
913        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
914            return mWrapped.onPrepareActionMode(mode, menu);
915        }
916
917        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
918            return mWrapped.onActionItemClicked(mode, item);
919        }
920
921        public void onDestroyActionMode(ActionMode mode) {
922            mWrapped.onDestroyActionMode(mode);
923            if (mActionModePopup != null) {
924                mActivity.getWindow().getDecorView().removeCallbacks(mShowActionModePopup);
925                mActionModePopup.dismiss();
926            } else if (mActionModeView != null) {
927                mActionModeView.setVisibility(View.GONE);
928            }
929            if (mActionModeView != null) {
930                mActionModeView.removeAllViews();
931            }
932            if (mActivity != null) {
933                try {
934                    mActivity.onSupportActionModeFinished(mActionMode);
935                } catch (AbstractMethodError ame) {
936                    // Older apps might not implement this callback method.
937                }
938            }
939            mActionMode = null;
940        }
941    }
942
943    private final class PanelMenuPresenterCallback implements MenuPresenter.Callback {
944        @Override
945        public boolean onOpenSubMenu(MenuBuilder subMenu) {
946            return false;
947        }
948
949        @Override
950        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
951            closePanel(Window.FEATURE_OPTIONS_PANEL);
952        }
953    }
954
955    private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
956        @Override
957        public boolean onOpenSubMenu(MenuBuilder subMenu) {
958            return false;
959        }
960
961        @Override
962        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
963            checkCloseActionMenu();
964        }
965    }
966
967}
968