ActionBarActivityDelegateBase.java revision c4b9e0cb716a4caff218b27d86f37ef8117d257b
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        // Only pass it through to the Activity's super impl if it's not ACTION_BAR. This is
411        // because ICS+ will try and create a framework action bar due to this call
412        if (featureId != WindowCompat.FEATURE_ACTION_BAR) {
413            mActivity.superOnPanelClosed(featureId, menu);
414        }
415    }
416
417    @Override
418    boolean onMenuOpened(int featureId, Menu menu) {
419        if (featureId != WindowCompat.FEATURE_ACTION_BAR) {
420            return mActivity.superOnMenuOpened(featureId, menu);
421        }
422        return true;
423    }
424
425    @Override
426    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
427        return mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
428    }
429
430    @Override
431    public void onMenuModeChange(MenuBuilder menu) {
432        reopenMenu(menu, true);
433    }
434
435    @Override
436    public ActionMode startSupportActionMode(ActionMode.Callback callback) {
437        if (callback == null) {
438            throw new IllegalArgumentException("ActionMode callback can not be null.");
439        }
440
441        if (mActionMode != null) {
442            mActionMode.finish();
443        }
444
445        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
446
447        ActionBar ab = getSupportActionBar();
448        if (ab != null) {
449            mActionMode = ab.startActionMode(wrappedCallback);
450            if (mActionMode != null) {
451                mActivity.onSupportActionModeStarted(mActionMode);
452            }
453        }
454
455        if (mActionMode == null) {
456            // If the action bar didn't provide an action mode, start the emulated window one
457            mActionMode = startSupportActionModeFromWindow(wrappedCallback);
458        }
459
460        return mActionMode;
461    }
462
463    @Override
464    public void supportInvalidateOptionsMenu() {
465        final ActionBar ab = getSupportActionBar();
466        if (ab != null && ab.invalidateOptionsMenu()) return;
467
468        if (mMenu != null) {
469            Bundle savedActionViewStates = new Bundle();
470            mMenu.saveActionViewStates(savedActionViewStates);
471            if (savedActionViewStates.size() > 0) {
472                mPanelFrozenActionViewState = savedActionViewStates;
473            }
474            // This will be started again when the panel is prepared.
475            mMenu.stopDispatchingItemsChanged();
476            mMenu.clear();
477        }
478        mPanelRefreshMenuContent = true;
479
480        // Prepare the options panel if we have an action bar
481        if (mDecorContentParent != null) {
482            mPanelIsPrepared = false;
483            preparePanel();
484        }
485    }
486
487    @Override
488    ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback) {
489        if (mActionMode != null) {
490            mActionMode.finish();
491        }
492
493        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
494        ActionMode mode = null;
495
496        if (mActionModeView == null) {
497            if (mIsFloating) {
498                mActionModeView = new ActionBarContextView(mActivity);
499                mActionModePopup = new PopupWindow(mActivity, null,
500                        R.attr.actionModePopupWindowStyle);
501                mActionModePopup.setContentView(mActionModeView);
502                mActionModePopup.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
503
504                TypedValue heightValue = new TypedValue();
505                mActivity.getTheme().resolveAttribute(R.attr.actionBarSize, heightValue, true);
506                final int height = TypedValue.complexToDimensionPixelSize(heightValue.data,
507                        mActivity.getResources().getDisplayMetrics());
508                mActionModeView.setContentHeight(height);
509                mActionModePopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
510                mShowActionModePopup = new Runnable() {
511                    public void run() {
512                        mActionModePopup.showAtLocation(
513                                mActionModeView,
514                                Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
515                    }
516                };
517            } else {
518                ViewStub stub = (ViewStub) mActivity.findViewById(R.id.action_mode_bar_stub);
519                if (stub != null) {
520                    mActionModeView = (ActionBarContextView) stub.inflate();
521                }
522            }
523        }
524
525        if (mActionModeView != null) {
526            mActionModeView.killMode();
527            mode = new StandaloneActionMode(mActivity, mActionModeView, wrappedCallback,
528                    mActionModePopup == null);
529            if (callback.onCreateActionMode(mode, mode.getMenu())) {
530                mode.invalidate();
531                mActionModeView.initForMode(mode);
532                mActionModeView.setVisibility(View.VISIBLE);
533                mActionMode = mode;
534                if (mActionModePopup != null) {
535                    mActivity.getWindow().getDecorView().post(mShowActionModePopup);
536                }
537                mActionModeView.sendAccessibilityEvent(
538                        AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
539            } else {
540                mActionMode = null;
541            }
542        }
543        if (mActionMode != null && mActivity != null) {
544            mActivity.onSupportActionModeStarted(mActionMode);
545        }
546        return mActionMode;
547    }
548
549    private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
550        if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() &&
551                (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity)) ||
552                        mDecorContentParent.isOverflowMenuShowPending())) {
553            if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {
554
555                // If we have a menu invalidation pending, do it now.
556                if (mInvalidatePanelMenuPosted) {
557                    mWindowDecor.removeCallbacks(mInvalidatePanelMenuRunnable);
558                    mInvalidatePanelMenuRunnable.run();
559                }
560
561                // If we don't have a menu or we're waiting for a full content refresh,
562                // forget it. This is a lingering event that no longer matters.
563                if (mMenu != null && !mPanelRefreshMenuContent && preparePanel()) {
564                    mDecorContentParent.showOverflowMenu();
565                }
566            } else {
567                mDecorContentParent.hideOverflowMenu();
568            }
569            return;
570        }
571    }
572
573    private MenuView getListMenuView(Context context) {
574        if (mMenu == null) {
575            return null;
576        }
577
578        if (mPanelMenuPresenterCallback == null) {
579            mPanelMenuPresenterCallback = new PanelMenuPresenterCallback();
580        }
581
582        if (mListMenuPresenter == null) {
583            TypedArray a = context.obtainStyledAttributes(R.styleable.Theme);
584            final int listPresenterTheme = a.getResourceId(
585                    R.styleable.Theme_panelMenuListTheme,
586                    R.style.Theme_AppCompat_CompactMenu);
587            a.recycle();
588
589            mListMenuPresenter = new ListMenuPresenter(
590                    R.layout.abc_list_menu_item_layout, listPresenterTheme);
591            mListMenuPresenter.setCallback(mPanelMenuPresenterCallback);
592            mMenu.addMenuPresenter(mListMenuPresenter, mActivity);
593        } else {
594            // Make sure we update the ListView
595            mListMenuPresenter.updateMenuView(false);
596        }
597
598        if (mListMenuPresenter.getAdapter().isEmpty()) {
599            return null;
600        }
601
602        return mListMenuPresenter.getMenuView(mWindowDecor);
603    }
604
605    @Override
606    public boolean onBackPressed() {
607        // Back cancels action modes first.
608        if (mActionMode != null) {
609            mActionMode.finish();
610            return true;
611        }
612
613        // Next collapse any expanded action views.
614        ActionBar ab = getSupportActionBar();
615        if (ab != null && ab.collapseActionView()) {
616            return true;
617        }
618
619        return false;
620    }
621
622    @Override
623    void setSupportProgressBarVisibility(boolean visible) {
624        updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
625                Window.PROGRESS_VISIBILITY_OFF);
626    }
627
628    @Override
629    void setSupportProgressBarIndeterminateVisibility(boolean visible) {
630        updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
631                Window.PROGRESS_VISIBILITY_OFF);
632    }
633
634    @Override
635    void setSupportProgressBarIndeterminate(boolean indeterminate) {
636        updateProgressBars(indeterminate ? Window.PROGRESS_INDETERMINATE_ON
637                : Window.PROGRESS_INDETERMINATE_OFF);
638    }
639
640    @Override
641    void setSupportProgress(int progress) {
642        updateProgressBars(Window.PROGRESS_START + progress);
643    }
644
645    @Override
646    int getHomeAsUpIndicatorAttrId() {
647        return R.attr.homeAsUpIndicator;
648    }
649
650    @Override
651    boolean onKeyShortcut(int keyCode, KeyEvent event) {
652        // If the panel is already prepared, then perform the shortcut using it.
653        return performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, event.getKeyCode(), event,
654                Menu.FLAG_PERFORM_NO_CLOSE);
655    }
656
657    @Override
658    boolean onKeyDown(int keyCode, KeyEvent event) {
659        // On API v7-10 we need to manually call onKeyShortcut() as this is not called
660        // from the Activity
661        return onKeyShortcut(keyCode, event);
662    }
663
664    /**
665     * Progress Bar function. Mostly extracted from PhoneWindow.java
666     */
667    private void updateProgressBars(int value) {
668        ProgressBarCompat circularProgressBar = getCircularProgressBar();
669        ProgressBarCompat horizontalProgressBar = getHorizontalProgressBar();
670
671        if (value == Window.PROGRESS_VISIBILITY_ON) {
672            if (mFeatureProgress) {
673                int level = horizontalProgressBar.getProgress();
674                int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
675                        View.VISIBLE : View.INVISIBLE;
676                horizontalProgressBar.setVisibility(visibility);
677            }
678            if (mFeatureIndeterminateProgress) {
679                circularProgressBar.setVisibility(View.VISIBLE);
680            }
681        } else if (value == Window.PROGRESS_VISIBILITY_OFF) {
682            if (mFeatureProgress) {
683                horizontalProgressBar.setVisibility(View.GONE);
684            }
685            if (mFeatureIndeterminateProgress) {
686                circularProgressBar.setVisibility(View.GONE);
687            }
688        } else if (value == Window.PROGRESS_INDETERMINATE_ON) {
689            horizontalProgressBar.setIndeterminate(true);
690        } else if (value == Window.PROGRESS_INDETERMINATE_OFF) {
691            horizontalProgressBar.setIndeterminate(false);
692        } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) {
693            // We want to set the progress value before testing for visibility
694            // so that when the progress bar becomes visible again, it has the
695            // correct level.
696            horizontalProgressBar.setProgress(value - Window.PROGRESS_START);
697
698            if (value < Window.PROGRESS_END) {
699                showProgressBars(horizontalProgressBar, circularProgressBar);
700            } else {
701                hideProgressBars(horizontalProgressBar, circularProgressBar);
702            }
703        }
704    }
705
706    private void showProgressBars(ProgressBarCompat horizontalProgressBar,
707            ProgressBarCompat spinnyProgressBar) {
708        if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.INVISIBLE) {
709            spinnyProgressBar.setVisibility(View.VISIBLE);
710        }
711        // Only show the progress bars if the primary progress is not complete
712        if (mFeatureProgress && horizontalProgressBar.getProgress() < 10000) {
713            horizontalProgressBar.setVisibility(View.VISIBLE);
714        }
715    }
716
717    private void hideProgressBars(ProgressBarCompat horizontalProgressBar,
718            ProgressBarCompat spinnyProgressBar) {
719        if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.VISIBLE) {
720            spinnyProgressBar.setVisibility(View.INVISIBLE);
721        }
722        if (mFeatureProgress && horizontalProgressBar.getVisibility() == View.VISIBLE) {
723            horizontalProgressBar.setVisibility(View.INVISIBLE);
724        }
725    }
726
727    private ProgressBarCompat getCircularProgressBar() {
728        ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_circular);
729        if (pb != null) {
730            pb.setVisibility(View.INVISIBLE);
731        }
732        return pb;
733    }
734
735    private ProgressBarCompat getHorizontalProgressBar() {
736        ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_horizontal);
737        if (pb != null) {
738            pb.setVisibility(View.INVISIBLE);
739        }
740        return pb;
741    }
742
743    private boolean initializePanelMenu() {
744        Context context = mActivity;
745
746        if (mDecorContentParent != null) {
747            final TypedValue outValue = new TypedValue();
748            final Resources.Theme baseTheme = context.getTheme();
749            baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
750
751            Resources.Theme widgetTheme = null;
752            if (outValue.resourceId != 0) {
753                widgetTheme = context.getResources().newTheme();
754                widgetTheme.setTo(baseTheme);
755                widgetTheme.applyStyle(outValue.resourceId, true);
756                widgetTheme.resolveAttribute(
757                        R.attr.actionBarWidgetTheme, outValue, true);
758            } else {
759                baseTheme.resolveAttribute(
760                        R.attr.actionBarWidgetTheme, outValue, true);
761            }
762
763            if (outValue.resourceId != 0) {
764                if (widgetTheme == null) {
765                    widgetTheme = context.getResources().newTheme();
766                    widgetTheme.setTo(baseTheme);
767                }
768                widgetTheme.applyStyle(outValue.resourceId, true);
769            }
770
771            if (widgetTheme != null) {
772                context = new ContextThemeWrapper(context, 0);
773                context.getTheme().setTo(widgetTheme);
774            }
775        }
776
777        mMenu = new MenuBuilder(context);
778        mMenu.setCallback(this);
779
780        return true;
781    }
782
783    private boolean preparePanel() {
784        // Already prepared (isPrepared will be reset to false later)
785        if (mPanelIsPrepared) {
786            return true;
787        }
788
789        if (mDecorContentParent != null) {
790            // Enforce ordering guarantees around events so that the action bar never
791            // dispatches menu-related events before the panel is prepared.
792            mDecorContentParent.setMenuPrepared();
793        }
794
795        // Init the panel state's menu--return false if init failed
796        if (mMenu == null || mPanelRefreshMenuContent) {
797            if (mMenu == null) {
798                if (!initializePanelMenu() || (mMenu == null)) {
799                    return false;
800                }
801            }
802
803            if (mDecorContentParent != null) {
804                if (mActionMenuPresenterCallback == null) {
805                    mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
806                }
807                mDecorContentParent.setMenu(mMenu, mActionMenuPresenterCallback);
808            }
809
810            // Creating the panel menu will involve a lot of manipulation;
811            // don't dispatch change events to presenters until we're done.
812            mMenu.stopDispatchingItemsChanged();
813
814            // Call callback, and return if it doesn't want to display menu.
815            if (!getWindowCallback().onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, mMenu)) {
816                // Ditch the menu created above
817                mMenu = null;
818
819                if (mDecorContentParent != null) {
820                    // Don't show it in the action bar either
821                    mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
822                }
823
824                return false;
825            }
826
827            mPanelRefreshMenuContent = false;
828        }
829
830        // Preparing the panel menu can involve a lot of manipulation;
831        // don't dispatch change events to presenters until we're done.
832        mMenu.stopDispatchingItemsChanged();
833
834        // Restore action view state before we prepare. This gives apps
835        // an opportunity to override frozen/restored state in onPrepare.
836        if (mPanelFrozenActionViewState != null) {
837            mMenu.restoreActionViewStates(mPanelFrozenActionViewState);
838            mPanelFrozenActionViewState = null;
839        }
840
841        // Callback and return if the callback does not want to show the menu
842        if (!getWindowCallback().onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, mMenu)) {
843            if (mDecorContentParent != null) {
844                // The app didn't want to show the menu for now but it still exists.
845                // Clear it out of the action bar.
846                mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
847            }
848            mMenu.startDispatchingItemsChanged();
849            return false;
850        }
851
852        mMenu.startDispatchingItemsChanged();
853
854        // Set other state
855        mPanelIsPrepared = true;
856
857        return true;
858    }
859
860    private void checkCloseActionMenu() {
861        if (mClosingActionMenu) {
862            return;
863        }
864
865        mClosingActionMenu = true;
866        mDecorContentParent.dismissPopups();
867        mClosingActionMenu = false;
868    }
869
870    private void closePanel(int featureId) {
871        if (featureId == Window.FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
872                mDecorContentParent.canShowOverflowMenu() &&
873                !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity))) {
874            mDecorContentParent.hideOverflowMenu();
875        } else {
876            mActivity.closeOptionsMenu();
877            mPanelIsPrepared = false;
878        }
879    }
880
881    private void invalidatePanelMenu() {
882        if (!mInvalidatePanelMenuPosted && mWindowDecor != null) {
883            ViewCompat.postOnAnimation(mWindowDecor, mInvalidatePanelMenuRunnable);
884            mInvalidatePanelMenuPosted = true;
885        }
886    }
887
888    final boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
889        if (event.isSystem()) {
890            return false;
891        }
892
893        boolean handled = false;
894
895        // Only try to perform menu shortcuts if preparePanel returned true (possible false
896        // return value from application not wanting to show the menu).
897        if ((mPanelIsPrepared || preparePanel()) && mMenu != null) {
898            // The menu is prepared now, perform the shortcut on it
899            handled = mMenu.performShortcut(keyCode, event, flags);
900        }
901
902        if (handled) {
903            // Only close down the menu if we don't have an action bar keeping it open.
904            if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) {
905                closePanel(featureId);
906            }
907        }
908
909        return handled;
910    }
911
912    /**
913     * Clears out internal reference when the action mode is destroyed.
914     */
915    private class ActionModeCallbackWrapper implements ActionMode.Callback {
916        private ActionMode.Callback mWrapped;
917
918        public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
919            mWrapped = wrapped;
920        }
921
922        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
923            return mWrapped.onCreateActionMode(mode, menu);
924        }
925
926        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
927            return mWrapped.onPrepareActionMode(mode, menu);
928        }
929
930        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
931            return mWrapped.onActionItemClicked(mode, item);
932        }
933
934        public void onDestroyActionMode(ActionMode mode) {
935            mWrapped.onDestroyActionMode(mode);
936            if (mActionModePopup != null) {
937                mActivity.getWindow().getDecorView().removeCallbacks(mShowActionModePopup);
938                mActionModePopup.dismiss();
939            } else if (mActionModeView != null) {
940                mActionModeView.setVisibility(View.GONE);
941            }
942            if (mActionModeView != null) {
943                mActionModeView.removeAllViews();
944            }
945            if (mActivity != null) {
946                try {
947                    mActivity.onSupportActionModeFinished(mActionMode);
948                } catch (AbstractMethodError ame) {
949                    // Older apps might not implement this callback method.
950                }
951            }
952            mActionMode = null;
953        }
954    }
955
956    private final class PanelMenuPresenterCallback implements MenuPresenter.Callback {
957        @Override
958        public boolean onOpenSubMenu(MenuBuilder subMenu) {
959            return false;
960        }
961
962        @Override
963        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
964            closePanel(Window.FEATURE_OPTIONS_PANEL);
965        }
966    }
967
968    private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
969        @Override
970        public boolean onOpenSubMenu(MenuBuilder subMenu) {
971            return false;
972        }
973
974        @Override
975        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
976            checkCloseActionMenu();
977        }
978    }
979
980}
981