Home | History | Annotate | Download | only in dataadapter
      1 /*
      2  * Copyright (C) 2015 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.deskclock.alarms.dataadapter;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.AnimatorSet;
     22 import android.animation.ObjectAnimator;
     23 import android.animation.PropertyValuesHolder;
     24 import android.content.Context;
     25 import android.graphics.Color;
     26 import android.graphics.Rect;
     27 import android.graphics.drawable.Drawable;
     28 import android.graphics.drawable.LayerDrawable;
     29 import android.os.Vibrator;
     30 import android.support.v4.content.ContextCompat;
     31 import android.support.v7.widget.RecyclerView.ViewHolder;
     32 import android.view.LayoutInflater;
     33 import android.view.View;
     34 import android.view.ViewGroup;
     35 import android.widget.CheckBox;
     36 import android.widget.CompoundButton;
     37 import android.widget.LinearLayout;
     38 import android.widget.TextView;
     39 
     40 import com.android.deskclock.AnimatorUtils;
     41 import com.android.deskclock.ItemAdapter;
     42 import com.android.deskclock.R;
     43 import com.android.deskclock.ThemeUtils;
     44 import com.android.deskclock.Utils;
     45 import com.android.deskclock.alarms.AlarmTimeClickHandler;
     46 import com.android.deskclock.data.DataModel;
     47 import com.android.deskclock.events.Events;
     48 import com.android.deskclock.provider.Alarm;
     49 import com.android.deskclock.provider.AlarmInstance;
     50 import com.android.deskclock.uidata.UiDataModel;
     51 
     52 import java.util.List;
     53 
     54 import static android.content.Context.VIBRATOR_SERVICE;
     55 import static android.view.View.TRANSLATION_Y;
     56 
     57 /**
     58  * A ViewHolder containing views for an alarm item in expanded state.
     59  */
     60 public final class ExpandedAlarmViewHolder extends AlarmItemViewHolder {
     61     public static final int VIEW_TYPE = R.layout.alarm_time_expanded;
     62 
     63     public final CheckBox repeat;
     64     private final TextView editLabel;
     65     public final LinearLayout repeatDays;
     66     private final CompoundButton[] dayButtons = new CompoundButton[7];
     67     public final CheckBox vibrate;
     68     public final TextView ringtone;
     69     public final TextView delete;
     70     private final View hairLine;
     71 
     72     private final boolean mHasVibrator;
     73 
     74     private ExpandedAlarmViewHolder(View itemView, boolean hasVibrator) {
     75         super(itemView);
     76 
     77         mHasVibrator = hasVibrator;
     78 
     79         delete = (TextView) itemView.findViewById(R.id.delete);
     80         repeat = (CheckBox) itemView.findViewById(R.id.repeat_onoff);
     81         vibrate = (CheckBox) itemView.findViewById(R.id.vibrate_onoff);
     82         ringtone = (TextView) itemView.findViewById(R.id.choose_ringtone);
     83         editLabel = (TextView) itemView.findViewById(R.id.edit_label);
     84         repeatDays = (LinearLayout) itemView.findViewById(R.id.repeat_days);
     85         hairLine = itemView.findViewById(R.id.hairline);
     86 
     87         final Context context = itemView.getContext();
     88         itemView.setBackground(new LayerDrawable(new Drawable[] {
     89                 ContextCompat.getDrawable(context, R.drawable.alarm_background_expanded),
     90                 ThemeUtils.resolveDrawable(context, R.attr.selectableItemBackground)
     91         }));
     92 
     93         // Build button for each day.
     94         final LayoutInflater inflater = LayoutInflater.from(context);
     95         final List<Integer> weekdays = DataModel.getDataModel().getWeekdayOrder().getCalendarDays();
     96         for (int i = 0; i < 7; i++) {
     97             final View dayButtonFrame = inflater.inflate(R.layout.day_button, repeatDays,
     98                     false /* attachToRoot */);
     99             final CompoundButton dayButton =
    100                     (CompoundButton) dayButtonFrame.findViewById(R.id.day_button_box);
    101             final int weekday = weekdays.get(i);
    102             dayButton.setText(UiDataModel.getUiDataModel().getShortWeekday(weekday));
    103             dayButton.setContentDescription(UiDataModel.getUiDataModel().getLongWeekday(weekday));
    104             repeatDays.addView(dayButtonFrame);
    105             dayButtons[i] = dayButton;
    106         }
    107 
    108         // Cannot set in xml since we need compat functionality for API < 21
    109         final Drawable labelIcon = Utils.getVectorDrawable(context, R.drawable.ic_label);
    110         editLabel.setCompoundDrawablesRelativeWithIntrinsicBounds(labelIcon, null, null, null);
    111         final Drawable deleteIcon = Utils.getVectorDrawable(context, R.drawable.ic_delete_small);
    112         delete.setCompoundDrawablesRelativeWithIntrinsicBounds(deleteIcon, null, null, null);
    113 
    114         // Collapse handler
    115         itemView.setOnClickListener(new View.OnClickListener() {
    116             @Override
    117             public void onClick(View v) {
    118                 Events.sendAlarmEvent(R.string.action_collapse_implied, R.string.label_deskclock);
    119                 getItemHolder().collapse();
    120             }
    121         });
    122         arrow.setOnClickListener(new View.OnClickListener() {
    123             @Override
    124             public void onClick(View v) {
    125                 Events.sendAlarmEvent(R.string.action_collapse, R.string.label_deskclock);
    126                 getItemHolder().collapse();
    127             }
    128         });
    129         // Edit time handler
    130         clock.setOnClickListener(new View.OnClickListener() {
    131             @Override
    132             public void onClick(View v) {
    133                 getAlarmTimeClickHandler().onClockClicked(getItemHolder().item);
    134             }
    135         });
    136         // Edit label handler
    137         editLabel.setOnClickListener(new View.OnClickListener() {
    138             @Override
    139             public void onClick(View view) {
    140                 getAlarmTimeClickHandler().onEditLabelClicked(getItemHolder().item);
    141             }
    142         });
    143         // Vibrator checkbox handler
    144         vibrate.setOnClickListener(new View.OnClickListener() {
    145             @Override
    146             public void onClick(View v) {
    147                 getAlarmTimeClickHandler().setAlarmVibrationEnabled(getItemHolder().item,
    148                         ((CheckBox) v).isChecked());
    149             }
    150         });
    151         // Ringtone editor handler
    152         ringtone.setOnClickListener(new View.OnClickListener() {
    153             @Override
    154             public void onClick(View view) {
    155                 getAlarmTimeClickHandler().onRingtoneClicked(context, getItemHolder().item);
    156             }
    157         });
    158         // Delete alarm handler
    159         delete.setOnClickListener(new View.OnClickListener() {
    160             @Override
    161             public void onClick(View v) {
    162                 getAlarmTimeClickHandler().onDeleteClicked(getItemHolder());
    163                 v.announceForAccessibility(context.getString(R.string.alarm_deleted));
    164             }
    165         });
    166         // Repeat checkbox handler
    167         repeat.setOnClickListener(new View.OnClickListener() {
    168             @Override
    169             public void onClick(View view) {
    170                 final boolean checked = ((CheckBox) view).isChecked();
    171                 getAlarmTimeClickHandler().setAlarmRepeatEnabled(getItemHolder().item, checked);
    172                 getItemHolder().notifyItemChanged(ANIMATE_REPEAT_DAYS);
    173             }
    174         });
    175         // Day buttons handler
    176         for (int i = 0; i < dayButtons.length; i++) {
    177             final int buttonIndex = i;
    178             dayButtons[i].setOnClickListener(new View.OnClickListener() {
    179                 @Override
    180                 public void onClick(View view) {
    181                     final boolean isChecked = ((CompoundButton) view).isChecked();
    182                     getAlarmTimeClickHandler().setDayOfWeekEnabled(getItemHolder().item,
    183                             isChecked, buttonIndex);
    184                 }
    185             });
    186         }
    187 
    188         itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
    189     }
    190 
    191     @Override
    192     protected void onBindItemView(final AlarmItemHolder itemHolder) {
    193         super.onBindItemView(itemHolder);
    194 
    195         final Alarm alarm = itemHolder.item;
    196         final AlarmInstance alarmInstance = itemHolder.getAlarmInstance();
    197         final Context context = itemView.getContext();
    198         bindEditLabel(context, alarm);
    199         bindDaysOfWeekButtons(alarm, context);
    200         bindVibrator(alarm);
    201         bindRingtone(context, alarm);
    202         bindPreemptiveDismissButton(context, alarm, alarmInstance);
    203     }
    204 
    205     private void bindRingtone(Context context, Alarm alarm) {
    206         final String title = DataModel.getDataModel().getRingtoneTitle(alarm.alert);
    207         ringtone.setText(title);
    208 
    209         final String description = context.getString(R.string.ringtone_description);
    210         ringtone.setContentDescription(description + " " + title);
    211 
    212         final boolean silent = Utils.RINGTONE_SILENT.equals(alarm.alert);
    213         final Drawable icon = Utils.getVectorDrawable(context,
    214                 silent ? R.drawable.ic_ringtone_silent : R.drawable.ic_ringtone);
    215         ringtone.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null);
    216     }
    217 
    218     private void bindDaysOfWeekButtons(Alarm alarm, Context context) {
    219         final List<Integer> weekdays = DataModel.getDataModel().getWeekdayOrder().getCalendarDays();
    220         for (int i = 0; i < weekdays.size(); i++) {
    221             final CompoundButton dayButton = dayButtons[i];
    222             if (alarm.daysOfWeek.isBitOn(weekdays.get(i))) {
    223                 dayButton.setChecked(true);
    224                 dayButton.setTextColor(ThemeUtils.resolveColor(context,
    225                         android.R.attr.windowBackground));
    226             } else {
    227                 dayButton.setChecked(false);
    228                 dayButton.setTextColor(Color.WHITE);
    229             }
    230         }
    231         if (alarm.daysOfWeek.isRepeating()) {
    232             repeat.setChecked(true);
    233             repeatDays.setVisibility(View.VISIBLE);
    234         } else {
    235             repeat.setChecked(false);
    236             repeatDays.setVisibility(View.GONE);
    237         }
    238     }
    239 
    240     private void bindEditLabel(Context context, Alarm alarm) {
    241         editLabel.setText(alarm.label);
    242         editLabel.setContentDescription(alarm.label != null && alarm.label.length() > 0
    243                 ? context.getString(R.string.label_description) + " " + alarm.label
    244                 : context.getString(R.string.no_label_specified));
    245     }
    246 
    247     private void bindVibrator(Alarm alarm) {
    248         if (!mHasVibrator) {
    249             vibrate.setVisibility(View.INVISIBLE);
    250         } else {
    251             vibrate.setVisibility(View.VISIBLE);
    252             vibrate.setChecked(alarm.vibrate);
    253         }
    254     }
    255 
    256     private AlarmTimeClickHandler getAlarmTimeClickHandler() {
    257         return getItemHolder().getAlarmTimeClickHandler();
    258     }
    259 
    260     @Override
    261     public Animator onAnimateChange(List<Object> payloads, int fromLeft, int fromTop, int fromRight,
    262             int fromBottom, long duration) {
    263         if (payloads == null || payloads.isEmpty() || !payloads.contains(ANIMATE_REPEAT_DAYS)) {
    264             return null;
    265         }
    266 
    267         final boolean isExpansion = repeatDays.getVisibility() == View.VISIBLE;
    268         final int height = repeatDays.getHeight();
    269         setTranslationY(isExpansion ? -height : 0f, isExpansion ? -height : height);
    270         repeatDays.setVisibility(View.VISIBLE);
    271         repeatDays.setAlpha(isExpansion ? 0f : 1f);
    272 
    273         final AnimatorSet animatorSet = new AnimatorSet();
    274         animatorSet.playTogether(AnimatorUtils.getBoundsAnimator(itemView,
    275                 fromLeft, fromTop, fromRight, fromBottom,
    276                 itemView.getLeft(), itemView.getTop(), itemView.getRight(), itemView.getBottom()),
    277                 ObjectAnimator.ofFloat(repeatDays, View.ALPHA, isExpansion ? 1f : 0f),
    278                 ObjectAnimator.ofFloat(repeatDays, TRANSLATION_Y, isExpansion ? 0f : -height),
    279                 ObjectAnimator.ofFloat(ringtone, TRANSLATION_Y, 0f),
    280                 ObjectAnimator.ofFloat(vibrate, TRANSLATION_Y, 0f),
    281                 ObjectAnimator.ofFloat(editLabel, TRANSLATION_Y, 0f),
    282                 ObjectAnimator.ofFloat(preemptiveDismissButton, TRANSLATION_Y, 0f),
    283                 ObjectAnimator.ofFloat(hairLine, TRANSLATION_Y, 0f),
    284                 ObjectAnimator.ofFloat(delete, TRANSLATION_Y, 0f),
    285                 ObjectAnimator.ofFloat(arrow, TRANSLATION_Y, 0f));
    286         animatorSet.addListener(new AnimatorListenerAdapter() {
    287             @Override
    288             public void onAnimationEnd(Animator animator) {
    289                 setTranslationY(0f, 0f);
    290                 repeatDays.setAlpha(1f);
    291                 repeatDays.setVisibility(isExpansion ? View.VISIBLE : View.GONE);
    292                 itemView.requestLayout();
    293             }
    294         });
    295         animatorSet.setDuration(duration);
    296         animatorSet.setInterpolator(AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN);
    297 
    298         return animatorSet;
    299     }
    300 
    301     private void setTranslationY(float repeatDaysTranslationY, float translationY) {
    302         repeatDays.setTranslationY(repeatDaysTranslationY);
    303         ringtone.setTranslationY(translationY);
    304         vibrate.setTranslationY(translationY);
    305         editLabel.setTranslationY(translationY);
    306         preemptiveDismissButton.setTranslationY(translationY);
    307         hairLine.setTranslationY(translationY);
    308         delete.setTranslationY(translationY);
    309         arrow.setTranslationY(translationY);
    310     }
    311 
    312     @Override
    313     public Animator onAnimateChange(final ViewHolder oldHolder, ViewHolder newHolder,
    314             long duration) {
    315         if (!(oldHolder instanceof AlarmItemViewHolder)
    316                 || !(newHolder instanceof AlarmItemViewHolder)) {
    317             return null;
    318         }
    319 
    320         final boolean isExpanding = this == newHolder;
    321         AnimatorUtils.setBackgroundAlpha(itemView, isExpanding ? 0 : 255);
    322         setChangingViewsAlpha(isExpanding ? 0f : 1f);
    323 
    324         final Animator changeAnimatorSet = isExpanding
    325                 ? createExpandingAnimator((AlarmItemViewHolder) oldHolder, duration)
    326                 : createCollapsingAnimator((AlarmItemViewHolder) newHolder, duration);
    327         changeAnimatorSet.addListener(new AnimatorListenerAdapter() {
    328             @Override
    329             public void onAnimationEnd(Animator animator) {
    330                 AnimatorUtils.setBackgroundAlpha(itemView, 255);
    331                 clock.setVisibility(View.VISIBLE);
    332                 onOff.setVisibility(View.VISIBLE);
    333                 arrow.setVisibility(View.VISIBLE);
    334                 arrow.setTranslationY(0f);
    335                 setChangingViewsAlpha(1f);
    336                 arrow.jumpDrawablesToCurrentState();
    337             }
    338         });
    339         return changeAnimatorSet;
    340     }
    341 
    342     private Animator createCollapsingAnimator(AlarmItemViewHolder newHolder, long duration) {
    343         arrow.setVisibility(View.INVISIBLE);
    344         clock.setVisibility(View.INVISIBLE);
    345         onOff.setVisibility(View.INVISIBLE);
    346 
    347         final boolean daysVisible = repeatDays.getVisibility() == View.VISIBLE;
    348         final int numberOfItems = countNumberOfItems();
    349 
    350         final View oldView = itemView;
    351         final View newView = newHolder.itemView;
    352 
    353         final Animator backgroundAnimator = ObjectAnimator.ofPropertyValuesHolder(oldView,
    354                 PropertyValuesHolder.ofInt(AnimatorUtils.BACKGROUND_ALPHA, 255, 0));
    355         backgroundAnimator.setDuration(duration);
    356 
    357         final Animator boundsAnimator = AnimatorUtils.getBoundsAnimator(oldView, oldView, newView);
    358         boundsAnimator.setDuration(duration);
    359         boundsAnimator.setInterpolator(AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN);
    360 
    361         final long shortDuration = (long) (duration * ANIM_SHORT_DURATION_MULTIPLIER);
    362         final Animator repeatAnimation = ObjectAnimator.ofFloat(repeat, View.ALPHA, 0f)
    363                 .setDuration(shortDuration);
    364         final Animator editLabelAnimation = ObjectAnimator.ofFloat(editLabel, View.ALPHA, 0f)
    365                 .setDuration(shortDuration);
    366         final Animator repeatDaysAnimation = ObjectAnimator.ofFloat(repeatDays, View.ALPHA, 0f)
    367                 .setDuration(shortDuration);
    368         final Animator vibrateAnimation = ObjectAnimator.ofFloat(vibrate, View.ALPHA, 0f)
    369                 .setDuration(shortDuration);
    370         final Animator ringtoneAnimation = ObjectAnimator.ofFloat(ringtone, View.ALPHA, 0f)
    371                 .setDuration(shortDuration);
    372         final Animator dismissAnimation = ObjectAnimator.ofFloat(preemptiveDismissButton,
    373                 View.ALPHA, 0f).setDuration(shortDuration);
    374         final Animator deleteAnimation = ObjectAnimator.ofFloat(delete, View.ALPHA, 0f)
    375                 .setDuration(shortDuration);
    376         final Animator hairLineAnimation = ObjectAnimator.ofFloat(hairLine, View.ALPHA, 0f)
    377                 .setDuration(shortDuration);
    378 
    379         // Set the staggered delays; use the first portion (duration * (1 - 1/4 - 1/6)) of the time,
    380         // so that the final animation, with a duration of 1/4 the total duration, finishes exactly
    381         // before the collapsed holder begins expanding.
    382         long startDelay = 0L;
    383         final long delayIncrement = (long) (duration * ANIM_LONG_DELAY_INCREMENT_MULTIPLIER)
    384                 / (numberOfItems - 1);
    385         deleteAnimation.setStartDelay(startDelay);
    386         if (preemptiveDismissButton.getVisibility() == View.VISIBLE) {
    387             startDelay += delayIncrement;
    388             dismissAnimation.setStartDelay(startDelay);
    389         }
    390         hairLineAnimation.setStartDelay(startDelay);
    391         startDelay += delayIncrement;
    392         editLabelAnimation.setStartDelay(startDelay);
    393         startDelay += delayIncrement;
    394         vibrateAnimation.setStartDelay(startDelay);
    395         ringtoneAnimation.setStartDelay(startDelay);
    396         startDelay += delayIncrement;
    397         if (daysVisible) {
    398             repeatDaysAnimation.setStartDelay(startDelay);
    399             startDelay += delayIncrement;
    400         }
    401         repeatAnimation.setStartDelay(startDelay);
    402 
    403         final AnimatorSet animatorSet = new AnimatorSet();
    404         animatorSet.playTogether(backgroundAnimator, boundsAnimator, repeatAnimation,
    405                 repeatDaysAnimation, vibrateAnimation, ringtoneAnimation, editLabelAnimation,
    406                 deleteAnimation, hairLineAnimation, dismissAnimation);
    407         return animatorSet;
    408     }
    409 
    410     private Animator createExpandingAnimator(AlarmItemViewHolder oldHolder, long duration) {
    411         final View oldView = oldHolder.itemView;
    412         final View newView = itemView;
    413         final Animator boundsAnimator = AnimatorUtils.getBoundsAnimator(newView, oldView, newView);
    414         boundsAnimator.setDuration(duration);
    415         boundsAnimator.setInterpolator(AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN);
    416 
    417         final Animator backgroundAnimator = ObjectAnimator.ofPropertyValuesHolder(newView,
    418                 PropertyValuesHolder.ofInt(AnimatorUtils.BACKGROUND_ALPHA, 0, 255));
    419         backgroundAnimator.setDuration(duration);
    420 
    421         final View oldArrow = oldHolder.arrow;
    422         final Rect oldArrowRect = new Rect(0, 0, oldArrow.getWidth(), oldArrow.getHeight());
    423         final Rect newArrowRect = new Rect(0, 0, arrow.getWidth(), arrow.getHeight());
    424         ((ViewGroup) newView).offsetDescendantRectToMyCoords(arrow, newArrowRect);
    425         ((ViewGroup) oldView).offsetDescendantRectToMyCoords(oldArrow, oldArrowRect);
    426         final float arrowTranslationY = oldArrowRect.bottom - newArrowRect.bottom;
    427 
    428         arrow.setTranslationY(arrowTranslationY);
    429         arrow.setVisibility(View.VISIBLE);
    430         clock.setVisibility(View.VISIBLE);
    431         onOff.setVisibility(View.VISIBLE);
    432 
    433         final long longDuration = (long) (duration * ANIM_LONG_DURATION_MULTIPLIER);
    434         final Animator repeatAnimation = ObjectAnimator.ofFloat(repeat, View.ALPHA, 1f)
    435                 .setDuration(longDuration);
    436         final Animator repeatDaysAnimation = ObjectAnimator.ofFloat(repeatDays, View.ALPHA, 1f)
    437                 .setDuration(longDuration);
    438         final Animator ringtoneAnimation = ObjectAnimator.ofFloat(ringtone, View.ALPHA, 1f)
    439                 .setDuration(longDuration);
    440         final Animator dismissAnimation = ObjectAnimator.ofFloat(preemptiveDismissButton,
    441                 View.ALPHA, 1f).setDuration(longDuration);
    442         final Animator vibrateAnimation = ObjectAnimator.ofFloat(vibrate, View.ALPHA, 1f)
    443                 .setDuration(longDuration);
    444         final Animator editLabelAnimation = ObjectAnimator.ofFloat(editLabel, View.ALPHA, 1f)
    445                 .setDuration(longDuration);
    446         final Animator hairLineAnimation = ObjectAnimator.ofFloat(hairLine, View.ALPHA, 1f)
    447                 .setDuration(longDuration);
    448         final Animator deleteAnimation = ObjectAnimator.ofFloat(delete, View.ALPHA, 1f)
    449                 .setDuration(longDuration);
    450         final Animator arrowAnimation = ObjectAnimator.ofFloat(arrow, View.TRANSLATION_Y, 0f)
    451                 .setDuration(duration);
    452         arrowAnimation.setInterpolator(AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN);
    453 
    454         // Set the stagger delays; delay the first by the amount of time it takes for the collapse
    455         // to complete, then stagger the expansion with the remaining time.
    456         long startDelay = (long) (duration * ANIM_STANDARD_DELAY_MULTIPLIER);
    457         final int numberOfItems = countNumberOfItems();
    458         final long delayIncrement = (long) (duration * ANIM_SHORT_DELAY_INCREMENT_MULTIPLIER)
    459                 / (numberOfItems - 1);
    460         repeatAnimation.setStartDelay(startDelay);
    461         startDelay += delayIncrement;
    462         final boolean daysVisible = repeatDays.getVisibility() == View.VISIBLE;
    463         if (daysVisible) {
    464             repeatDaysAnimation.setStartDelay(startDelay);
    465             startDelay += delayIncrement;
    466         }
    467         ringtoneAnimation.setStartDelay(startDelay);
    468         vibrateAnimation.setStartDelay(startDelay);
    469         startDelay += delayIncrement;
    470         editLabelAnimation.setStartDelay(startDelay);
    471         startDelay += delayIncrement;
    472         hairLineAnimation.setStartDelay(startDelay);
    473         if (preemptiveDismissButton.getVisibility() == View.VISIBLE) {
    474             dismissAnimation.setStartDelay(startDelay);
    475             startDelay += delayIncrement;
    476         }
    477         deleteAnimation.setStartDelay(startDelay);
    478 
    479         final AnimatorSet animatorSet = new AnimatorSet();
    480         animatorSet.playTogether(backgroundAnimator, repeatAnimation, boundsAnimator,
    481                 repeatDaysAnimation, vibrateAnimation, ringtoneAnimation, editLabelAnimation,
    482                 deleteAnimation, hairLineAnimation, dismissAnimation, arrowAnimation);
    483         animatorSet.addListener(new AnimatorListenerAdapter() {
    484             @Override
    485             public void onAnimationStart(Animator animator) {
    486                 AnimatorUtils.startDrawableAnimation(arrow);
    487             }
    488         });
    489         return animatorSet;
    490     }
    491 
    492     private int countNumberOfItems() {
    493         // Always between 4 and 6 items.
    494         int numberOfItems = 4;
    495         if (preemptiveDismissButton.getVisibility() == View.VISIBLE) {
    496             numberOfItems++;
    497         }
    498         if (repeatDays.getVisibility() == View.VISIBLE) {
    499             numberOfItems++;
    500         }
    501         return numberOfItems;
    502     }
    503 
    504     private void setChangingViewsAlpha(float alpha) {
    505         repeat.setAlpha(alpha);
    506         editLabel.setAlpha(alpha);
    507         repeatDays.setAlpha(alpha);
    508         vibrate.setAlpha(alpha);
    509         ringtone.setAlpha(alpha);
    510         hairLine.setAlpha(alpha);
    511         delete.setAlpha(alpha);
    512         preemptiveDismissButton.setAlpha(alpha);
    513     }
    514 
    515     public static class Factory implements ItemAdapter.ItemViewHolder.Factory {
    516 
    517         private final LayoutInflater mLayoutInflater;
    518         private final boolean mHasVibrator;
    519 
    520         public Factory(Context context) {
    521             mLayoutInflater = LayoutInflater.from(context);
    522             mHasVibrator = ((Vibrator) context.getSystemService(VIBRATOR_SERVICE)).hasVibrator();
    523         }
    524 
    525         @Override
    526         public ItemAdapter.ItemViewHolder<?> createViewHolder(ViewGroup parent, int viewType) {
    527             final View itemView = mLayoutInflater.inflate(viewType, parent, false);
    528             return new ExpandedAlarmViewHolder(itemView, mHasVibrator);
    529         }
    530     }
    531 }
    532