• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 
17 package com.android.tv.settings.dialog;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.ObjectAnimator;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.res.Resources;
25 import android.database.DataSetObserver;
26 import android.graphics.Bitmap;
27 import android.graphics.drawable.Drawable;
28 import android.media.AudioManager;
29 import android.net.Uri;
30 import android.support.v17.leanback.R;
31 import android.support.v7.widget.RecyclerView;
32 import android.support.v7.widget.RecyclerView.ViewHolder;
33 import android.text.TextUtils;
34 import android.util.Log;
35 import android.view.KeyEvent;
36 import android.view.LayoutInflater;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.WindowManager;
40 import android.view.animation.DecelerateInterpolator;
41 import android.view.animation.Interpolator;
42 import android.widget.AdapterView.OnItemSelectedListener;
43 import android.widget.ImageView;
44 import android.widget.TextView;
45 
46 import com.android.tv.settings.dialog.DialogFragment.Action;
47 import com.android.tv.settings.widget.BitmapWorkerOptions;
48 import com.android.tv.settings.widget.DrawableDownloader;
49 import com.android.tv.settings.widget.DrawableDownloader.BitmapCallback;
50 
51 import java.util.ArrayList;
52 import java.util.List;
53 
54 /**
55  * Adapter class which creates actions.
56  *
57  * @hide
58  */
59 class DialogActionAdapter extends RecyclerView.Adapter {
60     private static final String TAG = "ActionAdapter";
61     private static final boolean DEBUG = false;
62 
63     private final ActionOnKeyPressAnimator mActionOnKeyPressAnimator;
64     private final ActionOnFocusAnimator mActionOnFocusAnimator;
65     private LayoutInflater mInflater;
66     private final List<Action> mActions;
67     private Action.Listener mListener;
68     private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
69         @Override
70         public void onClick(View v) {
71             if (v != null && v.getWindowToken() != null && mListener != null) {
72                 mListener.onActionClicked(((ActionViewHolder) v.getTag(R.id.action_title)).getAction());
73             }
74         }
75     };
76 
DialogActionAdapter(Action.Listener listener, Action.OnFocusListener onFocusListener, List<Action> actions)77     public DialogActionAdapter(Action.Listener listener, Action.OnFocusListener onFocusListener,
78             List<Action> actions) {
79         super();
80         mListener = listener;
81         mActions = new ArrayList<Action>(actions);
82         mActionOnKeyPressAnimator = new ActionOnKeyPressAnimator(listener, mActions);
83         mActionOnFocusAnimator = new ActionOnFocusAnimator(onFocusListener);
84     }
85 
86     @Override
onCreateViewHolder(ViewGroup parent, int viewType)87     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
88         if (mInflater == null) {
89             mInflater = (LayoutInflater) parent.getContext().getSystemService(
90                     Context.LAYOUT_INFLATER_SERVICE);
91         }
92         View v = mInflater.inflate(R.layout.lb_dialog_action_list_item, parent, false);
93         v.setTag(R.layout.lb_dialog_action_list_item, parent);
94         return new ActionViewHolder(v, mActionOnKeyPressAnimator, mActionOnFocusAnimator, mOnClickListener);
95     }
96 
97     @Override
onBindViewHolder(RecyclerView.ViewHolder baseHolder, int position)98     public void onBindViewHolder(RecyclerView.ViewHolder baseHolder, int position) {
99         ActionViewHolder holder = (ActionViewHolder) baseHolder;
100 
101         if (position >= mActions.size()) {
102             return;
103         }
104 
105         holder.init(mActions.get(position));
106     }
107 
108     @Override
getItemCount()109     public int getItemCount() {
110         return mActions.size();
111     }
112 
getCount()113     public int getCount() {
114         return mActions.size();
115     }
116 
getItem(int position)117     public Action getItem(int position) {
118         return mActions.get(position);
119     }
120 
setListener(Action.Listener listener)121     public void setListener(Action.Listener listener) {
122         mListener = listener;
123         mActionOnKeyPressAnimator.setListener(listener);
124     }
125 
setOnFocusListener(Action.OnFocusListener onFocusListener)126     public void setOnFocusListener(Action.OnFocusListener onFocusListener) {
127         mActionOnFocusAnimator.setOnFocusListener(onFocusListener);
128     }
129 
130     /**
131      * Used for serialization only.
132      */
getActions()133     public ArrayList<Action> getActions() {
134         return new ArrayList<Action>(mActions);
135     }
136 
setActions(ArrayList<Action> actions)137     public void setActions(ArrayList<Action> actions) {
138         mActionOnFocusAnimator.unFocus(null);
139         mActions.clear();
140         mActions.addAll(actions);
141         notifyDataSetChanged();
142     }
143 
registerDataSetObserver(DataSetObserver dataSetObserver)144     public void registerDataSetObserver(DataSetObserver dataSetObserver) {
145     }
146 
setOnItemSelectedListener(OnItemSelectedListener listener)147     public void setOnItemSelectedListener(OnItemSelectedListener listener) {
148     }
149 
150     private static class ActionViewHolder extends ViewHolder {
151 
152         private final ActionOnKeyPressAnimator mActionOnKeyPressAnimator;
153         private final ActionOnFocusAnimator mActionOnFocusAnimator;
154         private final View.OnClickListener mViewOnClickListener;
155         private Action mAction;
156 
157         private BitmapCallback mPendingBitmapCallback;
158 
ActionViewHolder(View v, ActionOnKeyPressAnimator actionOnKeyPressAnimator, ActionOnFocusAnimator actionOnFocusAnimator, View.OnClickListener viewOnClickListener)159         public ActionViewHolder(View v, ActionOnKeyPressAnimator actionOnKeyPressAnimator,
160                 ActionOnFocusAnimator actionOnFocusAnimator,
161                 View.OnClickListener viewOnClickListener) {
162             super(v);
163             mActionOnKeyPressAnimator = actionOnKeyPressAnimator;
164             mActionOnFocusAnimator = actionOnFocusAnimator;
165             mViewOnClickListener = viewOnClickListener;
166         }
167 
getAction()168         public Action getAction() {
169             return mAction;
170         }
171 
init(Action action)172         public void init(Action action) {
173             mAction = action;
174 
175             if (mPendingBitmapCallback != null) {
176                 DrawableDownloader.getInstance(
177                         itemView.getContext()).cancelDownload(mPendingBitmapCallback);
178                 mPendingBitmapCallback = null;
179             }
180             TextView title = (TextView) itemView.findViewById(R.id.action_title);
181             TextView description = (TextView) itemView.findViewById(R.id.action_description);
182             description.setText(action.getDescription());
183             description.setVisibility(
184                     TextUtils.isEmpty(action.getDescription()) ? View.GONE : View.VISIBLE);
185             title.setText(action.getTitle());
186             ImageView checkmarkView = (ImageView) itemView.findViewById(R.id.action_checkmark);
187             checkmarkView.setVisibility(action.isChecked() ? View.VISIBLE : View.INVISIBLE);
188 
189             ImageView indicatorView = (ImageView) itemView.findViewById(R.id.action_icon);
190             View content = itemView.findViewById(R.id.action_content);
191             ViewGroup.LayoutParams contentLp = content.getLayoutParams();
192             if (setIndicator(indicatorView, action)) {
193                 contentLp.width = itemView.getContext().getResources()
194                         .getDimensionPixelSize(R.dimen.lb_action_text_width);
195             } else {
196                 contentLp.width = itemView.getContext().getResources()
197                         .getDimensionPixelSize(R.dimen.lb_action_text_width_no_icon);
198             }
199             content.setLayoutParams(contentLp);
200 
201             ImageView chevronView = (ImageView) itemView.findViewById(R.id.action_next_chevron);
202             chevronView.setVisibility(action.hasNext() ? View.VISIBLE : View.INVISIBLE);
203 
204             final Resources res = itemView.getContext().getResources();
205             if (action.hasMultilineDescription()) {
206                 title.setMaxLines(res.getInteger(R.integer.lb_dialog_action_title_max_lines));
207                 description.setMaxHeight(
208                         getDescriptionMaxHeight(itemView.getContext(), title));
209             } else {
210                 title.setMaxLines(res.getInteger(R.integer.lb_dialog_action_title_min_lines));
211                 description.setMaxLines(
212                         res.getInteger(R.integer.lb_dialog_action_description_min_lines));
213             }
214 
215             itemView.setTag(R.id.action_title, this);
216             itemView.setOnKeyListener(mActionOnKeyPressAnimator);
217             itemView.setOnClickListener(mViewOnClickListener);
218             itemView.setOnFocusChangeListener(mActionOnFocusAnimator);
219             mActionOnFocusAnimator.unFocus(itemView);
220         }
221 
setIndicator(final ImageView indicatorView, Action action)222         private boolean setIndicator(final ImageView indicatorView, Action action) {
223 
224             Context context = indicatorView.getContext();
225             Drawable indicator = action.getIndicator(context);
226             if (indicator != null) {
227                 indicatorView.setImageDrawable(indicator);
228                 indicatorView.setVisibility(View.VISIBLE);
229             } else {
230                 Uri iconUri = action.getIconUri();
231                 if (iconUri != null) {
232                     indicatorView.setVisibility(View.INVISIBLE);
233 
234                     mPendingBitmapCallback = new BitmapCallback() {
235                         @Override
236                         public void onBitmapRetrieved(Drawable bitmap) {
237                             if (bitmap != null) {
238                                 indicatorView.setVisibility(View.VISIBLE);
239                                 indicatorView.setImageDrawable(bitmap);
240                                 fadeIn(indicatorView);
241                             }
242                             mPendingBitmapCallback = null;
243                         }
244                     };
245 
246                     DrawableDownloader.getInstance(context).getBitmap(
247                             new BitmapWorkerOptions.Builder(
248                                     context).resource(iconUri)
249                                     .width(indicatorView.getLayoutParams().width).build(),
250                             mPendingBitmapCallback);
251 
252                 } else {
253                     indicatorView.setVisibility(View.GONE);
254                     return false;
255                 }
256             }
257             return true;
258         }
259 
fadeIn(View v)260         private void fadeIn(View v) {
261             v.setAlpha(0f);
262             ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(v,
263                     "alpha", 1f);
264             alphaAnimator.setDuration(
265                     v.getContext().getResources().getInteger(
266                             android.R.integer.config_mediumAnimTime));
267             alphaAnimator.start();
268         }
269 
270         /**
271          * @return the max height in pixels the description can be such that the
272          *         action nicely takes up the entire screen.
273          */
getDescriptionMaxHeight(Context context, TextView title)274         private int getDescriptionMaxHeight(Context context, TextView title) {
275             final Resources res = context.getResources();
276             final float verticalPadding = res.getDimension(R.dimen.lb_dialog_list_item_vertical_padding);
277             final int titleMaxLines = res.getInteger(R.integer.lb_dialog_action_title_max_lines);
278             final int displayHeight = ((WindowManager) context.getSystemService(
279                     Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight();
280 
281             // The 2 multiplier on the title height calculation is a
282             // conservative estimate for font padding which can not be
283             // calculated at this stage since the view hasn't been rendered yet.
284             return (int) (displayHeight -
285                     2 * verticalPadding - 2 * titleMaxLines * title.getLineHeight());
286         }
287 
288     }
289 
290     private static class ActionOnFocusAnimator implements View.OnFocusChangeListener {
291 
292         private boolean mResourcesSet;
293         private float mUnselectedAlpha;
294         private float mSelectedTitleAlpha;
295         private float mDisabledTitleAlpha;
296         private float mSelectedDescriptionAlpha;
297         private float mDisabledDescriptionAlpha;
298         private float mUnselectedDescriptionAlpha;
299         private float mSelectedChevronAlpha;
300         private float mDisabledChevronAlpha;
301         private int mAnimationDuration;
302         private Action.OnFocusListener mOnFocusListener;
303         private View mSelectedView;
304 
ActionOnFocusAnimator(Action.OnFocusListener onFocusListener)305         ActionOnFocusAnimator(Action.OnFocusListener onFocusListener) {
306             mOnFocusListener = onFocusListener;
307         }
308 
setOnFocusListener(Action.OnFocusListener onFocusListener)309         public void setOnFocusListener(Action.OnFocusListener onFocusListener) {
310             mOnFocusListener = onFocusListener;
311         }
312 
unFocus(View v)313         public void unFocus(View v) {
314             changeFocus((v != null) ? v : mSelectedView, false, false);
315         }
316 
317         @Override
onFocusChange(View v, boolean hasFocus)318         public void onFocusChange(View v, boolean hasFocus) {
319             if (hasFocus) {
320                 mSelectedView = v;
321                 changeFocus(v, true /* hasFocus */, true /* shouldAnimate */);
322                 if (mOnFocusListener != null) {
323                     // We still call onActionFocused so that listeners can clear
324                     // state if they want.
325                     mOnFocusListener.onActionFocused(
326                             ((ActionViewHolder) v.getTag(R.id.action_title)).getAction());
327                 }
328             } else {
329                 if (mSelectedView == v) {
330                     mSelectedView = null;
331                 }
332                 changeFocus(v, false /* hasFocus */, true /* shouldAnimate */);
333             }
334         }
335 
changeFocus(View v, boolean hasFocus, boolean shouldAnimate)336         private void changeFocus(View v, boolean hasFocus, boolean shouldAnimate) {
337             if (v == null) {
338                 return;
339             }
340 
341             if (!mResourcesSet) {
342                 mResourcesSet = true;
343                 final Resources res = v.getContext().getResources();
344 
345                 mAnimationDuration = res.getInteger(R.integer.lb_dialog_animation_duration);
346                 mUnselectedAlpha =
347                         Float.valueOf(res.getString(R.string.lb_dialog_list_item_unselected_text_alpha));
348 
349                 mSelectedTitleAlpha =
350                         Float.valueOf(res.getString(R.string.lb_dialog_list_item_selected_title_text_alpha));
351                 mDisabledTitleAlpha =
352                         Float.valueOf(res.getString(R.string.lb_dialog_list_item_disabled_title_text_alpha));
353 
354                 mSelectedDescriptionAlpha =
355                         Float.valueOf(
356                                 res.getString(R.string.lb_dialog_list_item_selected_description_text_alpha));
357                 mUnselectedDescriptionAlpha =
358                         Float.valueOf(
359                                 res.getString(R.string.lb_dialog_list_item_unselected_description_text_alpha));
360                 mDisabledDescriptionAlpha =
361                         Float.valueOf(
362                                 res.getString(R.string.lb_dialog_list_item_disabled_description_text_alpha));
363 
364                 mSelectedChevronAlpha =
365                         Float.valueOf(
366                                 res.getString(R.string.lb_dialog_list_item_selected_chevron_background_alpha));
367                 mDisabledChevronAlpha =
368                         Float.valueOf(
369                                 res.getString(R.string.lb_dialog_list_item_disabled_chevron_background_alpha));
370             }
371 
372             Action action = ((ActionViewHolder) v.getTag(R.id.action_title)).getAction();
373 
374             float titleAlpha = action.isEnabled() && !action.infoOnly()
375                     ? (hasFocus ? mSelectedTitleAlpha : mUnselectedAlpha) : mDisabledTitleAlpha;
376             float descriptionAlpha = (!hasFocus || action.infoOnly()) ? mUnselectedDescriptionAlpha
377                     : (action.isEnabled() ? mSelectedDescriptionAlpha : mDisabledDescriptionAlpha);
378             float chevronAlpha = action.hasNext() && !action.infoOnly()
379                     ? (action.isEnabled() ? mSelectedChevronAlpha : mDisabledChevronAlpha) : 0;
380 
381             TextView title = (TextView) v.findViewById(R.id.action_title);
382             setAlpha(title, shouldAnimate, titleAlpha);
383 
384             TextView description = (TextView) v.findViewById(R.id.action_description);
385             setAlpha(description, shouldAnimate, descriptionAlpha);
386 
387             ImageView checkmark = (ImageView) v.findViewById(R.id.action_checkmark);
388             setAlpha(checkmark, shouldAnimate, titleAlpha);
389 
390             ImageView icon = (ImageView) v.findViewById(R.id.action_icon);
391             setAlpha(icon, shouldAnimate, titleAlpha);
392 
393             ImageView chevron = (ImageView) v.findViewById(R.id.action_next_chevron);
394             setAlpha(chevron, shouldAnimate, chevronAlpha);
395         }
396 
setAlpha(View view, boolean shouldAnimate, float alpha)397         private void setAlpha(View view, boolean shouldAnimate, float alpha) {
398             if (shouldAnimate) {
399                 view.animate().alpha(alpha)
400                         .setDuration(mAnimationDuration)
401                         .setInterpolator(new DecelerateInterpolator(2F))
402                         .start();
403             } else {
404                 view.setAlpha(alpha);
405             }
406         }
407     }
408 
409     private static class ActionOnKeyPressAnimator implements View.OnKeyListener {
410 
411         private static final int SELECT_ANIM_DURATION = 100;
412         private static final int SELECT_ANIM_DELAY = 0;
413         private static final float SELECT_ANIM_SELECTED_ALPHA = 0.2f;
414         private static final float SELECT_ANIM_UNSELECTED_ALPHA = 1.0f;
415         private static final float CHECKMARK_ANIM_UNSELECTED_ALPHA = 0.0f;
416         private static final float CHECKMARK_ANIM_SELECTED_ALPHA = 1.0f;
417 
418         private final List<Action> mActions;
419         private boolean mKeyPressed = false;
420         private Action.Listener mListener;
421 
ActionOnKeyPressAnimator(Action.Listener listener, List<Action> actions)422         public ActionOnKeyPressAnimator(Action.Listener listener,
423                 List<Action> actions) {
424             mListener = listener;
425             mActions = actions;
426         }
427 
setListener(Action.Listener listener)428         public void setListener(Action.Listener listener) {
429             mListener = listener;
430         }
431 
playSound(Context context, int soundEffect)432         private void playSound(Context context, int soundEffect) {
433             AudioManager manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
434             manager.playSoundEffect(soundEffect);
435         }
436 
437         /**
438          * Now only handles KEYCODE_ENTER and KEYCODE_NUMPAD_ENTER key event.
439          */
440         @Override
onKey(View v, int keyCode, KeyEvent event)441         public boolean onKey(View v, int keyCode, KeyEvent event) {
442             if (v == null) {
443                 return false;
444             }
445             boolean handled = false;
446             Action action = ((ActionViewHolder) v.getTag(R.id.action_title)).getAction();
447             switch (keyCode) {
448                 case KeyEvent.KEYCODE_DPAD_CENTER:
449                 case KeyEvent.KEYCODE_NUMPAD_ENTER:
450                 case KeyEvent.KEYCODE_BUTTON_X:
451                 case KeyEvent.KEYCODE_BUTTON_Y:
452                 case KeyEvent.KEYCODE_ENTER:
453 
454                     if (!action.isEnabled() || action.infoOnly()) {
455                         if (v.isSoundEffectsEnabled()
456                                 && event.getAction() == KeyEvent.ACTION_DOWN) {
457                             // TODO: requires API 19
458                             //playSound(v.getContext(), AudioManager.FX_KEYPRESS_INVALID);
459                         }
460                         return true;
461                     }
462 
463                     switch (event.getAction()) {
464                         case KeyEvent.ACTION_DOWN:
465                             if (!mKeyPressed) {
466                                 mKeyPressed = true;
467 
468                                 if (v.isSoundEffectsEnabled()) {
469                                     playSound(v.getContext(), AudioManager.FX_KEY_CLICK);
470                                 }
471 
472                                 if (DEBUG) {
473                                     Log.d(TAG, "Enter Key down");
474                                 }
475 
476                                 prepareAndAnimateView(v, SELECT_ANIM_UNSELECTED_ALPHA,
477                                         SELECT_ANIM_SELECTED_ALPHA, SELECT_ANIM_DURATION,
478                                         SELECT_ANIM_DELAY, null, mKeyPressed);
479                                 handled = true;
480                             }
481                             break;
482                         case KeyEvent.ACTION_UP:
483                             if (mKeyPressed) {
484                                 mKeyPressed = false;
485 
486                                 if (DEBUG) {
487                                     Log.d(TAG, "Enter Key up");
488                                 }
489 
490                                 prepareAndAnimateView(v, SELECT_ANIM_SELECTED_ALPHA,
491                                         SELECT_ANIM_UNSELECTED_ALPHA, SELECT_ANIM_DURATION,
492                                         SELECT_ANIM_DELAY, null, mKeyPressed);
493                                 handled = true;
494                             }
495                             break;
496                         default:
497                             break;
498                     }
499                     break;
500                 default:
501                     break;
502             }
503             return handled;
504         }
505 
prepareAndAnimateView(final View v, float initAlpha, float destAlpha, int duration, int delay, Interpolator interpolator, final boolean pressed)506         private void prepareAndAnimateView(final View v, float initAlpha, float destAlpha,
507                 int duration,
508                 int delay, Interpolator interpolator, final boolean pressed) {
509             if (v != null && v.getWindowToken() != null) {
510                 final Action action = ((ActionViewHolder) v.getTag(R.id.action_title)).getAction();
511 
512                 if (!pressed) {
513                     fadeCheckmarks(v, action, duration, delay, interpolator);
514                 }
515 
516                 v.setAlpha(initAlpha);
517                 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
518                 v.buildLayer();
519                 v.animate().alpha(destAlpha).setDuration(duration).setStartDelay(delay);
520                 if (interpolator != null) {
521                     v.animate().setInterpolator(interpolator);
522                 }
523                 v.animate().setListener(new AnimatorListenerAdapter() {
524                         @Override
525                     public void onAnimationEnd(Animator animation) {
526 
527                         v.setLayerType(View.LAYER_TYPE_NONE, null);
528                         if (!pressed) {
529                             if (mListener != null) {
530                                 mListener.onActionClicked(action);
531                             }
532                         }
533                     }
534                 });
535                 v.animate().start();
536             }
537         }
538 
fadeCheckmarks(final View v, final Action action, int duration, int delay, Interpolator interpolator)539         private void fadeCheckmarks(final View v, final Action action, int duration, int delay,
540                 Interpolator interpolator) {
541             int actionCheckSetId = action.getCheckSetId();
542             if (actionCheckSetId != Action.NO_CHECK_SET) {
543                 ViewGroup parent = (ViewGroup) v.getTag(R.layout.lb_dialog_action_list_item);
544                 // Find any actions that are checked and are in the same group
545                 // as the selected action. Fade their checkmarks out.
546                 for (int i = 0, size = mActions.size(); i < size; i++) {
547                     Action a = mActions.get(i);
548                     if (a != action && a.getCheckSetId() == actionCheckSetId && a.isChecked()) {
549                         a.setChecked(false);
550                         View viewToAnimateOut = parent.getChildAt(i);
551                         if (viewToAnimateOut != null) {
552                             final View checkView = viewToAnimateOut.findViewById(
553                                     R.id.action_checkmark);
554                             checkView.animate().alpha(CHECKMARK_ANIM_UNSELECTED_ALPHA)
555                                     .setDuration(duration).setStartDelay(delay);
556                             if (interpolator != null) {
557                                 checkView.animate().setInterpolator(interpolator);
558                             }
559                             checkView.animate().setListener(new AnimatorListenerAdapter() {
560                                     @Override
561                                 public void onAnimationEnd(Animator animation) {
562                                     checkView.setVisibility(View.INVISIBLE);
563                                 }
564                             });
565                         }
566                     }
567                 }
568 
569                 // If we we'ren't already checked, fade our checkmark in.
570                 if (!action.isChecked()) {
571                     action.setChecked(true);
572                     final View checkView = v.findViewById(R.id.action_checkmark);
573                     checkView.setVisibility(View.VISIBLE);
574                     checkView.setAlpha(CHECKMARK_ANIM_UNSELECTED_ALPHA);
575                     checkView.animate().alpha(CHECKMARK_ANIM_SELECTED_ALPHA).setDuration(duration)
576                             .setStartDelay(delay);
577                     if (interpolator != null) {
578                         checkView.animate().setInterpolator(interpolator);
579                     }
580                     checkView.animate().setListener(null);
581                 }
582             }
583         }
584     }
585 }
586