• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ActionBarHelperBase(Activity activity)53     protected ActionBarHelperBase(Activity activity) {
54         super(activity);
55     }
56 
57     /**{@inheritDoc}*/
58     @Override
onCreate(Bundle savedInstanceState)59     public void onCreate(Bundle savedInstanceState) {
60         mActivity.requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
61     }
62 
63     /**{@inheritDoc}*/
64     @Override
onPostCreate(Bundle savedInstanceState)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      */
setupActionBar()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
setRefreshActionItemState(boolean refreshing)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
onCreateOptionsMenu(Menu menu)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
onTitleChanged(CharSequence title, int color)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      */
getMenuInflater(MenuInflater superMenuInflater)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      */
getActionBarCompat()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      */
addActionItemCompatFromMenuItem(final MenuItem item)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 
WrappedMenuInflater(Context context, MenuInflater inflater)235         public WrappedMenuInflater(Context context, MenuInflater inflater) {
236             super(context);
237             mInflater = inflater;
238         }
239 
240         @Override
inflate(int menuRes, Menu menu)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          */
loadActionBarMetadata(int menuResId)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