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.database.DataSetObserver;
     21 import android.os.Bundle;
     22 import android.os.Parcelable;
     23 import android.util.SparseArray;
     24 import android.view.ContextThemeWrapper;
     25 import android.view.LayoutInflater;
     26 import android.view.View;
     27 import android.view.ViewGroup;
     28 import android.widget.AdapterView;
     29 import android.widget.BaseAdapter;
     30 import android.widget.ListAdapter;
     31 
     32 import java.util.ArrayList;
     33 
     34 /**
     35  * MenuPresenter for list-style menus.
     36  */
     37 public 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         if (mAdapter != null) {
     92             mAdapter.notifyDataSetChanged();
     93         }
     94     }
     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 
    174     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 
    182     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);
    192         if (viewStates != null) {
    193             ((View) mMenuView).restoreHierarchyState(viewStates);
    194         }
    195     }
    196 
    197     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 
    222     private class MenuAdapter extends BaseAdapter {
    223         private int mExpandedIndex = -1;
    224 
    225         public MenuAdapter() {
    226             findExpandedIndex();
    227         }
    228 
    229         public int getCount() {
    230             ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
    231             int count = items.size() - mItemIndexOffset;
    232             if (mExpandedIndex < 0) {
    233                 return count;
    234             }
    235             return count - 1;
    236         }
    237 
    238         public MenuItemImpl getItem(int position) {
    239             ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
    240             position += mItemIndexOffset;
    241             if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
    242                 position++;
    243             }
    244             return items.get(position);
    245         }
    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         }
    262 
    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         }
    278 
    279         @Override
    280         public void notifyDataSetChanged() {
    281             findExpandedIndex();
    282             super.notifyDataSetChanged();
    283         }
    284     }
    285 }
    286