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