Home | History | Annotate | Download | only in dialog
      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 
     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
     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
     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
    109     public int getItemCount() {
    110         return mActions.size();
    111     }
    112 
    113     public int getCount() {
    114         return mActions.size();
    115     }
    116 
    117     public Action getItem(int position) {
    118         return mActions.get(position);
    119     }
    120 
    121     public void setListener(Action.Listener listener) {
    122         mListener = listener;
    123         mActionOnKeyPressAnimator.setListener(listener);
    124     }
    125 
    126     public void setOnFocusListener(Action.OnFocusListener onFocusListener) {
    127         mActionOnFocusAnimator.setOnFocusListener(onFocusListener);
    128     }
    129 
    130     /**
    131      * Used for serialization only.
    132      */
    133     public ArrayList<Action> getActions() {
    134         return new ArrayList<Action>(mActions);
    135     }
    136 
    137     public void setActions(ArrayList<Action> actions) {
    138         mActionOnFocusAnimator.unFocus(null);
    139         mActions.clear();
    140         mActions.addAll(actions);
    141         notifyDataSetChanged();
    142     }
    143 
    144     public void registerDataSetObserver(DataSetObserver dataSetObserver) {
    145     }
    146 
    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 
    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 
    168         public Action getAction() {
    169             return mAction;
    170         }
    171 
    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 
    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 
    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          */
    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 
    305         ActionOnFocusAnimator(Action.OnFocusListener onFocusListener) {
    306             mOnFocusListener = onFocusListener;
    307         }
    308 
    309         public void setOnFocusListener(Action.OnFocusListener onFocusListener) {
    310             mOnFocusListener = onFocusListener;
    311         }
    312 
    313         public void unFocus(View v) {
    314             changeFocus((v != null) ? v : mSelectedView, false, false);
    315         }
    316 
    317         @Override
    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 
    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 
    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 
    422         public ActionOnKeyPressAnimator(Action.Listener listener,
    423                 List<Action> actions) {
    424             mListener = listener;
    425             mActions = actions;
    426         }
    427 
    428         public void setListener(Action.Listener listener) {
    429             mListener = listener;
    430         }
    431 
    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
    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 
    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 
    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