2b60292ce451fd34132a30b701cd5b671b51d743
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / appmenu / AppMenuAdapter.java
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.appmenu;
6
7 import android.animation.Animator;
8 import android.animation.AnimatorListenerAdapter;
9 import android.animation.AnimatorSet;
10 import android.animation.ObjectAnimator;
11 import android.graphics.drawable.Drawable;
12 import android.view.LayoutInflater;
13 import android.view.MenuItem;
14 import android.view.View;
15 import android.view.View.OnClickListener;
16 import android.view.ViewGroup;
17 import android.widget.BaseAdapter;
18 import android.widget.ImageButton;
19 import android.widget.ImageView;
20 import android.widget.ImageView.ScaleType;
21 import android.widget.ListView;
22 import android.widget.TextView;
23
24 import org.chromium.base.ApiCompatibilityUtils;
25 import org.chromium.chrome.R;
26 import org.chromium.ui.base.LocalizationUtils;
27 import org.chromium.ui.interpolators.BakedBezierInterpolator;
28
29 import java.util.List;
30
31 /**
32  * ListAdapter to customize the view of items in the list.
33  */
34 class AppMenuAdapter extends BaseAdapter {
35     private static final int VIEW_TYPE_COUNT = 5;
36
37     /**
38      * Regular Android menu item that contains a title and an icon if icon is specified.
39      */
40     private static final int STANDARD_MENU_ITEM = 0;
41     /**
42      * Menu item that has two buttons, the first one is a title and the second one is an icon.
43      * It is different from the regular menu item because it contains two separate buttons.
44      */
45     private static final int TITLE_BUTTON_MENU_ITEM = 1;
46     /**
47      * Menu item that has three buttons. Every one of these buttons is displayed as an icon.
48      */
49     private static final int THREE_BUTTON_MENU_ITEM = 2;
50     /**
51      * Menu item that has four buttons. Every one of these buttons is displayed as an icon.
52      */
53     private static final int FOUR_BUTTON_MENU_ITEM = 3;
54     /**
55      * Menu item that has two buttons, the first one is a title and the second is a menu icon.
56      * This is similar to {@link #TITLE_BUTTON_MENU_ITEM} but has some slight layout differences.
57      */
58     private static final int MENU_BUTTON_MENU_ITEM = 4;
59
60     /** MenuItem Animation Constants */
61     private static final int ENTER_ITEM_DURATION_MS = 350;
62     private static final int ENTER_ITEM_BASE_DELAY_MS = 80;
63     private static final int ENTER_ITEM_ADDL_DELAY_MS = 30;
64     private static final float ENTER_STANDARD_ITEM_OFFSET_Y_DP = -10.f;
65     private static final float ENTER_STANDARD_ITEM_OFFSET_X_DP = 10.f;
66
67     /** Menu Button Layout Constants */
68     private static final float MENU_BUTTON_WIDTH_DP = 59.f;
69     private static final float MENU_BUTTON_START_PADDING_DP = 21.f;
70
71     private final AppMenu mAppMenu;
72     private final LayoutInflater mInflater;
73     private final List<MenuItem> mMenuItems;
74     private final int mNumMenuItems;
75     private final boolean mShowMenuButton;
76     private final float mDpToPx;
77
78     public AppMenuAdapter(AppMenu appMenu, List<MenuItem> menuItems, LayoutInflater inflater,
79             boolean showMenuButton) {
80         mAppMenu = appMenu;
81         mMenuItems = menuItems;
82         mInflater = inflater;
83         mNumMenuItems = menuItems.size();
84         mShowMenuButton = showMenuButton;
85         mDpToPx = inflater.getContext().getResources().getDisplayMetrics().density;
86     }
87
88     @Override
89     public int getCount() {
90         return mNumMenuItems;
91     }
92
93     @Override
94     public int getViewTypeCount() {
95         return VIEW_TYPE_COUNT;
96     }
97
98     @Override
99     public int getItemViewType(int position) {
100         MenuItem item = getItem(position);
101         boolean hasMenuButton = mShowMenuButton && position == 0;
102         int viewCount = item.hasSubMenu() ? item.getSubMenu().size() : 1;
103         if (hasMenuButton) viewCount++;
104
105         if (viewCount == 4) {
106             return FOUR_BUTTON_MENU_ITEM;
107         } else if (viewCount == 3) {
108             return THREE_BUTTON_MENU_ITEM;
109         } else if (viewCount == 2) {
110             return hasMenuButton ? MENU_BUTTON_MENU_ITEM : TITLE_BUTTON_MENU_ITEM;
111         }
112         return STANDARD_MENU_ITEM;
113     }
114
115     @Override
116     public long getItemId(int position) {
117         return getItem(position).getItemId();
118     }
119
120     @Override
121     public MenuItem getItem(int position) {
122         if (position == ListView.INVALID_POSITION) return null;
123         assert position >= 0;
124         assert position < mMenuItems.size();
125         return mMenuItems.get(position);
126     }
127
128     @Override
129     public View getView(int position, View convertView, ViewGroup parent) {
130         final boolean hasMenuButton = mShowMenuButton && position == 0;
131         final MenuItem item = getItem(position);
132         switch (getItemViewType(position)) {
133             case STANDARD_MENU_ITEM: {
134                 StandardMenuItemViewHolder holder = null;
135                 if (convertView == null) {
136                     holder = new StandardMenuItemViewHolder();
137                     convertView = mInflater.inflate(R.layout.menu_item, parent, false);
138                     holder.text = (TextView) convertView.findViewById(R.id.menu_item_text);
139                     holder.image = (AppMenuItemIcon) convertView.findViewById(R.id.menu_item_icon);
140                     convertView.setTag(holder);
141                     convertView.setTag(R.id.menu_item_enter_anim_id,
142                             buildStandardItemEnterAnimator(convertView, position));
143                 } else {
144                     holder = (StandardMenuItemViewHolder) convertView.getTag();
145                 }
146
147                 convertView.setOnClickListener(new OnClickListener() {
148                     @Override
149                     public void onClick(View v) {
150                         mAppMenu.onItemClick(item);
151                     }
152                 });
153                 // Set up the icon.
154                 Drawable icon = item.getIcon();
155                 holder.image.setImageDrawable(icon);
156                 holder.image.setVisibility(icon == null ? View.GONE : View.VISIBLE);
157                 holder.image.setChecked(item.isChecked());
158
159                 holder.text.setText(item.getTitle());
160                 boolean isEnabled = item.isEnabled();
161                 // Set the text color (using a color state list).
162                 holder.text.setEnabled(isEnabled);
163                 // This will ensure that the item is not highlighted when selected.
164                 convertView.setEnabled(isEnabled);
165                 break;
166             }
167             case THREE_BUTTON_MENU_ITEM: {
168                 ThreeButtonMenuItemViewHolder holder = null;
169                 if (convertView == null) {
170                     holder = new ThreeButtonMenuItemViewHolder();
171                     convertView = mInflater.inflate(R.layout.three_button_menu_item, parent, false);
172                     holder.buttons[0] = (ImageButton) convertView.findViewById(R.id.button_one);
173                     holder.buttons[1] = (ImageButton) convertView.findViewById(R.id.button_two);
174                     holder.buttons[2] = (ImageButton) convertView.findViewById(R.id.button_three);
175                     convertView.setTag(holder);
176                     convertView.setTag(R.id.menu_item_enter_anim_id,
177                             buildIconItemEnterAnimator(holder.buttons, hasMenuButton));
178                 } else {
179                     holder = (ThreeButtonMenuItemViewHolder) convertView.getTag();
180                 }
181                 setupImageButton(holder.buttons[0], item.getSubMenu().getItem(0));
182                 setupImageButton(holder.buttons[1], item.getSubMenu().getItem(1));
183                 if (hasMenuButton) {
184                     setupMenuButton(holder.buttons[3]);
185                 } else {
186                     setupImageButton(holder.buttons[2], item.getSubMenu().getItem(2));
187                 }
188
189                 convertView.setFocusable(false);
190                 convertView.setEnabled(false);
191                 break;
192             }
193             case FOUR_BUTTON_MENU_ITEM: {
194                 FourButtonMenuItemViewHolder holder = null;
195                 if (convertView == null) {
196                     holder = new FourButtonMenuItemViewHolder();
197                     convertView = mInflater.inflate(R.layout.four_button_menu_item, parent, false);
198                     holder.buttons[0] = (ImageButton) convertView.findViewById(R.id.button_one);
199                     holder.buttons[1] = (ImageButton) convertView.findViewById(R.id.button_two);
200                     holder.buttons[2] = (ImageButton) convertView.findViewById(R.id.button_three);
201                     holder.buttons[3] = (ImageButton) convertView.findViewById(R.id.button_four);
202                     convertView.setTag(holder);
203                     convertView.setTag(R.id.menu_item_enter_anim_id,
204                             buildIconItemEnterAnimator(holder.buttons, hasMenuButton));
205                 } else {
206                     holder = (FourButtonMenuItemViewHolder) convertView.getTag();
207                 }
208                 setupImageButton(holder.buttons[0], item.getSubMenu().getItem(0));
209                 setupImageButton(holder.buttons[1], item.getSubMenu().getItem(1));
210                 setupImageButton(holder.buttons[2], item.getSubMenu().getItem(2));
211                 if (hasMenuButton) {
212                     setupMenuButton(holder.buttons[3]);
213                 } else {
214                     setupImageButton(holder.buttons[3], item.getSubMenu().getItem(3));
215                 }
216                 convertView.setFocusable(false);
217                 convertView.setEnabled(false);
218                 break;
219             }
220             case TITLE_BUTTON_MENU_ITEM:
221                 // Fall through.
222             case MENU_BUTTON_MENU_ITEM: {
223                 TitleButtonMenuItemViewHolder holder = null;
224                 if (convertView == null) {
225                     holder = new TitleButtonMenuItemViewHolder();
226                     convertView = mInflater.inflate(R.layout.title_button_menu_item, parent, false);
227                     holder.title = (TextView) convertView.findViewById(R.id.title);
228                     holder.button = (ImageButton) convertView.findViewById(R.id.button);
229
230                     View animatedView = hasMenuButton ? holder.title : convertView;
231
232                     convertView.setTag(holder);
233                     convertView.setTag(R.id.menu_item_enter_anim_id,
234                             buildStandardItemEnterAnimator(animatedView, position));
235                 } else {
236                     holder = (TitleButtonMenuItemViewHolder) convertView.getTag();
237                 }
238                 final MenuItem titleItem = item.hasSubMenu() ? item.getSubMenu().getItem(0) : item;
239                 holder.title.setText(titleItem.getTitle());
240                 holder.title.setEnabled(titleItem.isEnabled());
241                 holder.title.setFocusable(titleItem.isEnabled());
242                 holder.title.setOnClickListener(new OnClickListener() {
243                     @Override
244                     public void onClick(View v) {
245                         mAppMenu.onItemClick(titleItem);
246                     }
247                 });
248
249                 if (hasMenuButton) {
250                     holder.button.setVisibility(View.VISIBLE);
251                     setupMenuButton(holder.button);
252                 } else if (item.getSubMenu().getItem(1).getIcon() != null) {
253                     holder.button.setVisibility(View.VISIBLE);
254                     setupImageButton(holder.button, item.getSubMenu().getItem(1));
255                 } else {
256                     holder.button.setVisibility(View.GONE);
257                 }
258                 convertView.setFocusable(false);
259                 convertView.setEnabled(false);
260                 break;
261             }
262             default:
263                 assert false : "Unexpected MenuItem type";
264         }
265         return convertView;
266     }
267
268     private void setupImageButton(ImageButton button, final MenuItem item) {
269         // Store and recover the level of image as button.setimageDrawable
270         // resets drawable to default level.
271         int currentLevel = item.getIcon().getLevel();
272         button.setImageDrawable(item.getIcon());
273         item.getIcon().setLevel(currentLevel);
274         button.setContentDescription(item.getTitle());
275         button.setEnabled(item.isEnabled());
276         button.setFocusable(item.isEnabled());
277         button.setOnClickListener(new OnClickListener() {
278             @Override
279             public void onClick(View v) {
280                 mAppMenu.onItemClick(item);
281             }
282         });
283     }
284
285     private void setupMenuButton(ImageButton button) {
286         button.setImageResource(R.drawable.btn_menu_pressed);
287         button.setContentDescription(button.getResources().getString(R.string.menu_dismiss_btn));
288         button.setEnabled(true);
289         button.setFocusable(true);
290         button.setOnClickListener(new OnClickListener() {
291             @Override
292             public void onClick(View v) {
293                 mAppMenu.dismiss();
294             }
295         });
296
297         // Set the button layout to make it properly line up with any underlying menu button
298         ApiCompatibilityUtils.setPaddingRelative(
299                 button, (int) (MENU_BUTTON_START_PADDING_DP * mDpToPx), 0, 0, 0);
300         button.getLayoutParams().width = (int) (MENU_BUTTON_WIDTH_DP * mDpToPx);
301         button.setScaleType(ScaleType.CENTER);
302     }
303
304     /**
305      * This builds an {@link Animator} for the enter animation of a standard menu item.  This means
306      * it will animate the alpha from 0 to 1 and translate the view from -10dp to 0dp on the y axis.
307      *
308      * @param view     The menu item {@link View} to be animated.
309      * @param position The position in the menu.  This impacts the start delay of the animation.
310      * @return         The {@link Animator}.
311      */
312     private Animator buildStandardItemEnterAnimator(final View view, int position) {
313         final float offsetYPx = ENTER_STANDARD_ITEM_OFFSET_Y_DP * mDpToPx;
314         final int startDelay = ENTER_ITEM_BASE_DELAY_MS + ENTER_ITEM_ADDL_DELAY_MS * position;
315
316         AnimatorSet animation = new AnimatorSet();
317         animation.playTogether(
318                 ObjectAnimator.ofFloat(view, View.ALPHA, 0.f, 1.f),
319                 ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, offsetYPx, 0.f));
320         animation.setDuration(ENTER_ITEM_DURATION_MS);
321         animation.setStartDelay(startDelay);
322         animation.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
323
324         animation.addListener(new AnimatorListenerAdapter() {
325             @Override
326             public void onAnimationStart(Animator animation) {
327                 view.setAlpha(0.f);
328             }
329         });
330         return animation;
331     }
332
333     /**
334      * This builds an {@link Animator} for the enter animation of icon row menu items.  This means
335      * it will animate the alpha from 0 to 1 and translate the views from 10dp to 0dp on the x axis.
336      *
337      * @param views        The list if icons in the menu item that should be animated.
338      * @param skipLastItem Whether or not the last item should be animated or not.
339      * @return             The {@link Animator}.
340      */
341     private Animator buildIconItemEnterAnimator(final ImageView[] views, boolean skipLastItem) {
342         final boolean rtl = LocalizationUtils.isLayoutRtl();
343         final float offsetXPx = ENTER_STANDARD_ITEM_OFFSET_X_DP * mDpToPx * (rtl ? -1.f : 1.f);
344         final int maxViewsToAnimate = views.length - (skipLastItem ? 1 : 0);
345
346         AnimatorSet animation = new AnimatorSet();
347         AnimatorSet.Builder builder = null;
348         for (int i = 0; i < maxViewsToAnimate; i++) {
349             final int startDelay = ENTER_ITEM_ADDL_DELAY_MS * i;
350
351             Animator alpha = ObjectAnimator.ofFloat(views[i], View.ALPHA, 0.f, 1.f);
352             Animator translate = ObjectAnimator.ofFloat(views[i], View.TRANSLATION_X, offsetXPx, 0);
353             alpha.setStartDelay(startDelay);
354             translate.setStartDelay(startDelay);
355             alpha.setDuration(ENTER_ITEM_DURATION_MS);
356             translate.setDuration(ENTER_ITEM_DURATION_MS);
357
358             if (builder == null) {
359                 builder = animation.play(alpha);
360             } else {
361                 builder.with(alpha);
362             }
363             builder.with(translate);
364         }
365         animation.setStartDelay(ENTER_ITEM_BASE_DELAY_MS);
366         animation.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
367
368         animation.addListener(new AnimatorListenerAdapter() {
369             @Override
370             public void onAnimationStart(Animator animation) {
371                 for (int i = 0; i < maxViewsToAnimate; i++) {
372                     views[i].setAlpha(0.f);
373                 }
374             }
375         });
376         return animation;
377     }
378
379     static class StandardMenuItemViewHolder {
380         public TextView text;
381         public AppMenuItemIcon image;
382     }
383
384     static class ThreeButtonMenuItemViewHolder {
385         public ImageButton[] buttons = new ImageButton[3];
386     }
387
388     static class FourButtonMenuItemViewHolder {
389         public ImageButton[] buttons = new ImageButton[4];
390     }
391
392     static class TitleButtonMenuItemViewHolder {
393         public TextView title;
394         public ImageButton button;
395     }
396 }