Home | History | Annotate | Download | only in menu
      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 
     17 package com.android.internal.view.menu;
     18 
     19 import android.content.Context;
     20 import android.os.Bundle;
     21 import android.os.Parcelable;
     22 import android.util.SparseArray;
     23 import android.view.ContextThemeWrapper;
     24 import android.view.LayoutInflater;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 import android.widget.AdapterView;
     28 import android.widget.BaseAdapter;
     29 import android.widget.ListAdapter;
     30 
     31 import java.util.ArrayList;
     32 
     33 /**
     34  * MenuPresenter for list-style menus.
     35  */
     36 public 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