Home | History | Annotate | Download | only in app
      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 
     17 package android.support.v7.app;
     18 
     19 import android.content.Context;
     20 import android.content.res.Configuration;
     21 import android.content.res.TypedArray;
     22 import android.graphics.drawable.Drawable;
     23 import android.support.v4.app.ActionBarDrawerToggle;
     24 import android.support.v4.view.WindowCompat;
     25 import android.support.v7.appcompat.R;
     26 import android.support.v7.internal.view.menu.ListMenuPresenter;
     27 import android.support.v7.internal.view.menu.MenuBuilder;
     28 import android.support.v7.internal.view.menu.MenuPresenter;
     29 import android.support.v7.internal.view.menu.MenuView;
     30 import android.support.v7.internal.view.menu.MenuWrapperFactory;
     31 import android.support.v7.internal.widget.ActionBarContainer;
     32 import android.support.v7.internal.widget.ActionBarContextView;
     33 import android.support.v7.internal.widget.ActionBarView;
     34 import android.support.v7.internal.widget.ProgressBarICS;
     35 import android.support.v7.view.ActionMode;
     36 import android.view.LayoutInflater;
     37 import android.view.Menu;
     38 import android.view.MenuItem;
     39 import android.view.View;
     40 import android.view.ViewGroup;
     41 import android.view.Window;
     42 import android.widget.FrameLayout;
     43 
     44 class ActionBarActivityDelegateBase extends ActionBarActivityDelegate implements
     45         MenuPresenter.Callback, MenuBuilder.Callback {
     46     private static final String TAG = "ActionBarActivityDelegateBase";
     47 
     48     private static final int[] ACTION_BAR_DRAWABLE_TOGGLE_ATTRS = new int[] {
     49             R.attr.homeAsUpIndicator
     50     };
     51 
     52     private ActionBarView mActionBarView;
     53     private ListMenuPresenter mListMenuPresenter;
     54     private MenuBuilder mMenu;
     55 
     56     private ActionMode mActionMode;
     57 
     58     // true if we have installed a window sub-decor layout.
     59     private boolean mSubDecorInstalled;
     60 
     61     // Used to keep track of Progress Bar Window features
     62     private boolean mFeatureProgress, mFeatureIndeterminateProgress;
     63 
     64     private boolean mInvalidateMenuPosted;
     65     private final Runnable mInvalidateMenuRunnable = new Runnable() {
     66         @Override
     67         public void run() {
     68             final MenuBuilder menu = createMenu();
     69             if (mActivity.superOnCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) &&
     70                     mActivity.superOnPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) {
     71                 setMenu(menu);
     72             } else {
     73                 setMenu(null);
     74             }
     75 
     76             mInvalidateMenuPosted = false;
     77         }
     78     };
     79 
     80     ActionBarActivityDelegateBase(ActionBarActivity activity) {
     81         super(activity);
     82     }
     83 
     84     @Override
     85     public ActionBar createSupportActionBar() {
     86         ensureSubDecor();
     87         return new ActionBarImplBase(mActivity, mActivity);
     88     }
     89 
     90     @Override
     91     public void onConfigurationChanged(Configuration newConfig) {
     92         // If this is called before sub-decor is installed, ActionBar will not
     93         // be properly initialized.
     94         if (mHasActionBar && mSubDecorInstalled) {
     95             // Note: The action bar will need to access
     96             // view changes from superclass.
     97             ActionBarImplBase actionBar = (ActionBarImplBase) getSupportActionBar();
     98             actionBar.onConfigurationChanged(newConfig);
     99         }
    100     }
    101 
    102     @Override
    103     public void onStop() {
    104         ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar();
    105         if (ab != null) {
    106             ab.setShowHideAnimationEnabled(false);
    107         }
    108     }
    109 
    110     @Override
    111     public void onPostResume() {
    112         ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar();
    113         if (ab != null) {
    114             ab.setShowHideAnimationEnabled(true);
    115         }
    116     }
    117 
    118     @Override
    119     public void setContentView(View v) {
    120         ensureSubDecor();
    121         if (mHasActionBar) {
    122             final ViewGroup contentParent =
    123                     (ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content);
    124             contentParent.removeAllViews();
    125             contentParent.addView(v);
    126         } else {
    127             mActivity.superSetContentView(v);
    128         }
    129     }
    130 
    131     @Override
    132     public void setContentView(int resId) {
    133         ensureSubDecor();
    134         if (mHasActionBar) {
    135             final ViewGroup contentParent =
    136                     (ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content);
    137             contentParent.removeAllViews();
    138             final LayoutInflater inflater = mActivity.getLayoutInflater();
    139             inflater.inflate(resId, contentParent);
    140         } else {
    141             mActivity.superSetContentView(resId);
    142         }
    143     }
    144 
    145     @Override
    146     public void setContentView(View v, ViewGroup.LayoutParams lp) {
    147         ensureSubDecor();
    148         if (mHasActionBar) {
    149             final ViewGroup contentParent =
    150                     (ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content);
    151             contentParent.removeAllViews();
    152             contentParent.addView(v, lp);
    153         } else {
    154             mActivity.superSetContentView(v, lp);
    155         }
    156     }
    157 
    158     @Override
    159     public void addContentView(View v, ViewGroup.LayoutParams lp) {
    160         ensureSubDecor();
    161         if (mHasActionBar) {
    162             final ViewGroup contentParent =
    163                     (ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content);
    164             contentParent.addView(v, lp);
    165         } else {
    166             mActivity.superSetContentView(v, lp);
    167         }
    168     }
    169 
    170     final void ensureSubDecor() {
    171         if (mHasActionBar && !mSubDecorInstalled) {
    172             if (mOverlayActionBar) {
    173                 mActivity.superSetContentView(R.layout.abc_action_bar_decor_overlay);
    174             } else {
    175                 mActivity.superSetContentView(R.layout.abc_action_bar_decor);
    176             }
    177             mActionBarView = (ActionBarView) mActivity.findViewById(R.id.action_bar);
    178             mActionBarView.setWindowCallback(mActivity);
    179 
    180             /**
    181              * Progress Bars
    182              */
    183             if (mFeatureProgress) {
    184                 mActionBarView.initProgress();
    185             }
    186             if (mFeatureIndeterminateProgress) {
    187                 mActionBarView.initIndeterminateProgress();
    188             }
    189 
    190             /**
    191              * Split Action Bar
    192              */
    193             boolean splitWhenNarrow = UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW
    194                     .equals(getUiOptionsFromMetadata());
    195             boolean splitActionBar;
    196 
    197             if (splitWhenNarrow) {
    198                 splitActionBar = mActivity.getResources()
    199                         .getBoolean(R.bool.abc_split_action_bar_is_narrow);
    200             } else {
    201                 TypedArray a = mActivity.obtainStyledAttributes(R.styleable.ActionBarWindow);
    202                 splitActionBar = a
    203                         .getBoolean(R.styleable.ActionBarWindow_windowSplitActionBar, false);
    204                 a.recycle();
    205             }
    206 
    207             final ActionBarContainer splitView = (ActionBarContainer) mActivity.findViewById(
    208                     R.id.split_action_bar);
    209             if (splitView != null) {
    210                 mActionBarView.setSplitView(splitView);
    211                 mActionBarView.setSplitActionBar(splitActionBar);
    212                 mActionBarView.setSplitWhenNarrow(splitWhenNarrow);
    213 
    214                 final ActionBarContextView cab = (ActionBarContextView) mActivity.findViewById(
    215                         R.id.action_context_bar);
    216                 cab.setSplitView(splitView);
    217                 cab.setSplitActionBar(splitActionBar);
    218                 cab.setSplitWhenNarrow(splitWhenNarrow);
    219             }
    220 
    221             mSubDecorInstalled = true;
    222 
    223             supportInvalidateOptionsMenu();
    224         }
    225     }
    226 
    227     @Override
    228     public boolean supportRequestWindowFeature(int featureId) {
    229         switch (featureId) {
    230             case WindowCompat.FEATURE_ACTION_BAR:
    231                 mHasActionBar = true;
    232                 return true;
    233             case WindowCompat.FEATURE_ACTION_BAR_OVERLAY:
    234                 mOverlayActionBar = true;
    235                 return true;
    236             case Window.FEATURE_PROGRESS:
    237                 mFeatureProgress = true;
    238                 return true;
    239             case Window.FEATURE_INDETERMINATE_PROGRESS:
    240                 mFeatureIndeterminateProgress = true;
    241                 return true;
    242             default:
    243                 return mActivity.requestWindowFeature(featureId);
    244         }
    245     }
    246 
    247     @Override
    248     public void onTitleChanged(CharSequence title) {
    249         if (mActionBarView != null) {
    250             mActionBarView.setWindowTitle(title);
    251         }
    252     }
    253 
    254     @Override
    255     public View onCreatePanelView(int featureId) {
    256         View createdPanelView = null;
    257 
    258         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
    259             boolean show = true;
    260             MenuBuilder menu = mMenu;
    261 
    262             if (mActionMode == null) {
    263                 // We only want to dispatch Activity/Fragment menu calls if there isn't
    264                 // currently an action mode
    265 
    266                 if (menu == null) {
    267                     // We don't have a menu created, so create one
    268                     menu = createMenu();
    269                     setMenu(menu);
    270 
    271                     // Make sure we're not dispatching item changes to presenters
    272                     menu.stopDispatchingItemsChanged();
    273                     // Dispatch onCreateSupportOptionsMenu
    274                     show = mActivity.superOnCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu);
    275                 }
    276 
    277                 if (show) {
    278                     // Make sure we're not dispatching item changes to presenters
    279                     menu.stopDispatchingItemsChanged();
    280                     // Dispatch onPrepareSupportOptionsMenu
    281                     show = mActivity.superOnPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu);
    282                 }
    283             }
    284 
    285             if (show) {
    286                 createdPanelView = (View) getListMenuView(mActivity, this);
    287 
    288                 // Allow menu to start dispatching changes to presenters
    289                 menu.startDispatchingItemsChanged();
    290             } else {
    291                 // If the menu isn't being shown, we no longer need it
    292                 setMenu(null);
    293             }
    294         }
    295 
    296         return createdPanelView;
    297     }
    298 
    299     @Override
    300     public boolean onCreatePanelMenu(int featureId, Menu menu) {
    301         if (featureId != Window.FEATURE_OPTIONS_PANEL) {
    302             return mActivity.superOnCreatePanelMenu(featureId, menu);
    303         }
    304         return false;
    305     }
    306 
    307     @Override
    308     public boolean onPreparePanel(int featureId, View view, Menu menu) {
    309         if (featureId != Window.FEATURE_OPTIONS_PANEL) {
    310             return mActivity.superOnPreparePanel(featureId, view, menu);
    311         }
    312         return false;
    313     }
    314 
    315     @Override
    316     public boolean onMenuItemSelected(int featureId, MenuItem item) {
    317         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
    318             item = MenuWrapperFactory.createMenuItemWrapper(item);
    319         }
    320         return mActivity.superOnMenuItemSelected(featureId, item);
    321     }
    322 
    323     @Override
    324     public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
    325         return mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
    326     }
    327 
    328     @Override
    329     public void onMenuModeChange(MenuBuilder menu) {
    330         reopenMenu(menu, true);
    331     }
    332 
    333     @Override
    334     public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
    335         mActivity.closeOptionsMenu();
    336     }
    337 
    338     @Override
    339     public boolean onOpenSubMenu(MenuBuilder subMenu) {
    340         return false;
    341     }
    342 
    343     @Override
    344     public ActionMode startSupportActionMode(ActionMode.Callback callback) {
    345         if (callback == null) {
    346             throw new IllegalArgumentException("ActionMode callback can not be null.");
    347         }
    348 
    349         if (mActionMode != null) {
    350             mActionMode.finish();
    351         }
    352 
    353         final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
    354 
    355         ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar();
    356         if (ab != null) {
    357             mActionMode = ab.startActionMode(wrappedCallback);
    358         }
    359 
    360         if (mActionMode != null) {
    361             mActivity.onSupportActionModeStarted(mActionMode);
    362         }
    363         return mActionMode;
    364     }
    365 
    366     @Override
    367     public void supportInvalidateOptionsMenu() {
    368         if (!mInvalidateMenuPosted) {
    369             mInvalidateMenuPosted = true;
    370             mActivity.getWindow().getDecorView().post(mInvalidateMenuRunnable);
    371         }
    372     }
    373 
    374     private MenuBuilder createMenu() {
    375         MenuBuilder menu = new MenuBuilder(getActionBarThemedContext());
    376         menu.setCallback(this);
    377         return menu;
    378     }
    379 
    380     private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
    381         if (mActionBarView != null && mActionBarView.isOverflowReserved()) {
    382             if (!mActionBarView.isOverflowMenuShowing() || !toggleMenuMode) {
    383                 if (mActionBarView.getVisibility() == View.VISIBLE) {
    384                     mActionBarView.showOverflowMenu();
    385                 }
    386             } else {
    387                 mActionBarView.hideOverflowMenu();
    388             }
    389             return;
    390         }
    391 
    392         menu.close();
    393     }
    394 
    395     private MenuView getListMenuView(Context context, MenuPresenter.Callback cb) {
    396         if (mMenu == null) {
    397             return null;
    398         }
    399 
    400         if (mListMenuPresenter == null) {
    401             TypedArray a = context.obtainStyledAttributes(R.styleable.Theme);
    402             final int listPresenterTheme = a.getResourceId(
    403                     R.styleable.Theme_panelMenuListTheme,
    404                     R.style.Theme_AppCompat_CompactMenu);
    405             a.recycle();
    406 
    407             mListMenuPresenter = new ListMenuPresenter(
    408                     R.layout.abc_list_menu_item_layout, listPresenterTheme);
    409             mListMenuPresenter.setCallback(cb);
    410             mMenu.addMenuPresenter(mListMenuPresenter);
    411         } else {
    412             // Make sure we update the ListView
    413             mListMenuPresenter.updateMenuView(false);
    414         }
    415 
    416         return mListMenuPresenter.getMenuView(new FrameLayout(context));
    417     }
    418 
    419     private void setMenu(MenuBuilder menu) {
    420         if (menu == mMenu) {
    421             return;
    422         }
    423 
    424         if (mMenu != null) {
    425             mMenu.removeMenuPresenter(mListMenuPresenter);
    426         }
    427         mMenu = menu;
    428 
    429         if (menu != null && mListMenuPresenter != null) {
    430             // Only update list menu if there isn't an action mode menu
    431             menu.addMenuPresenter(mListMenuPresenter);
    432         }
    433         if (mActionBarView != null) {
    434             mActionBarView.setMenu(menu, this);
    435         }
    436     }
    437 
    438     @Override
    439     public boolean onBackPressed() {
    440         // Back cancels action modes first.
    441         if (mActionMode != null) {
    442             mActionMode.finish();
    443             return true;
    444         }
    445 
    446         // Next collapse any expanded action views.
    447         if (mActionBarView != null && mActionBarView.hasExpandedActionView()) {
    448             mActionBarView.collapseActionView();
    449             return true;
    450         }
    451 
    452         return false;
    453     }
    454 
    455     @Override
    456     void setSupportProgressBarVisibility(boolean visible) {
    457         updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
    458                 Window.PROGRESS_VISIBILITY_OFF);
    459     }
    460 
    461     @Override
    462     void setSupportProgressBarIndeterminateVisibility(boolean visible) {
    463         updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
    464                 Window.PROGRESS_VISIBILITY_OFF);
    465     }
    466 
    467     @Override
    468     void setSupportProgressBarIndeterminate(boolean indeterminate) {
    469         updateProgressBars(indeterminate ? Window.PROGRESS_INDETERMINATE_ON
    470                 : Window.PROGRESS_INDETERMINATE_OFF);
    471     }
    472 
    473     @Override
    474     void setSupportProgress(int progress) {
    475         updateProgressBars(Window.PROGRESS_START + progress);
    476     }
    477 
    478     @Override
    479     ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
    480         return new ActionBarDrawableToggleImpl();
    481     }
    482 
    483     /**
    484      * Progress Bar function. Mostly extracted from PhoneWindow.java
    485      */
    486     private void updateProgressBars(int value) {
    487         ProgressBarICS circularProgressBar = getCircularProgressBar();
    488         ProgressBarICS horizontalProgressBar = getHorizontalProgressBar();
    489 
    490         if (value == Window.PROGRESS_VISIBILITY_ON) {
    491             if (mFeatureProgress) {
    492                 int level = horizontalProgressBar.getProgress();
    493                 int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
    494                         View.VISIBLE : View.INVISIBLE;
    495                 horizontalProgressBar.setVisibility(visibility);
    496             }
    497             if (mFeatureIndeterminateProgress) {
    498                 circularProgressBar.setVisibility(View.VISIBLE);
    499             }
    500         } else if (value == Window.PROGRESS_VISIBILITY_OFF) {
    501             if (mFeatureProgress) {
    502                 horizontalProgressBar.setVisibility(View.GONE);
    503             }
    504             if (mFeatureIndeterminateProgress) {
    505                 circularProgressBar.setVisibility(View.GONE);
    506             }
    507         } else if (value == Window.PROGRESS_INDETERMINATE_ON) {
    508             horizontalProgressBar.setIndeterminate(true);
    509         } else if (value == Window.PROGRESS_INDETERMINATE_OFF) {
    510             horizontalProgressBar.setIndeterminate(false);
    511         } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) {
    512             // We want to set the progress value before testing for visibility
    513             // so that when the progress bar becomes visible again, it has the
    514             // correct level.
    515             horizontalProgressBar.setProgress(value - Window.PROGRESS_START);
    516 
    517             if (value < Window.PROGRESS_END) {
    518                 showProgressBars(horizontalProgressBar, circularProgressBar);
    519             } else {
    520                 hideProgressBars(horizontalProgressBar, circularProgressBar);
    521             }
    522         }
    523     }
    524 
    525     private void showProgressBars(ProgressBarICS horizontalProgressBar,
    526             ProgressBarICS spinnyProgressBar) {
    527         if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.INVISIBLE) {
    528             spinnyProgressBar.setVisibility(View.VISIBLE);
    529         }
    530         // Only show the progress bars if the primary progress is not complete
    531         if (mFeatureProgress && horizontalProgressBar.getProgress() < 10000) {
    532             horizontalProgressBar.setVisibility(View.VISIBLE);
    533         }
    534     }
    535 
    536     private void hideProgressBars(ProgressBarICS horizontalProgressBar,
    537             ProgressBarICS spinnyProgressBar) {
    538         if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.VISIBLE) {
    539             spinnyProgressBar.setVisibility(View.INVISIBLE);
    540         }
    541         if (mFeatureProgress && horizontalProgressBar.getVisibility() == View.VISIBLE) {
    542             horizontalProgressBar.setVisibility(View.INVISIBLE);
    543         }
    544     }
    545 
    546     private ProgressBarICS getCircularProgressBar() {
    547         ProgressBarICS pb = (ProgressBarICS) mActionBarView.findViewById(R.id.progress_circular);
    548         if (pb != null) {
    549             pb.setVisibility(View.INVISIBLE);
    550         }
    551         return pb;
    552     }
    553 
    554     private ProgressBarICS getHorizontalProgressBar() {
    555         ProgressBarICS pb = (ProgressBarICS) mActionBarView.findViewById(R.id.progress_horizontal);
    556         if (pb != null) {
    557             pb.setVisibility(View.INVISIBLE);
    558         }
    559         return pb;
    560     }
    561 
    562     /**
    563      * Clears out internal reference when the action mode is destroyed.
    564      */
    565     private class ActionModeCallbackWrapper implements ActionMode.Callback {
    566         private ActionMode.Callback mWrapped;
    567 
    568         public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
    569             mWrapped = wrapped;
    570         }
    571 
    572         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    573             return mWrapped.onCreateActionMode(mode, menu);
    574         }
    575 
    576         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
    577             return mWrapped.onPrepareActionMode(mode, menu);
    578         }
    579 
    580         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    581             return mWrapped.onActionItemClicked(mode, item);
    582         }
    583 
    584         public void onDestroyActionMode(ActionMode mode) {
    585             mWrapped.onDestroyActionMode(mode);
    586             mActivity.onSupportActionModeFinished(mode);
    587             mActionMode = null;
    588         }
    589     }
    590 
    591     private class ActionBarDrawableToggleImpl
    592             implements ActionBarDrawerToggle.Delegate {
    593 
    594         @Override
    595         public Drawable getThemeUpIndicator() {
    596             final TypedArray a = mActivity.obtainStyledAttributes(ACTION_BAR_DRAWABLE_TOGGLE_ATTRS);
    597             final Drawable result = a.getDrawable(0);
    598             a.recycle();
    599             return result;
    600         }
    601 
    602         @Override
    603         public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
    604             if (mActionBarView != null) {
    605                 mActionBarView.setHomeAsUpIndicator(upDrawable);
    606             }
    607         }
    608 
    609         @Override
    610         public void setActionBarDescription(int contentDescRes) {
    611             // No support for setting Action Bar content description
    612         }
    613     }
    614 
    615 }
    616