Home | History | Annotate | Download | only in actionbarcompat
      1 /*
      2  * Copyright 2011 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 com.example.android.actionbarcompat;
     18 
     19 import org.xmlpull.v1.XmlPullParser;
     20 import org.xmlpull.v1.XmlPullParserException;
     21 
     22 import android.app.Activity;
     23 import android.content.Context;
     24 import android.content.res.XmlResourceParser;
     25 import android.os.Bundle;
     26 import android.view.InflateException;
     27 import android.view.Menu;
     28 import android.view.MenuInflater;
     29 import android.view.MenuItem;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 import android.view.Window;
     33 import android.widget.ImageButton;
     34 import android.widget.ImageView;
     35 import android.widget.LinearLayout;
     36 import android.widget.ProgressBar;
     37 import android.widget.TextView;
     38 
     39 import java.io.IOException;
     40 import java.util.HashSet;
     41 import java.util.Set;
     42 
     43 /**
     44  * A class that implements the action bar pattern for pre-Honeycomb devices.
     45  */
     46 public class ActionBarHelperBase extends ActionBarHelper {
     47     private static final String MENU_RES_NAMESPACE = "http://schemas.android.com/apk/res/android";
     48     private static final String MENU_ATTR_ID = "id";
     49     private static final String MENU_ATTR_SHOW_AS_ACTION = "showAsAction";
     50 
     51     protected Set<Integer> mActionItemIds = new HashSet<Integer>();
     52 
     53     protected ActionBarHelperBase(Activity activity) {
     54         super(activity);
     55     }
     56 
     57     /**{@inheritDoc}*/
     58     @Override
     59     public void onCreate(Bundle savedInstanceState) {
     60         mActivity.requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
     61     }
     62 
     63     /**{@inheritDoc}*/
     64     @Override
     65     public void onPostCreate(Bundle savedInstanceState) {
     66         mActivity.getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
     67                 R.layout.actionbar_compat);
     68         setupActionBar();
     69 
     70         SimpleMenu menu = new SimpleMenu(mActivity);
     71         mActivity.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu);
     72         mActivity.onPrepareOptionsMenu(menu);
     73         for (int i = 0; i < menu.size(); i++) {
     74             MenuItem item = menu.getItem(i);
     75             if (mActionItemIds.contains(item.getItemId())) {
     76                 addActionItemCompatFromMenuItem(item);
     77             }
     78         }
     79     }
     80 
     81     /**
     82      * Sets up the compatibility action bar with the given title.
     83      */
     84     private void setupActionBar() {
     85         final ViewGroup actionBarCompat = getActionBarCompat();
     86         if (actionBarCompat == null) {
     87             return;
     88         }
     89 
     90         LinearLayout.LayoutParams springLayoutParams = new LinearLayout.LayoutParams(
     91                 0, ViewGroup.LayoutParams.FILL_PARENT);
     92         springLayoutParams.weight = 1;
     93 
     94         // Add Home button
     95         SimpleMenu tempMenu = new SimpleMenu(mActivity);
     96         SimpleMenuItem homeItem = new SimpleMenuItem(
     97                 tempMenu, android.R.id.home, 0, mActivity.getString(R.string.app_name));
     98         homeItem.setIcon(R.drawable.ic_home);
     99         addActionItemCompatFromMenuItem(homeItem);
    100 
    101         // Add title text
    102         TextView titleText = new TextView(mActivity, null, R.attr.actionbarCompatTitleStyle);
    103         titleText.setLayoutParams(springLayoutParams);
    104         titleText.setText(mActivity.getTitle());
    105         actionBarCompat.addView(titleText);
    106     }
    107 
    108     /**{@inheritDoc}*/
    109     @Override
    110     public void setRefreshActionItemState(boolean refreshing) {
    111         View refreshButton = mActivity.findViewById(R.id.actionbar_compat_item_refresh);
    112         View refreshIndicator = mActivity.findViewById(
    113                 R.id.actionbar_compat_item_refresh_progress);
    114 
    115         if (refreshButton != null) {
    116             refreshButton.setVisibility(refreshing ? View.GONE : View.VISIBLE);
    117         }
    118         if (refreshIndicator != null) {
    119             refreshIndicator.setVisibility(refreshing ? View.VISIBLE : View.GONE);
    120         }
    121     }
    122 
    123     /**
    124      * Action bar helper code to be run in {@link Activity#onCreateOptionsMenu(android.view.Menu)}.
    125      *
    126      * NOTE: This code will mark on-screen menu items as invisible.
    127      */
    128     @Override
    129     public boolean onCreateOptionsMenu(Menu menu) {
    130         // Hides on-screen action items from the options menu.
    131         for (Integer id : mActionItemIds) {
    132             menu.findItem(id).setVisible(false);
    133         }
    134         return true;
    135     }
    136 
    137     /**{@inheritDoc}*/
    138     @Override
    139     protected void onTitleChanged(CharSequence title, int color) {
    140         TextView titleView = (TextView) mActivity.findViewById(R.id.actionbar_compat_title);
    141         if (titleView != null) {
    142             titleView.setText(title);
    143         }
    144     }
    145 
    146     /**
    147      * Returns a {@link android.view.MenuInflater} that can read action bar metadata on
    148      * pre-Honeycomb devices.
    149      */
    150     public MenuInflater getMenuInflater(MenuInflater superMenuInflater) {
    151         return new WrappedMenuInflater(mActivity, superMenuInflater);
    152     }
    153 
    154     /**
    155      * Returns the {@link android.view.ViewGroup} for the action bar on phones (compatibility action
    156      * bar). Can return null, and will return null on Honeycomb.
    157      */
    158     private ViewGroup getActionBarCompat() {
    159         return (ViewGroup) mActivity.findViewById(R.id.actionbar_compat);
    160     }
    161 
    162     /**
    163      * Adds an action button to the compatibility action bar, using menu information from a {@link
    164      * android.view.MenuItem}. If the menu item ID is <code>menu_refresh</code>, the menu item's
    165      * state can be changed to show a loading spinner using
    166      * {@link com.example.android.actionbarcompat.ActionBarHelperBase#setRefreshActionItemState(boolean)}.
    167      */
    168     private View addActionItemCompatFromMenuItem(final MenuItem item) {
    169         final int itemId = item.getItemId();
    170 
    171         final ViewGroup actionBar = getActionBarCompat();
    172         if (actionBar == null) {
    173             return null;
    174         }
    175 
    176         // Create the button
    177         ImageButton actionButton = new ImageButton(mActivity, null,
    178                 itemId == android.R.id.home
    179                         ? R.attr.actionbarCompatItemHomeStyle
    180                         : R.attr.actionbarCompatItemStyle);
    181         actionButton.setLayoutParams(new ViewGroup.LayoutParams(
    182                 (int) mActivity.getResources().getDimension(
    183                         itemId == android.R.id.home
    184                                 ? R.dimen.actionbar_compat_button_home_width
    185                                 : R.dimen.actionbar_compat_button_width),
    186                 ViewGroup.LayoutParams.FILL_PARENT));
    187         if (itemId == R.id.menu_refresh) {
    188             actionButton.setId(R.id.actionbar_compat_item_refresh);
    189         }
    190         actionButton.setImageDrawable(item.getIcon());
    191         actionButton.setScaleType(ImageView.ScaleType.CENTER);
    192         actionButton.setContentDescription(item.getTitle());
    193         actionButton.setOnClickListener(new View.OnClickListener() {
    194             public void onClick(View view) {
    195                 mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
    196             }
    197         });
    198 
    199         actionBar.addView(actionButton);
    200 
    201         if (item.getItemId() == R.id.menu_refresh) {
    202             // Refresh buttons should be stateful, and allow for indeterminate progress indicators,
    203             // so add those.
    204             ProgressBar indicator = new ProgressBar(mActivity, null,
    205                     R.attr.actionbarCompatProgressIndicatorStyle);
    206 
    207             final int buttonWidth = mActivity.getResources().getDimensionPixelSize(
    208                     R.dimen.actionbar_compat_button_width);
    209             final int buttonHeight = mActivity.getResources().getDimensionPixelSize(
    210                     R.dimen.actionbar_compat_height);
    211             final int progressIndicatorWidth = buttonWidth / 2;
    212 
    213             LinearLayout.LayoutParams indicatorLayoutParams = new LinearLayout.LayoutParams(
    214                     progressIndicatorWidth, progressIndicatorWidth);
    215             indicatorLayoutParams.setMargins(
    216                     (buttonWidth - progressIndicatorWidth) / 2,
    217                     (buttonHeight - progressIndicatorWidth) / 2,
    218                     (buttonWidth - progressIndicatorWidth) / 2,
    219                     0);
    220             indicator.setLayoutParams(indicatorLayoutParams);
    221             indicator.setVisibility(View.GONE);
    222             indicator.setId(R.id.actionbar_compat_item_refresh_progress);
    223             actionBar.addView(indicator);
    224         }
    225 
    226         return actionButton;
    227     }
    228 
    229     /**
    230      * A {@link android.view.MenuInflater} that reads action bar metadata.
    231      */
    232     private class WrappedMenuInflater extends MenuInflater {
    233         MenuInflater mInflater;
    234 
    235         public WrappedMenuInflater(Context context, MenuInflater inflater) {
    236             super(context);
    237             mInflater = inflater;
    238         }
    239 
    240         @Override
    241         public void inflate(int menuRes, Menu menu) {
    242             loadActionBarMetadata(menuRes);
    243             mInflater.inflate(menuRes, menu);
    244         }
    245 
    246         /**
    247          * Loads action bar metadata from a menu resource, storing a list of menu item IDs that
    248          * should be shown on-screen (i.e. those with showAsAction set to always or ifRoom).
    249          * @param menuResId
    250          */
    251         private void loadActionBarMetadata(int menuResId) {
    252             XmlResourceParser parser = null;
    253             try {
    254                 parser = mActivity.getResources().getXml(menuResId);
    255 
    256                 int eventType = parser.getEventType();
    257                 int itemId;
    258                 int showAsAction;
    259 
    260                 boolean eof = false;
    261                 while (!eof) {
    262                     switch (eventType) {
    263                         case XmlPullParser.START_TAG:
    264                             if (!parser.getName().equals("item")) {
    265                                 break;
    266                             }
    267 
    268                             itemId = parser.getAttributeResourceValue(MENU_RES_NAMESPACE,
    269                                     MENU_ATTR_ID, 0);
    270                             if (itemId == 0) {
    271                                 break;
    272                             }
    273 
    274                             showAsAction = parser.getAttributeIntValue(MENU_RES_NAMESPACE,
    275                                     MENU_ATTR_SHOW_AS_ACTION, -1);
    276                             if (showAsAction == MenuItem.SHOW_AS_ACTION_ALWAYS ||
    277                                     showAsAction == MenuItem.SHOW_AS_ACTION_IF_ROOM) {
    278                                 mActionItemIds.add(itemId);
    279                             }
    280                             break;
    281 
    282                         case XmlPullParser.END_DOCUMENT:
    283                             eof = true;
    284                             break;
    285                     }
    286 
    287                     eventType = parser.next();
    288                 }
    289             } catch (XmlPullParserException e) {
    290                 throw new InflateException("Error inflating menu XML", e);
    291             } catch (IOException e) {
    292                 throw new InflateException("Error inflating menu XML", e);
    293             } finally {
    294                 if (parser != null) {
    295                     parser.close();
    296                 }
    297             }
    298         }
    299 
    300     }
    301 }
    302