ListMenuPresenter.java revision c0cc68053e5013c207be6f64ffd2ad9e8c467def
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.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 {
37    private static final String TAG = "ListMenuPresenter";
38
39    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;
50    MenuAdapter mAdapter;
51
52    private int mId;
53
54    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;
65        mInflater = LayoutInflater.from(mContext);
66    }
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);
82            mInflater = LayoutInflater.from(mContext);
83        } else if (mContext != null) {
84            mContext = context;
85            if (mInflater == null) {
86                mInflater = LayoutInflater.from(mContext);
87            }
88        }
89        mMenu = menu;
90        if (mAdapter != null) {
91            mAdapter.notifyDataSetChanged();
92        }
93    }
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) {
165        mMenu.performItemAction(mAdapter.getItem(position), this, 0);
166    }
167
168    @Override
169    public boolean flagActionItems() {
170        return false;
171    }
172
173    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
181    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);
191        if (viewStates != null) {
192            ((View) mMenuView).restoreHierarchyState(viewStates);
193        }
194    }
195
196    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
221    private class MenuAdapter extends BaseAdapter {
222        private int mExpandedIndex = -1;
223
224        public MenuAdapter() {
225            findExpandedIndex();
226        }
227
228        public int getCount() {
229            ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
230            int count = items.size() - mItemIndexOffset;
231            if (mExpandedIndex < 0) {
232                return count;
233            }
234            return count - 1;
235        }
236
237        public MenuItemImpl getItem(int position) {
238            ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
239            position += mItemIndexOffset;
240            if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
241                position++;
242            }
243            return items.get(position);
244        }
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        }
261
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        }
277
278        @Override
279        public void notifyDataSetChanged() {
280            findExpandedIndex();
281            super.notifyDataSetChanged();
282        }
283    }
284}
285