ActionMenuView.java revision cf0357639e952a87f0d535c82691919af81f058b
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.view.ViewParent;
28import android.widget.ImageButton;
29import android.widget.ImageView;
30import android.widget.LinearLayout;
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    private class OpenOverflowRunnable implements Runnable {
63        private MenuPopupHelper mPopup;
64
65        public OpenOverflowRunnable(MenuPopupHelper popup) {
66            mPopup = popup;
67        }
68
69        public void run() {
70            mOverflowPopup = mPopup;
71            mPopup.show();
72            mPostedOpenRunnable = null;
73        }
74    }
75
76    private OpenOverflowRunnable mPostedOpenRunnable;
77
78    public ActionMenuView(Context context) {
79        this(context, null);
80    }
81
82    public ActionMenuView(Context context, AttributeSet attrs) {
83        super(context, attrs);
84
85        final Resources res = getResources();
86
87        // Measure for initial configuration
88        mMaxItems = getMaxActionButtons();
89
90        // TODO There has to be a better way to indicate that we don't have a hard menu key.
91        final int screen = res.getConfiguration().screenLayout;
92        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
93                Configuration.SCREENLAYOUT_SIZE_XLARGE;
94
95        TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
96        final int buttonStyle = a.getResourceId(
97                com.android.internal.R.styleable.Theme_actionButtonStyle, 0);
98        mDivider = a.getDrawable(com.android.internal.R.styleable.Theme_dividerVertical);
99        a.recycle();
100
101        a = context.obtainStyledAttributes(buttonStyle, com.android.internal.R.styleable.View);
102        mButtonPaddingLeft = a.getDimension(com.android.internal.R.styleable.View_paddingLeft, 0);
103        mButtonPaddingRight = a.getDimension(com.android.internal.R.styleable.View_paddingRight, 0);
104        a.recycle();
105
106        mDividerPadding = DIVIDER_PADDING * res.getDisplayMetrics().density;
107
108        setBaselineAligned(false);
109    }
110
111    @Override
112    public void onConfigurationChanged(Configuration newConfig) {
113        super.onConfigurationChanged(newConfig);
114        final int screen = newConfig.screenLayout;
115        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
116                Configuration.SCREENLAYOUT_SIZE_XLARGE;
117        mMaxItems = getMaxActionButtons();
118        if (mMenu != null) {
119            mMenu.setMaxActionItems(mMaxItems);
120            updateChildren(false);
121        }
122
123        if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
124            mOverflowPopup.dismiss();
125            post(mShowOverflow);
126        }
127    }
128
129    @Override
130    public void onDetachedFromWindow() {
131        super.onDetachedFromWindow();
132        if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
133            mOverflowPopup.dismiss();
134        }
135    }
136
137    private int getMaxActionButtons() {
138        return getResources().getInteger(com.android.internal.R.integer.max_action_buttons);
139    }
140
141    public boolean isOverflowReserved() {
142        return mReserveOverflow;
143    }
144
145    public void setOverflowReserved(boolean reserveOverflow) {
146        mReserveOverflow = reserveOverflow;
147    }
148
149    @Override
150    protected LayoutParams generateDefaultLayoutParams() {
151        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
152                LayoutParams.WRAP_CONTENT);
153        params.gravity = Gravity.CENTER_VERTICAL;
154        return params;
155    }
156
157    @Override
158    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
159        return generateDefaultLayoutParams();
160    }
161
162    public boolean invokeItem(MenuItemImpl item) {
163        return mMenu.performItemAction(item, 0);
164    }
165
166    public int getWindowAnimations() {
167        return 0;
168    }
169
170    public void initialize(MenuBuilder menu, int menuType) {
171        menu.setMaxActionItems(mMaxItems);
172        mMenu = menu;
173        updateChildren(true);
174    }
175
176    public void updateChildren(boolean cleared) {
177        final boolean reserveOverflow = mReserveOverflow;
178        removeAllViews();
179
180        final ArrayList<MenuItemImpl> itemsToShow = mMenu.getActionItems(reserveOverflow);
181        final int itemCount = itemsToShow.size();
182
183        boolean needsDivider = false;
184        for (int i = 0; i < itemCount; i++) {
185            if (needsDivider) {
186                addView(makeDividerView(), makeDividerLayoutParams());
187            }
188            final MenuItemImpl itemData = itemsToShow.get(i);
189            final View actionView = itemData.getActionView();
190            if (actionView != null) {
191                final ViewParent parent = actionView.getParent();
192                if (parent instanceof ViewGroup) {
193                    ((ViewGroup) parent).removeView(actionView);
194                }
195                addView(actionView, makeActionViewLayoutParams());
196            } else {
197                needsDivider = addItemView(i == 0 || !needsDivider,
198                        (ActionMenuItemView) itemData.getItemView(
199                                MenuBuilder.TYPE_ACTION_BUTTON, this));
200            }
201        }
202
203        if (reserveOverflow) {
204            if (mMenu.getNonActionItems(true).size() > 0) {
205                OverflowMenuButton button = new OverflowMenuButton(mContext);
206                addView(button);
207                mOverflowButton = button;
208            } else {
209                mOverflowButton = null;
210            }
211        }
212    }
213
214    public boolean showOverflowMenu() {
215        if (mOverflowButton != null && !isOverflowMenuShowing()) {
216            mMenu.getCallback().onMenuModeChange(mMenu);
217            return true;
218        }
219        return false;
220    }
221
222    public void openOverflowMenu() {
223        OverflowPopup popup = new OverflowPopup(getContext(), mMenu, mOverflowButton, true);
224        mPostedOpenRunnable = new OpenOverflowRunnable(popup);
225        // Post this for later; we might still need a layout for the anchor to be right.
226        post(mPostedOpenRunnable);
227    }
228
229    public boolean isOverflowMenuShowing() {
230        return mOverflowPopup != null && mOverflowPopup.isShowing();
231    }
232
233    public boolean isOverflowMenuOpen() {
234        return mOverflowPopup != null;
235    }
236
237    public boolean hideOverflowMenu() {
238        if (mPostedOpenRunnable != null) {
239            removeCallbacks(mPostedOpenRunnable);
240            return true;
241        }
242
243        MenuPopupHelper popup = mOverflowPopup;
244        if (popup != null) {
245            popup.dismiss();
246            return true;
247        }
248        return false;
249    }
250
251    private boolean addItemView(boolean needsDivider, ActionMenuItemView view) {
252        view.setItemInvoker(this);
253        boolean hasText = view.hasText();
254
255        if (hasText && needsDivider) {
256            addView(makeDividerView(), makeDividerLayoutParams());
257        }
258        addView(view);
259        return hasText;
260    }
261
262    private ImageView makeDividerView() {
263        ImageView result = new ImageView(mContext);
264        result.setImageDrawable(mDivider);
265        result.setScaleType(ImageView.ScaleType.FIT_XY);
266        return result;
267    }
268
269    private LayoutParams makeDividerLayoutParams() {
270        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
271                LayoutParams.MATCH_PARENT);
272        params.topMargin = (int) mDividerPadding;
273        params.bottomMargin = (int) mDividerPadding;
274        return params;
275    }
276
277    private LayoutParams makeActionViewLayoutParams() {
278        LayoutParams params = generateDefaultLayoutParams();
279        params.leftMargin = (int) mButtonPaddingLeft;
280        params.rightMargin = (int) mButtonPaddingRight;
281        return params;
282    }
283
284    private class OverflowMenuButton extends ImageButton {
285        public OverflowMenuButton(Context context) {
286            super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
287
288            setClickable(true);
289            setFocusable(true);
290            setVisibility(VISIBLE);
291            setEnabled(true);
292        }
293
294        @Override
295        public boolean performClick() {
296            if (super.performClick()) {
297                return true;
298            }
299
300            showOverflowMenu();
301            return true;
302        }
303    }
304
305    private class OverflowPopup extends MenuPopupHelper {
306        public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
307                boolean overflowOnly) {
308            super(context, menu, anchorView, overflowOnly);
309        }
310
311        @Override
312        public void onDismiss() {
313            super.onDismiss();
314            mMenu.getCallback().onCloseMenu(mMenu, true);
315            mOverflowPopup = null;
316        }
317    }
318}
319