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