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 
ExpandedAlarmViewHolder(View itemView, boolean hasVibrator)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
onBindItemView(final AlarmItemHolder itemHolder)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 
bindRingtone(Context context, Alarm alarm)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 
bindDaysOfWeekButtons(Alarm alarm, Context context)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 
bindEditLabel(Context context, Alarm alarm)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 
bindVibrator(Alarm alarm)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 
getAlarmTimeClickHandler()256     private AlarmTimeClickHandler getAlarmTimeClickHandler() {
257         return getItemHolder().getAlarmTimeClickHandler();
258     }
259 
260     @Override
onAnimateChange(List<Object> payloads, int fromLeft, int fromTop, int fromRight, int fromBottom, long duration)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 
setTranslationY(float repeatDaysTranslationY, float translationY)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
onAnimateChange(final ViewHolder oldHolder, ViewHolder newHolder, long duration)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 
createCollapsingAnimator(AlarmItemViewHolder newHolder, long duration)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 
createExpandingAnimator(AlarmItemViewHolder oldHolder, long duration)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 
countNumberOfItems()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 
setChangingViewsAlpha(float alpha)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 
Factory(Context context)520         public Factory(Context context) {
521             mLayoutInflater = LayoutInflater.from(context);
522             mHasVibrator = ((Vibrator) context.getSystemService(VIBRATOR_SERVICE)).hasVibrator();
523         }
524 
525         @Override
createViewHolder(ViewGroup parent, int viewType)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