ActionMenuView.java revision be4d68e7b238b8ee879de0481e39c40d3f1683b6
1/*
2 * Copyright (C) 2010 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 */
16package com.android.internal.view.menu;
17
18import android.content.Context;
19import android.content.res.Configuration;
20import android.content.res.Resources;
21import android.content.res.TypedArray;
22import android.graphics.drawable.Drawable;
23import android.util.AttributeSet;
24import android.view.Gravity;
25import android.view.View;
26import android.view.ViewGroup;
27import android.widget.ImageButton;
28import android.widget.ImageView;
29import android.widget.LinearLayout;
30import android.widget.LinearLayout.LayoutParams;
31
32import java.util.ArrayList;
33
34/**
35 * @hide
36 */
37public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvoker, MenuView {
38    private static final String TAG = "ActionMenuView";
39
40    // TODO Theme/style this.
41    private static final int DIVIDER_PADDING = 12; // dips
42
43    private MenuBuilder mMenu;
44
45    private int mMaxItems;
46    private boolean mReserveOverflow;
47    private OverflowMenuButton mOverflowButton;
48    private MenuPopupHelper mOverflowPopup;
49
50    private float mButtonPaddingLeft;
51    private float mButtonPaddingRight;
52    private float mDividerPadding;
53
54    private Drawable mDivider;
55
56    private Runnable mShowOverflow = new Runnable() {
57        public void run() {
58            showOverflowMenu();
59        }
60    };
61
62    public ActionMenuView(Context context) {
63        this(context, null);
64    }
65
66    public ActionMenuView(Context context, AttributeSet attrs) {
67        super(context, attrs);
68
69        final Resources res = getResources();
70
71        // Measure for initial configuration
72        mMaxItems = getMaxActionButtons();
73
74        // TODO There has to be a better way to indicate that we don't have a hard menu key.
75        final int screen = res.getConfiguration().screenLayout;
76        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
77                Configuration.SCREENLAYOUT_SIZE_XLARGE;
78
79        TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
80        final int buttonStyle = a.getResourceId(
81                com.android.internal.R.styleable.Theme_actionButtonStyle, 0);
82        final int groupStyle = a.getResourceId(
83                com.android.internal.R.styleable.Theme_buttonGroupStyle, 0);
84        a.recycle();
85
86        a = context.obtainStyledAttributes(buttonStyle, com.android.internal.R.styleable.View);
87        mButtonPaddingLeft = a.getDimension(com.android.internal.R.styleable.View_paddingLeft, 0);
88        mButtonPaddingRight = a.getDimension(com.android.internal.R.styleable.View_paddingRight, 0);
89        a.recycle();
90
91        a = context.obtainStyledAttributes(groupStyle,
92                com.android.internal.R.styleable.ButtonGroup);
93        mDivider = a.getDrawable(com.android.internal.R.styleable.ButtonGroup_divider);
94        a.recycle();
95
96        mDividerPadding = DIVIDER_PADDING * res.getDisplayMetrics().density;
97    }
98
99    @Override
100    public void onConfigurationChanged(Configuration newConfig) {
101        final int screen = newConfig.screenLayout;
102        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
103                Configuration.SCREENLAYOUT_SIZE_XLARGE;
104        mMaxItems = getMaxActionButtons();
105        if (mMenu != null) {
106            mMenu.setMaxActionItems(mMaxItems);
107            updateChildren(false);
108        }
109
110        if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
111            mOverflowPopup.dismiss();
112            post(mShowOverflow);
113        }
114    }
115
116    private int getMaxActionButtons() {
117        return getResources().getInteger(com.android.internal.R.integer.max_action_buttons);
118    }
119
120    public boolean isOverflowReserved() {
121        return mReserveOverflow;
122    }
123
124    public void setOverflowReserved(boolean reserveOverflow) {
125        mReserveOverflow = reserveOverflow;
126    }
127
128    @Override
129    protected LayoutParams generateDefaultLayoutParams() {
130        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
131                LayoutParams.WRAP_CONTENT);
132        params.gravity = Gravity.CENTER_VERTICAL;
133        return params;
134    }
135
136    @Override
137    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
138        return generateDefaultLayoutParams();
139    }
140
141    public boolean invokeItem(MenuItemImpl item) {
142        return mMenu.performItemAction(item, 0);
143    }
144
145    public int getWindowAnimations() {
146        return 0;
147    }
148
149    public void initialize(MenuBuilder menu, int menuType) {
150        menu.setMaxActionItems(mMaxItems);
151        mMenu = menu;
152        updateChildren(true);
153    }
154
155    public void updateChildren(boolean cleared) {
156        final boolean reserveOverflow = mReserveOverflow;
157        removeAllViews();
158
159        final ArrayList<MenuItemImpl> itemsToShow = mMenu.getActionItems(reserveOverflow);
160        final int itemCount = itemsToShow.size();
161
162        boolean needsDivider = false;
163        for (int i = 0; i < itemCount; i++) {
164            if (needsDivider) {
165                addView(makeDividerView(), makeDividerLayoutParams());
166            }
167            final MenuItemImpl itemData = itemsToShow.get(i);
168            final View actionView = itemData.getActionView();
169            if (actionView != null) {
170                addView(actionView, makeActionViewLayoutParams());
171            } else {
172                needsDivider = addItemView(i == 0 || !needsDivider,
173                        (ActionMenuItemView) itemData.getItemView(
174                                MenuBuilder.TYPE_ACTION_BUTTON, this));
175            }
176        }
177
178        if (reserveOverflow) {
179            if (mMenu.getNonActionItems(true).size() > 0) {
180                OverflowMenuButton button = new OverflowMenuButton(mContext);
181                addView(button);
182                mOverflowButton = button;
183            } else {
184                mOverflowButton = null;
185            }
186        }
187    }
188
189    public boolean showOverflowMenu() {
190        if (mOverflowButton != null) {
191            final MenuPopupHelper popup =
192                    new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
193            // Post this for later; we might still need a layout for the anchor to be right.
194            post(new Runnable() {
195                public void run() {
196                    popup.show();
197                }
198            });
199            mOverflowPopup = popup;
200            return true;
201        }
202        return false;
203    }
204
205    public boolean isOverflowMenuShowing() {
206        MenuPopupHelper popup = mOverflowPopup;
207        if (popup != null) {
208            return popup.isShowing();
209        }
210        return false;
211    }
212
213    public boolean hideOverflowMenu() {
214        MenuPopupHelper popup = mOverflowPopup;
215        if (popup != null) {
216            popup.dismiss();
217            return true;
218        }
219        return false;
220    }
221
222    private boolean addItemView(boolean needsDivider, ActionMenuItemView view) {
223        view.setItemInvoker(this);
224        boolean hasText = view.hasText();
225
226        if (hasText && needsDivider) {
227            addView(makeDividerView(), makeDividerLayoutParams());
228        }
229        addView(view);
230        return hasText;
231    }
232
233    private ImageView makeDividerView() {
234        ImageView result = new ImageView(mContext);
235        result.setImageDrawable(mDivider);
236        result.setScaleType(ImageView.ScaleType.FIT_XY);
237        return result;
238    }
239
240    private LayoutParams makeDividerLayoutParams() {
241        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
242                LayoutParams.MATCH_PARENT);
243        params.topMargin = (int) mDividerPadding;
244        params.bottomMargin = (int) mDividerPadding;
245        return params;
246    }
247
248    private LayoutParams makeActionViewLayoutParams() {
249        LayoutParams params = generateDefaultLayoutParams();
250        params.leftMargin = (int) mButtonPaddingLeft;
251        params.rightMargin = (int) mButtonPaddingRight;
252        return params;
253    }
254
255    private class OverflowMenuButton extends ImageButton {
256        public OverflowMenuButton(Context context) {
257            super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
258
259            setClickable(true);
260            setFocusable(true);
261            setVisibility(VISIBLE);
262            setEnabled(true);
263        }
264
265        @Override
266        public boolean performClick() {
267            if (super.performClick()) {
268                return true;
269            }
270
271            // Change to overflow mode
272            mMenu.getCallback().onMenuModeChange(mMenu);
273            return true;
274        }
275    }
276}
277