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