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