1/*
2 * Copyright (C) 2006 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
17package com.android.internal.view.menu;
18
19import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
20
21import android.content.Context;
22import android.content.res.Resources;
23import android.content.res.TypedArray;
24import android.graphics.Canvas;
25import android.graphics.Rect;
26import android.graphics.drawable.Drawable;
27import android.os.Parcel;
28import android.os.Parcelable;
29import android.util.AttributeSet;
30import android.view.KeyEvent;
31import android.view.View;
32import android.view.ViewConfiguration;
33import android.view.ViewGroup;
34import android.view.LayoutInflater;
35
36import java.util.ArrayList;
37
38/**
39 * The icon menu view is an icon-based menu usually with a subset of all the menu items.
40 * It is opened as the default menu, and shows either the first five or all six of the menu items
41 * with text and icon.  In the situation of there being more than six items, the first five items
42 * will be accompanied with a 'More' button that opens an {@link ExpandedMenuView} which lists
43 * all the menu items.
44 *
45 * @attr ref android.R.styleable#IconMenuView_rowHeight
46 * @attr ref android.R.styleable#IconMenuView_maxRows
47 * @attr ref android.R.styleable#IconMenuView_maxItemsPerRow
48 *
49 * @hide
50 */
51public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuView, Runnable {
52    private static final int ITEM_CAPTION_CYCLE_DELAY = 1000;
53
54    private MenuBuilder mMenu;
55
56    /** Height of each row */
57    private int mRowHeight;
58    /** Maximum number of rows to be shown */
59    private int mMaxRows;
60    /** Maximum number of items to show in the icon menu. */
61    private int mMaxItems;
62    /** Maximum number of items per row */
63    private int mMaxItemsPerRow;
64    /** Actual number of items (the 'More' view does not count as an item) shown */
65    private int mNumActualItemsShown;
66
67    /** Divider that is drawn between all rows */
68    private Drawable mHorizontalDivider;
69    /** Height of the horizontal divider */
70    private int mHorizontalDividerHeight;
71    /** Set of horizontal divider positions where the horizontal divider will be drawn */
72    private ArrayList<Rect> mHorizontalDividerRects;
73
74    /** Divider that is drawn between all columns */
75    private Drawable mVerticalDivider;
76    /** Width of the vertical divider */
77    private int mVerticalDividerWidth;
78    /** Set of vertical divider positions where the vertical divider will be drawn */
79    private ArrayList<Rect> mVerticalDividerRects;
80
81    /** Icon for the 'More' button */
82    private Drawable mMoreIcon;
83
84    /** Background of each item (should contain the selected and focused states) */
85    private Drawable mItemBackground;
86
87    /** Default animations for this menu */
88    private int mAnimations;
89
90    /**
91     * Whether this IconMenuView has stale children and needs to update them.
92     * Set true by {@link #markStaleChildren()} and reset to false by
93     * {@link #onMeasure(int, int)}
94     */
95    private boolean mHasStaleChildren;
96
97    /**
98     * Longpress on MENU (while this is shown) switches to shortcut caption
99     * mode. When the user releases the longpress, we do not want to pass the
100     * key-up event up since that will dismiss the menu.
101     */
102    private boolean mMenuBeingLongpressed = false;
103
104    /**
105     * While {@link #mMenuBeingLongpressed}, we toggle the children's caption
106     * mode between each's title and its shortcut. This is the last caption mode
107     * we broadcasted to children.
108     */
109    private boolean mLastChildrenCaptionMode;
110
111    /**
112     * The layout to use for menu items. Each index is the row number (0 is the
113     * top-most). Each value contains the number of items in that row.
114     * <p>
115     * The length of this array should not be used to get the number of rows in
116     * the current layout, instead use {@link #mLayoutNumRows}.
117     */
118    private int[] mLayout;
119
120    /**
121     * The number of rows in the current layout.
122     */
123    private int mLayoutNumRows;
124
125    /**
126     * Instantiates the IconMenuView that is linked with the provided MenuBuilder.
127     */
128    public IconMenuView(Context context, AttributeSet attrs) {
129        super(context, attrs);
130
131        TypedArray a =
132            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.IconMenuView, 0, 0);
133        mRowHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.IconMenuView_rowHeight, 64);
134        mMaxRows = a.getInt(com.android.internal.R.styleable.IconMenuView_maxRows, 2);
135        mMaxItems = a.getInt(com.android.internal.R.styleable.IconMenuView_maxItems, 6);
136        mMaxItemsPerRow = a.getInt(com.android.internal.R.styleable.IconMenuView_maxItemsPerRow, 3);
137        mMoreIcon = a.getDrawable(com.android.internal.R.styleable.IconMenuView_moreIcon);
138        a.recycle();
139
140        a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.MenuView, 0, 0);
141        mItemBackground = a.getDrawable(com.android.internal.R.styleable.MenuView_itemBackground);
142        mHorizontalDivider = a.getDrawable(com.android.internal.R.styleable.MenuView_horizontalDivider);
143        mHorizontalDividerRects = new ArrayList<Rect>();
144        mVerticalDivider =  a.getDrawable(com.android.internal.R.styleable.MenuView_verticalDivider);
145        mVerticalDividerRects = new ArrayList<Rect>();
146        mAnimations = a.getResourceId(com.android.internal.R.styleable.MenuView_windowAnimationStyle, 0);
147        a.recycle();
148
149        if (mHorizontalDivider != null) {
150            mHorizontalDividerHeight = mHorizontalDivider.getIntrinsicHeight();
151            // Make sure to have some height for the divider
152            if (mHorizontalDividerHeight == -1) mHorizontalDividerHeight = 1;
153        }
154
155        if (mVerticalDivider != null) {
156            mVerticalDividerWidth = mVerticalDivider.getIntrinsicWidth();
157            // Make sure to have some width for the divider
158            if (mVerticalDividerWidth == -1) mVerticalDividerWidth = 1;
159        }
160
161        mLayout = new int[mMaxRows];
162
163        // This view will be drawing the dividers
164        setWillNotDraw(false);
165
166        // This is so we'll receive the MENU key in touch mode
167        setFocusableInTouchMode(true);
168        // This is so our children can still be arrow-key focused
169        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
170    }
171
172    int getMaxItems() {
173        return mMaxItems;
174    }
175
176    /**
177     * Figures out the layout for the menu items.
178     *
179     * @param width The available width for the icon menu.
180     */
181    private void layoutItems(int width) {
182        int numItems = getChildCount();
183        if (numItems == 0) {
184            mLayoutNumRows = 0;
185            return;
186        }
187
188        // Start with the least possible number of rows
189        int curNumRows =
190                Math.min((int) Math.ceil(numItems / (float) mMaxItemsPerRow), mMaxRows);
191
192        /*
193         * Increase the number of rows until we find a configuration that fits
194         * all of the items' titles. Worst case, we use mMaxRows.
195         */
196        for (; curNumRows <= mMaxRows; curNumRows++) {
197            layoutItemsUsingGravity(curNumRows, numItems);
198
199            if (curNumRows >= numItems) {
200                // Can't have more rows than items
201                break;
202            }
203
204            if (doItemsFit()) {
205                // All the items fit, so this is a good configuration
206                break;
207            }
208        }
209    }
210
211    /**
212     * Figures out the layout for the menu items by equally distributing, and
213     * adding any excess items equally to lower rows.
214     *
215     * @param numRows The total number of rows for the menu view
216     * @param numItems The total number of items (across all rows) contained in
217     *            the menu view
218     * @return int[] Where the value of index i contains the number of items for row i
219     */
220    private void layoutItemsUsingGravity(int numRows, int numItems) {
221        int numBaseItemsPerRow = numItems / numRows;
222        int numLeftoverItems = numItems % numRows;
223        /**
224         * The bottom rows will each get a leftover item. Rows (indexed at 0)
225         * that are >= this get a leftover item. Note: if there are 0 leftover
226         * items, no rows will get them since this value will be greater than
227         * the last row.
228         */
229        int rowsThatGetALeftoverItem = numRows - numLeftoverItems;
230
231        int[] layout = mLayout;
232        for (int i = 0; i < numRows; i++) {
233            layout[i] = numBaseItemsPerRow;
234
235            // Fill the bottom rows with a leftover item each
236            if (i >= rowsThatGetALeftoverItem) {
237                layout[i]++;
238            }
239        }
240
241        mLayoutNumRows = numRows;
242    }
243
244    /**
245     * Checks whether each item's title is fully visible using the current
246     * layout.
247     *
248     * @return True if the items fit (each item's text is fully visible), false
249     *         otherwise.
250     */
251    private boolean doItemsFit() {
252        int itemPos = 0;
253
254        int[] layout = mLayout;
255        int numRows = mLayoutNumRows;
256        for (int row = 0; row < numRows; row++) {
257            int numItemsOnRow = layout[row];
258
259            /*
260             * If there is only one item on this row, increasing the
261             * number of rows won't help.
262             */
263            if (numItemsOnRow == 1) {
264                itemPos++;
265                continue;
266            }
267
268            for (int itemsOnRowCounter = numItemsOnRow; itemsOnRowCounter > 0;
269                    itemsOnRowCounter--) {
270                View child = getChildAt(itemPos++);
271                LayoutParams lp = (LayoutParams) child.getLayoutParams();
272                if (lp.maxNumItemsOnRow < numItemsOnRow) {
273                    return false;
274                }
275            }
276        }
277
278        return true;
279    }
280
281    Drawable getItemBackgroundDrawable() {
282        return mItemBackground.getConstantState().newDrawable(getContext().getResources());
283    }
284
285    /**
286     * Creates the item view for the 'More' button which is used to switch to
287     * the expanded menu view. This button is a special case since it does not
288     * have a MenuItemData backing it.
289     * @return The IconMenuItemView for the 'More' button
290     */
291    IconMenuItemView createMoreItemView() {
292        Context context = getContext();
293        LayoutInflater inflater = LayoutInflater.from(context);
294
295        final IconMenuItemView itemView = (IconMenuItemView) inflater.inflate(
296                com.android.internal.R.layout.icon_menu_item_layout, null);
297
298        Resources r = context.getResources();
299        itemView.initialize(r.getText(com.android.internal.R.string.more_item_label), mMoreIcon);
300
301        // Set up a click listener on the view since there will be no invocation sequence
302        // due to the lack of a MenuItemData this view
303        itemView.setOnClickListener(new OnClickListener() {
304            public void onClick(View v) {
305                // Switches the menu to expanded mode. Requires support from
306                // the menu's active callback.
307                mMenu.changeMenuMode();
308            }
309        });
310
311        return itemView;
312    }
313
314
315    public void initialize(MenuBuilder menu) {
316        mMenu = menu;
317    }
318
319    /**
320     * The positioning algorithm that gets called from onMeasure.  It
321     * just computes positions for each child, and then stores them in the child's layout params.
322     * @param menuWidth The width of this menu to assume for positioning
323     * @param menuHeight The height of this menu to assume for positioning
324     */
325    private void positionChildren(int menuWidth, int menuHeight) {
326        // Clear the containers for the positions where the dividers should be drawn
327        if (mHorizontalDivider != null) mHorizontalDividerRects.clear();
328        if (mVerticalDivider != null) mVerticalDividerRects.clear();
329
330        // Get the minimum number of rows needed
331        final int numRows = mLayoutNumRows;
332        final int numRowsMinus1 = numRows - 1;
333        final int numItemsForRow[] = mLayout;
334
335        // The item position across all rows
336        int itemPos = 0;
337        View child;
338        IconMenuView.LayoutParams childLayoutParams = null;
339
340        // Use float for this to get precise positions (uniform item widths
341        // instead of last one taking any slack), and then convert to ints at last opportunity
342        float itemLeft;
343        float itemTop = 0;
344        // Since each row can have a different number of items, this will be computed per row
345        float itemWidth;
346        // Subtract the space needed for the horizontal dividers
347        final float itemHeight = (menuHeight - mHorizontalDividerHeight * (numRows - 1))
348                / (float)numRows;
349
350        for (int row = 0; row < numRows; row++) {
351            // Start at the left
352            itemLeft = 0;
353
354            // Subtract the space needed for the vertical dividers, and divide by the number of items
355            itemWidth = (menuWidth - mVerticalDividerWidth * (numItemsForRow[row] - 1))
356                    / (float)numItemsForRow[row];
357
358            for (int itemPosOnRow = 0; itemPosOnRow < numItemsForRow[row]; itemPosOnRow++) {
359                // Tell the child to be exactly this size
360                child = getChildAt(itemPos);
361                child.measure(MeasureSpec.makeMeasureSpec((int) itemWidth, MeasureSpec.EXACTLY),
362                        MeasureSpec.makeMeasureSpec((int) itemHeight, MeasureSpec.EXACTLY));
363
364                // Remember the child's position for layout
365                childLayoutParams = (IconMenuView.LayoutParams) child.getLayoutParams();
366                childLayoutParams.left = (int) itemLeft;
367                childLayoutParams.right = (int) (itemLeft + itemWidth);
368                childLayoutParams.top = (int) itemTop;
369                childLayoutParams.bottom = (int) (itemTop + itemHeight);
370
371                // Increment by item width
372                itemLeft += itemWidth;
373                itemPos++;
374
375                // Add a vertical divider to draw
376                if (mVerticalDivider != null) {
377                    mVerticalDividerRects.add(new Rect((int) itemLeft,
378                            (int) itemTop, (int) (itemLeft + mVerticalDividerWidth),
379                            (int) (itemTop + itemHeight)));
380                }
381
382                // Increment by divider width (even if we're not computing
383                // dividers, since we need to leave room for them when
384                // calculating item positions)
385                itemLeft += mVerticalDividerWidth;
386            }
387
388            // Last child on each row should extend to very right edge
389            if (childLayoutParams != null) {
390                childLayoutParams.right = menuWidth;
391            }
392
393            itemTop += itemHeight;
394
395            // Add a horizontal divider to draw
396            if ((mHorizontalDivider != null) && (row < numRowsMinus1)) {
397                mHorizontalDividerRects.add(new Rect(0, (int) itemTop, menuWidth,
398                        (int) (itemTop + mHorizontalDividerHeight)));
399
400                itemTop += mHorizontalDividerHeight;
401            }
402        }
403    }
404
405    @Override
406    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
407        int measuredWidth = resolveSize(Integer.MAX_VALUE, widthMeasureSpec);
408        calculateItemFittingMetadata(measuredWidth);
409        layoutItems(measuredWidth);
410
411        // Get the desired height of the icon menu view (last row of items does
412        // not have a divider below)
413        final int layoutNumRows = mLayoutNumRows;
414        final int desiredHeight = (mRowHeight + mHorizontalDividerHeight) *
415                layoutNumRows - mHorizontalDividerHeight;
416
417        // Maximum possible width and desired height
418        setMeasuredDimension(measuredWidth,
419                resolveSize(desiredHeight, heightMeasureSpec));
420
421        // Position the children
422        if (layoutNumRows > 0) {
423            positionChildren(getMeasuredWidth(), getMeasuredHeight());
424        }
425    }
426
427
428    @Override
429    protected void onLayout(boolean changed, int l, int t, int r, int b) {
430        View child;
431        IconMenuView.LayoutParams childLayoutParams;
432
433        for (int i = getChildCount() - 1; i >= 0; i--) {
434            child = getChildAt(i);
435            childLayoutParams = (IconMenuView.LayoutParams)child
436                    .getLayoutParams();
437
438            // Layout children according to positions set during the measure
439            child.layout(childLayoutParams.left, childLayoutParams.top, childLayoutParams.right,
440                    childLayoutParams.bottom);
441        }
442    }
443
444    @Override
445    protected void onDraw(Canvas canvas) {
446        Drawable drawable = mHorizontalDivider;
447        if (drawable != null) {
448            // If we have a horizontal divider to draw, draw it at the remembered positions
449            final ArrayList<Rect> rects = mHorizontalDividerRects;
450            for (int i = rects.size() - 1; i >= 0; i--) {
451                drawable.setBounds(rects.get(i));
452                drawable.draw(canvas);
453            }
454        }
455
456        drawable = mVerticalDivider;
457        if (drawable != null) {
458            // If we have a vertical divider to draw, draw it at the remembered positions
459            final ArrayList<Rect> rects = mVerticalDividerRects;
460            for (int i = rects.size() - 1; i >= 0; i--) {
461                drawable.setBounds(rects.get(i));
462                drawable.draw(canvas);
463            }
464        }
465    }
466
467    public boolean invokeItem(MenuItemImpl item) {
468        return mMenu.performItemAction(item, 0);
469    }
470
471    @Override
472    public LayoutParams generateLayoutParams(AttributeSet attrs) {
473        return new IconMenuView.LayoutParams(getContext(), attrs);
474    }
475
476    @Override
477    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
478        // Override to allow type-checking of LayoutParams.
479        return p instanceof IconMenuView.LayoutParams;
480    }
481
482    /**
483     * Marks as having stale children.
484     */
485    void markStaleChildren() {
486        if (!mHasStaleChildren) {
487            mHasStaleChildren = true;
488            requestLayout();
489        }
490    }
491
492    /**
493     * @return The number of actual items shown (those that are backed by an
494     *         {@link MenuView.ItemView} implementation--eg: excludes More
495     *         item).
496     */
497    int getNumActualItemsShown() {
498        return mNumActualItemsShown;
499    }
500
501    void setNumActualItemsShown(int count) {
502        mNumActualItemsShown = count;
503    }
504
505    public int getWindowAnimations() {
506        return mAnimations;
507    }
508
509    /**
510     * Returns the number of items per row.
511     * <p>
512     * This should only be used for testing.
513     *
514     * @return The length of the array is the number of rows. A value at a
515     *         position is the number of items in that row.
516     * @hide
517     */
518    public int[] getLayout() {
519        return mLayout;
520    }
521
522    /**
523     * Returns the number of rows in the layout.
524     * <p>
525     * This should only be used for testing.
526     *
527     * @return The length of the array is the number of rows. A value at a
528     *         position is the number of items in that row.
529     * @hide
530     */
531    public int getLayoutNumRows() {
532        return mLayoutNumRows;
533    }
534
535    @Override
536    public boolean dispatchKeyEvent(KeyEvent event) {
537
538        if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) {
539            if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
540                removeCallbacks(this);
541                postDelayed(this, ViewConfiguration.getLongPressTimeout());
542            } else if (event.getAction() == KeyEvent.ACTION_UP) {
543
544                if (mMenuBeingLongpressed) {
545                    // It was in cycle mode, so reset it (will also remove us
546                    // from being called back)
547                    setCycleShortcutCaptionMode(false);
548                    return true;
549
550                } else {
551                    // Just remove us from being called back
552                    removeCallbacks(this);
553                    // Fall through to normal processing too
554                }
555            }
556        }
557
558        return super.dispatchKeyEvent(event);
559    }
560
561    @Override
562    protected void onAttachedToWindow() {
563        super.onAttachedToWindow();
564
565        requestFocus();
566    }
567
568    @Override
569    protected void onDetachedFromWindow() {
570        setCycleShortcutCaptionMode(false);
571        super.onDetachedFromWindow();
572    }
573
574    @Override
575    public void onWindowFocusChanged(boolean hasWindowFocus) {
576
577        if (!hasWindowFocus) {
578            setCycleShortcutCaptionMode(false);
579        }
580
581        super.onWindowFocusChanged(hasWindowFocus);
582    }
583
584    /**
585     * Sets the shortcut caption mode for IconMenuView. This mode will
586     * continuously cycle between a child's shortcut and its title.
587     *
588     * @param cycleShortcutAndNormal Whether to go into cycling shortcut mode,
589     *        or to go back to normal.
590     */
591    private void setCycleShortcutCaptionMode(boolean cycleShortcutAndNormal) {
592
593        if (!cycleShortcutAndNormal) {
594            /*
595             * We're setting back to title, so remove any callbacks for setting
596             * to shortcut
597             */
598            removeCallbacks(this);
599            setChildrenCaptionMode(false);
600            mMenuBeingLongpressed = false;
601
602        } else {
603
604            // Set it the first time (the cycle will be started in run()).
605            setChildrenCaptionMode(true);
606        }
607
608    }
609
610    /**
611     * When this method is invoked if the menu is currently not being
612     * longpressed, it means that the longpress has just been reached (so we set
613     * longpress flag, and start cycling). If it is being longpressed, we cycle
614     * to the next mode.
615     */
616    public void run() {
617
618        if (mMenuBeingLongpressed) {
619
620            // Cycle to other caption mode on the children
621            setChildrenCaptionMode(!mLastChildrenCaptionMode);
622
623        } else {
624
625            // Switch ourselves to continuously cycle the items captions
626            mMenuBeingLongpressed = true;
627            setCycleShortcutCaptionMode(true);
628        }
629
630        // We should run again soon to cycle to the other caption mode
631        postDelayed(this, ITEM_CAPTION_CYCLE_DELAY);
632    }
633
634    /**
635     * Iterates children and sets the desired shortcut mode. Only
636     * {@link #setCycleShortcutCaptionMode(boolean)} and {@link #run()} should call
637     * this.
638     *
639     * @param shortcut Whether to show shortcut or the title.
640     */
641    private void setChildrenCaptionMode(boolean shortcut) {
642
643        // Set the last caption mode pushed to children
644        mLastChildrenCaptionMode = shortcut;
645
646        for (int i = getChildCount() - 1; i >= 0; i--) {
647            ((IconMenuItemView) getChildAt(i)).setCaptionMode(shortcut);
648        }
649    }
650
651    /**
652     * For each item, calculates the most dense row that fully shows the item's
653     * title.
654     *
655     * @param width The available width of the icon menu.
656     */
657    private void calculateItemFittingMetadata(int width) {
658        int maxNumItemsPerRow = mMaxItemsPerRow;
659        int numItems = getChildCount();
660        for (int i = 0; i < numItems; i++) {
661            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
662            // Start with 1, since that case does not get covered in the loop below
663            lp.maxNumItemsOnRow = 1;
664            for (int curNumItemsPerRow = maxNumItemsPerRow; curNumItemsPerRow > 0;
665                    curNumItemsPerRow--) {
666                // Check whether this item can fit into a row containing curNumItemsPerRow
667                if (lp.desiredWidth < width / curNumItemsPerRow) {
668                    // It can, mark this value as the most dense row it can fit into
669                    lp.maxNumItemsOnRow = curNumItemsPerRow;
670                    break;
671                }
672            }
673        }
674    }
675
676    @Override
677    protected Parcelable onSaveInstanceState() {
678        Parcelable superState = super.onSaveInstanceState();
679
680        View focusedView = getFocusedChild();
681
682        for (int i = getChildCount() - 1; i >= 0; i--) {
683            if (getChildAt(i) == focusedView) {
684                return new SavedState(superState, i);
685            }
686        }
687
688        return new SavedState(superState, -1);
689    }
690
691    @Override
692    protected void onRestoreInstanceState(Parcelable state) {
693        SavedState ss = (SavedState) state;
694        super.onRestoreInstanceState(ss.getSuperState());
695
696        if (ss.focusedPosition >= getChildCount()) {
697            return;
698        }
699
700        View v = getChildAt(ss.focusedPosition);
701        if (v != null) {
702            v.requestFocus();
703        }
704    }
705
706    private static class SavedState extends BaseSavedState {
707        int focusedPosition;
708
709        /**
710         * Constructor called from {@link IconMenuView#onSaveInstanceState()}
711         */
712        public SavedState(Parcelable superState, int focusedPosition) {
713            super(superState);
714            this.focusedPosition = focusedPosition;
715        }
716
717        /**
718         * Constructor called from {@link #CREATOR}
719         */
720        private SavedState(Parcel in) {
721            super(in);
722            focusedPosition = in.readInt();
723        }
724
725        @Override
726        public void writeToParcel(Parcel dest, int flags) {
727            super.writeToParcel(dest, flags);
728            dest.writeInt(focusedPosition);
729        }
730
731        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
732            public SavedState createFromParcel(Parcel in) {
733                return new SavedState(in);
734            }
735
736            public SavedState[] newArray(int size) {
737                return new SavedState[size];
738            }
739        };
740
741    }
742
743    /**
744     * Layout parameters specific to IconMenuView (stores the left, top, right, bottom from the
745     * measure pass).
746     */
747    public static class LayoutParams extends ViewGroup.MarginLayoutParams
748    {
749        int left, top, right, bottom;
750        int desiredWidth;
751        int maxNumItemsOnRow;
752
753        public LayoutParams(Context c, AttributeSet attrs) {
754            super(c, attrs);
755        }
756
757        public LayoutParams(int width, int height) {
758            super(width, height);
759        }
760    }
761}
762