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