ListMenuPresenter.java revision 275702c2f47ed87ab642462530092d9777f32ecd
1/*
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.database.DataSetObserver;
21import 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 {
38    private static final String TAG = "ListMenuPresenter";
39
40    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;
51    MenuAdapter mAdapter;
52
53    private int mId;
54
55    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;
66        mInflater = LayoutInflater.from(mContext);
67    }
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);
83            mInflater = LayoutInflater.from(mContext);
84        } else if (mContext != null) {
85            mContext = context;
86            if (mInflater == null) {
87                mInflater = LayoutInflater.from(mContext);
88            }
89        }
90        mMenu = menu;
91    }
92
93    @Override
94    public MenuView getMenuView(ViewGroup root) {
95        if (mMenuView == null) {
96            mMenuView = (ExpandedMenuView) mInflater.inflate(
97                    com.android.internal.R.layout.expanded_menu_layout, root, false);
98            if (mAdapter == null) {
99                mAdapter = new MenuAdapter();
100            }
101            mMenuView.setAdapter(mAdapter);
102            mMenuView.setOnItemClickListener(this);
103        }
104        return mMenuView;
105    }
106
107    /**
108     * Call this instead of getMenuView if you want to manage your own ListView.
109     * For proper operation, the ListView hosting this adapter should add
110     * this presenter as an OnItemClickListener.
111     *
112     * @return A ListAdapter containing the items in the menu.
113     */
114    public ListAdapter getAdapter() {
115        if (mAdapter == null) {
116            mAdapter = new MenuAdapter();
117        }
118        return mAdapter;
119    }
120
121    @Override
122    public void updateMenuView(boolean cleared) {
123        if (mAdapter != null) mAdapter.notifyDataSetChanged();
124    }
125
126    @Override
127    public void setCallback(Callback cb) {
128        mCallback = cb;
129    }
130
131    @Override
132    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
133        if (!subMenu.hasVisibleItems()) return false;
134
135        // The window manager will give us a token.
136        new MenuDialogHelper(subMenu).show(null);
137        if (mCallback != null) {
138            mCallback.onOpenSubMenu(subMenu);
139        }
140        return true;
141    }
142
143    @Override
144    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
145        if (mCallback != null) {
146            mCallback.onCloseMenu(menu, allMenusAreClosing);
147        }
148    }
149
150    int getItemIndexOffset() {
151        return mItemIndexOffset;
152    }
153
154    public void setItemIndexOffset(int offset) {
155        mItemIndexOffset = offset;
156        if (mMenuView != null) {
157            updateMenuView(false);
158        }
159    }
160
161    @Override
162    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
163        mMenu.performItemAction(mAdapter.getItem(position), 0);
164    }
165
166    @Override
167    public boolean flagActionItems() {
168        return false;
169    }
170
171    public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
172        return false;
173    }
174
175    public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
176        return false;
177    }
178
179    public void saveHierarchyState(Bundle outState) {
180        SparseArray<Parcelable> viewStates = new SparseArray<Parcelable>();
181        if (mMenuView != null) {
182            ((View) mMenuView).saveHierarchyState(viewStates);
183        }
184        outState.putSparseParcelableArray(VIEWS_TAG, viewStates);
185    }
186
187    public void restoreHierarchyState(Bundle inState) {
188        SparseArray<Parcelable> viewStates = inState.getSparseParcelableArray(VIEWS_TAG);
189        if (viewStates != null) {
190            ((View) mMenuView).restoreHierarchyState(viewStates);
191        }
192    }
193
194    public void setId(int id) {
195        mId = id;
196    }
197
198    @Override
199    public int getId() {
200        return mId;
201    }
202
203    @Override
204    public Parcelable onSaveInstanceState() {
205        if (mMenuView == null) {
206            return null;
207        }
208
209        Bundle state = new Bundle();
210        saveHierarchyState(state);
211        return state;
212    }
213
214    @Override
215    public void onRestoreInstanceState(Parcelable state) {
216        restoreHierarchyState((Bundle) state);
217    }
218
219    private class MenuAdapter extends BaseAdapter {
220        private int mExpandedIndex = -1;
221
222        public MenuAdapter() {
223            registerDataSetObserver(new ExpandedIndexObserver());
224            findExpandedIndex();
225        }
226
227        public int getCount() {
228            ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
229            int count = items.size() - mItemIndexOffset;
230            if (mExpandedIndex < 0) {
231                return count;
232            }
233            return count - 1;
234        }
235
236        public MenuItemImpl getItem(int position) {
237            ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
238            position += mItemIndexOffset;
239            if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
240                position++;
241            }
242            return items.get(position);
243        }
244
245        public long getItemId(int position) {
246            // Since a menu item's ID is optional, we'll use the position as an
247            // ID for the item in the AdapterView
248            return position;
249        }
250
251        public View getView(int position, View convertView, ViewGroup parent) {
252            if (convertView == null) {
253                convertView = mInflater.inflate(mItemLayoutRes, parent, false);
254            }
255
256            MenuView.ItemView itemView = (MenuView.ItemView) convertView;
257            itemView.initialize(getItem(position), 0);
258            return convertView;
259        }
260
261        void findExpandedIndex() {
262            final MenuItemImpl expandedItem = mMenu.getExpandedItem();
263            if (expandedItem != null) {
264                final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
265                final int count = items.size();
266                for (int i = 0; i < count; i++) {
267                    final MenuItemImpl item = items.get(i);
268                    if (item == expandedItem) {
269                        mExpandedIndex = i;
270                        return;
271                    }
272                }
273            }
274            mExpandedIndex = -1;
275        }
276    }
277
278    private class ExpandedIndexObserver extends DataSetObserver {
279        @Override
280        public void onChanged() {
281            mAdapter.findExpandedIndex();
282        }
283    }
284}
285