blob: 4882adc99266aafeb875df6bc0f4037bb5060041 [file] [log] [blame]
Adam Powell696cba52011-03-29 10:38:16 -07001/*
2 * Copyright (C) 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
17package com.android.internal.view.menu;
18
19import android.content.Context;
Adam Powell275702c2011-09-23 17:34:04 -070020import android.database.DataSetObserver;
Adam Powell696cba52011-03-29 10:38:16 -070021import android.os.Bundle;
22import android.os.Parcelable;
23import android.util.SparseArray;
24import android.view.ContextThemeWrapper;
25import android.view.LayoutInflater;
26import android.view.View;
27import android.view.ViewGroup;
28import android.widget.AdapterView;
29import android.widget.BaseAdapter;
30import android.widget.ListAdapter;
31
32import java.util.ArrayList;
33
34/**
35 * MenuPresenter for list-style menus.
36 */
37public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClickListener {
Adam Powell1d07e162011-09-07 20:46:24 -070038 private static final String TAG = "ListMenuPresenter";
39
Adam Powell696cba52011-03-29 10:38:16 -070040 Context mContext;
41 LayoutInflater mInflater;
42 MenuBuilder mMenu;
43
44 ExpandedMenuView mMenuView;
45
46 private int mItemIndexOffset;
47 int mThemeRes;
48 int mItemLayoutRes;
49
50 private Callback mCallback;
Adam Powell275702c2011-09-23 17:34:04 -070051 MenuAdapter mAdapter;
Adam Powell696cba52011-03-29 10:38:16 -070052
Adam Powell11ed1d62011-07-11 21:19:59 -070053 private int mId;
54
Adam Powell696cba52011-03-29 10:38:16 -070055 public static final String VIEWS_TAG = "android:menu:list";
56
57 /**
58 * Construct a new ListMenuPresenter.
59 * @param context Context to use for theming. This will supersede the context provided
60 * to initForMenu when this presenter is added.
61 * @param itemLayoutRes Layout resource for individual item views.
62 */
63 public ListMenuPresenter(Context context, int itemLayoutRes) {
64 this(itemLayoutRes, 0);
65 mContext = context;
Adam Powellbc835032011-09-13 15:52:33 -070066 mInflater = LayoutInflater.from(mContext);
Adam Powell696cba52011-03-29 10:38:16 -070067 }
68
69 /**
70 * Construct a new ListMenuPresenter.
71 * @param itemLayoutRes Layout resource for individual item views.
72 * @param themeRes Resource ID of a theme to use for views.
73 */
74 public ListMenuPresenter(int itemLayoutRes, int themeRes) {
75 mItemLayoutRes = itemLayoutRes;
76 mThemeRes = themeRes;
77 }
78
79 @Override
80 public void initForMenu(Context context, MenuBuilder menu) {
81 if (mThemeRes != 0) {
82 mContext = new ContextThemeWrapper(context, mThemeRes);
Adam Powellbc835032011-09-13 15:52:33 -070083 mInflater = LayoutInflater.from(mContext);
Adam Powell1d07e162011-09-07 20:46:24 -070084 } else if (mContext != null) {
Adam Powell696cba52011-03-29 10:38:16 -070085 mContext = context;
Adam Powellbc835032011-09-13 15:52:33 -070086 if (mInflater == null) {
87 mInflater = LayoutInflater.from(mContext);
88 }
Adam Powell696cba52011-03-29 10:38:16 -070089 }
Adam Powell696cba52011-03-29 10:38:16 -070090 mMenu = menu;
Adam Powell28048d02012-06-06 22:46:42 -070091 if (mAdapter != null) {
92 mAdapter.notifyDataSetChanged();
93 }
Adam Powell696cba52011-03-29 10:38:16 -070094 }
95
96 @Override
97 public MenuView getMenuView(ViewGroup root) {
98 if (mMenuView == null) {
99 mMenuView = (ExpandedMenuView) mInflater.inflate(
100 com.android.internal.R.layout.expanded_menu_layout, root, false);
101 if (mAdapter == null) {
102 mAdapter = new MenuAdapter();
103 }
104 mMenuView.setAdapter(mAdapter);
105 mMenuView.setOnItemClickListener(this);
106 }
107 return mMenuView;
108 }
109
110 /**
111 * Call this instead of getMenuView if you want to manage your own ListView.
112 * For proper operation, the ListView hosting this adapter should add
113 * this presenter as an OnItemClickListener.
114 *
115 * @return A ListAdapter containing the items in the menu.
116 */
117 public ListAdapter getAdapter() {
118 if (mAdapter == null) {
119 mAdapter = new MenuAdapter();
120 }
121 return mAdapter;
122 }
123
124 @Override
125 public void updateMenuView(boolean cleared) {
126 if (mAdapter != null) mAdapter.notifyDataSetChanged();
127 }
128
129 @Override
130 public void setCallback(Callback cb) {
131 mCallback = cb;
132 }
133
134 @Override
135 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
136 if (!subMenu.hasVisibleItems()) return false;
137
138 // The window manager will give us a token.
139 new MenuDialogHelper(subMenu).show(null);
140 if (mCallback != null) {
141 mCallback.onOpenSubMenu(subMenu);
142 }
143 return true;
144 }
145
146 @Override
147 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
148 if (mCallback != null) {
149 mCallback.onCloseMenu(menu, allMenusAreClosing);
150 }
151 }
152
153 int getItemIndexOffset() {
154 return mItemIndexOffset;
155 }
156
157 public void setItemIndexOffset(int offset) {
158 mItemIndexOffset = offset;
159 if (mMenuView != null) {
160 updateMenuView(false);
161 }
162 }
163
164 @Override
165 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
166 mMenu.performItemAction(mAdapter.getItem(position), 0);
167 }
168
169 @Override
170 public boolean flagActionItems() {
171 return false;
172 }
173
Adam Powell8d02dea2011-05-31 21:35:13 -0700174 public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
175 return false;
176 }
177
178 public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
179 return false;
180 }
181
Adam Powell696cba52011-03-29 10:38:16 -0700182 public void saveHierarchyState(Bundle outState) {
183 SparseArray<Parcelable> viewStates = new SparseArray<Parcelable>();
184 if (mMenuView != null) {
185 ((View) mMenuView).saveHierarchyState(viewStates);
186 }
187 outState.putSparseParcelableArray(VIEWS_TAG, viewStates);
188 }
189
190 public void restoreHierarchyState(Bundle inState) {
191 SparseArray<Parcelable> viewStates = inState.getSparseParcelableArray(VIEWS_TAG);
Adam Powellf1a45492011-06-17 14:31:34 -0700192 if (viewStates != null) {
193 ((View) mMenuView).restoreHierarchyState(viewStates);
194 }
Adam Powell696cba52011-03-29 10:38:16 -0700195 }
196
Adam Powell11ed1d62011-07-11 21:19:59 -0700197 public void setId(int id) {
198 mId = id;
199 }
200
201 @Override
202 public int getId() {
203 return mId;
204 }
205
206 @Override
207 public Parcelable onSaveInstanceState() {
208 if (mMenuView == null) {
209 return null;
210 }
211
212 Bundle state = new Bundle();
213 saveHierarchyState(state);
214 return state;
215 }
216
217 @Override
218 public void onRestoreInstanceState(Parcelable state) {
219 restoreHierarchyState((Bundle) state);
220 }
221
Adam Powell696cba52011-03-29 10:38:16 -0700222 private class MenuAdapter extends BaseAdapter {
Adam Powell275702c2011-09-23 17:34:04 -0700223 private int mExpandedIndex = -1;
224
225 public MenuAdapter() {
Adam Powell275702c2011-09-23 17:34:04 -0700226 findExpandedIndex();
227 }
228
Adam Powell696cba52011-03-29 10:38:16 -0700229 public int getCount() {
Adam Powell51195b52011-06-21 15:16:32 -0700230 ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
Adam Powell275702c2011-09-23 17:34:04 -0700231 int count = items.size() - mItemIndexOffset;
232 if (mExpandedIndex < 0) {
233 return count;
234 }
235 return count - 1;
Adam Powell696cba52011-03-29 10:38:16 -0700236 }
237
238 public MenuItemImpl getItem(int position) {
Adam Powell51195b52011-06-21 15:16:32 -0700239 ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
Adam Powell275702c2011-09-23 17:34:04 -0700240 position += mItemIndexOffset;
241 if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
242 position++;
243 }
244 return items.get(position);
Adam Powell696cba52011-03-29 10:38:16 -0700245 }
246
247 public long getItemId(int position) {
248 // Since a menu item's ID is optional, we'll use the position as an
249 // ID for the item in the AdapterView
250 return position;
251 }
252
253 public View getView(int position, View convertView, ViewGroup parent) {
254 if (convertView == null) {
255 convertView = mInflater.inflate(mItemLayoutRes, parent, false);
256 }
257
258 MenuView.ItemView itemView = (MenuView.ItemView) convertView;
259 itemView.initialize(getItem(position), 0);
260 return convertView;
261 }
Adam Powell275702c2011-09-23 17:34:04 -0700262
263 void findExpandedIndex() {
264 final MenuItemImpl expandedItem = mMenu.getExpandedItem();
265 if (expandedItem != null) {
266 final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
267 final int count = items.size();
268 for (int i = 0; i < count; i++) {
269 final MenuItemImpl item = items.get(i);
270 if (item == expandedItem) {
271 mExpandedIndex = i;
272 return;
273 }
274 }
275 }
276 mExpandedIndex = -1;
277 }
Adam Powell275702c2011-09-23 17:34:04 -0700278
Adam Powell275702c2011-09-23 17:34:04 -0700279 @Override
Adam Powell76889f32012-05-08 22:22:52 -0700280 public void notifyDataSetChanged() {
281 findExpandedIndex();
282 super.notifyDataSetChanged();
Adam Powell275702c2011-09-23 17:34:04 -0700283 }
Adam Powell696cba52011-03-29 10:38:16 -0700284 }
285}