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