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