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.content.Context; 24 import android.graphics.Rect; 25 import android.support.v7.widget.RecyclerView.ViewHolder; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.widget.TextView; 30 31 import com.android.deskclock.AnimatorUtils; 32 import com.android.deskclock.ItemAdapter; 33 import com.android.deskclock.R; 34 import com.android.deskclock.data.DataModel; 35 import com.android.deskclock.data.Weekdays; 36 import com.android.deskclock.events.Events; 37 import com.android.deskclock.provider.Alarm; 38 import com.android.deskclock.provider.AlarmInstance; 39 40 import java.util.Calendar; 41 import java.util.List; 42 43 /** 44 * A ViewHolder containing views for an alarm item in collapsed stated. 45 */ 46 public final class CollapsedAlarmViewHolder extends AlarmItemViewHolder { 47 48 public static final int VIEW_TYPE = R.layout.alarm_time_collapsed; 49 50 private final TextView alarmLabel; 51 public final TextView daysOfWeek; 52 private final TextView upcomingInstanceLabel; 53 private final View hairLine; 54 55 private CollapsedAlarmViewHolder(View itemView) { 56 super(itemView); 57 58 alarmLabel = (TextView) itemView.findViewById(R.id.label); 59 daysOfWeek = (TextView) itemView.findViewById(R.id.days_of_week); 60 upcomingInstanceLabel = (TextView) itemView.findViewById(R.id.upcoming_instance_label); 61 hairLine = itemView.findViewById(R.id.hairline); 62 63 // Expand handler 64 itemView.setOnClickListener(new View.OnClickListener() { 65 @Override 66 public void onClick(View v) { 67 Events.sendAlarmEvent(R.string.action_expand_implied, R.string.label_deskclock); 68 getItemHolder().expand(); 69 } 70 }); 71 alarmLabel.setOnClickListener(new View.OnClickListener() { 72 @Override 73 public void onClick(View v) { 74 Events.sendAlarmEvent(R.string.action_expand_implied, R.string.label_deskclock); 75 getItemHolder().expand(); 76 } 77 }); 78 arrow.setOnClickListener(new View.OnClickListener() { 79 @Override 80 public void onClick(View v) { 81 Events.sendAlarmEvent(R.string.action_expand, R.string.label_deskclock); 82 getItemHolder().expand(); 83 } 84 }); 85 // Edit time handler 86 clock.setOnClickListener(new View.OnClickListener() { 87 @Override 88 public void onClick(View v) { 89 getItemHolder().getAlarmTimeClickHandler().onClockClicked(getItemHolder().item); 90 Events.sendAlarmEvent(R.string.action_expand_implied, R.string.label_deskclock); 91 getItemHolder().expand(); 92 } 93 }); 94 95 itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); 96 } 97 98 @Override 99 protected void onBindItemView(AlarmItemHolder itemHolder) { 100 super.onBindItemView(itemHolder); 101 final Alarm alarm = itemHolder.item; 102 final AlarmInstance alarmInstance = itemHolder.getAlarmInstance(); 103 final Context context = itemView.getContext(); 104 bindRepeatText(context, alarm); 105 bindReadOnlyLabel(context, alarm); 106 bindUpcomingInstance(context, alarm); 107 bindPreemptiveDismissButton(context, alarm, alarmInstance); 108 } 109 110 private void bindReadOnlyLabel(Context context, Alarm alarm) { 111 if (alarm.label != null && alarm.label.length() != 0) { 112 alarmLabel.setText(alarm.label); 113 alarmLabel.setVisibility(View.VISIBLE); 114 alarmLabel.setContentDescription(context.getString(R.string.label_description) 115 + " " + alarm.label); 116 } else { 117 alarmLabel.setVisibility(View.GONE); 118 } 119 } 120 121 private void bindRepeatText(Context context, Alarm alarm) { 122 if (alarm.daysOfWeek.isRepeating()) { 123 final Weekdays.Order weekdayOrder = DataModel.getDataModel().getWeekdayOrder(); 124 final String daysOfWeekText = alarm.daysOfWeek.toString(context, weekdayOrder); 125 daysOfWeek.setText(daysOfWeekText); 126 127 final String string = alarm.daysOfWeek.toAccessibilityString(context, weekdayOrder); 128 daysOfWeek.setContentDescription(string); 129 130 daysOfWeek.setVisibility(View.VISIBLE); 131 } else { 132 daysOfWeek.setVisibility(View.GONE); 133 } 134 } 135 136 private void bindUpcomingInstance(Context context, Alarm alarm) { 137 if (alarm.daysOfWeek.isRepeating()) { 138 upcomingInstanceLabel.setVisibility(View.GONE); 139 } else { 140 upcomingInstanceLabel.setVisibility(View.VISIBLE); 141 final String labelText = Alarm.isTomorrow(alarm, Calendar.getInstance()) ? 142 context.getString(R.string.alarm_tomorrow) : 143 context.getString(R.string.alarm_today); 144 upcomingInstanceLabel.setText(labelText); 145 } 146 } 147 148 @Override 149 public Animator onAnimateChange(List<Object> payloads, int fromLeft, int fromTop, int fromRight, 150 int fromBottom, long duration) { 151 /* There are no possible partial animations for collapsed view holders. */ 152 return null; 153 } 154 155 @Override 156 public Animator onAnimateChange(final ViewHolder oldHolder, ViewHolder newHolder, 157 long duration) { 158 if (!(oldHolder instanceof AlarmItemViewHolder) 159 || !(newHolder instanceof AlarmItemViewHolder)) { 160 return null; 161 } 162 163 final boolean isCollapsing = this == newHolder; 164 setChangingViewsAlpha(isCollapsing ? 0f : 1f); 165 166 final Animator changeAnimatorSet = isCollapsing 167 ? createCollapsingAnimator((AlarmItemViewHolder) oldHolder, duration) 168 : createExpandingAnimator((AlarmItemViewHolder) newHolder, duration); 169 changeAnimatorSet.addListener(new AnimatorListenerAdapter() { 170 @Override 171 public void onAnimationEnd(Animator animator) { 172 clock.setVisibility(View.VISIBLE); 173 onOff.setVisibility(View.VISIBLE); 174 arrow.setVisibility(View.VISIBLE); 175 arrow.setTranslationY(0f); 176 setChangingViewsAlpha(1f); 177 arrow.jumpDrawablesToCurrentState(); 178 } 179 }); 180 return changeAnimatorSet; 181 } 182 183 private Animator createExpandingAnimator(AlarmItemViewHolder newHolder, long duration) { 184 clock.setVisibility(View.INVISIBLE); 185 onOff.setVisibility(View.INVISIBLE); 186 arrow.setVisibility(View.INVISIBLE); 187 188 final AnimatorSet alphaAnimatorSet = new AnimatorSet(); 189 alphaAnimatorSet.playTogether( 190 ObjectAnimator.ofFloat(alarmLabel, View.ALPHA, 0f), 191 ObjectAnimator.ofFloat(daysOfWeek, View.ALPHA, 0f), 192 ObjectAnimator.ofFloat(upcomingInstanceLabel, View.ALPHA, 0f), 193 ObjectAnimator.ofFloat(preemptiveDismissButton, View.ALPHA, 0f), 194 ObjectAnimator.ofFloat(hairLine, View.ALPHA, 0f)); 195 alphaAnimatorSet.setDuration((long) (duration * ANIM_SHORT_DURATION_MULTIPLIER)); 196 197 final View oldView = itemView; 198 final View newView = newHolder.itemView; 199 final Animator boundsAnimator = AnimatorUtils.getBoundsAnimator(oldView, oldView, newView) 200 .setDuration(duration); 201 boundsAnimator.setInterpolator(AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN); 202 203 final AnimatorSet animatorSet = new AnimatorSet(); 204 animatorSet.playTogether(alphaAnimatorSet, boundsAnimator); 205 return animatorSet; 206 } 207 208 private Animator createCollapsingAnimator(AlarmItemViewHolder oldHolder, long duration) { 209 final AnimatorSet alphaAnimatorSet = new AnimatorSet(); 210 alphaAnimatorSet.playTogether( 211 ObjectAnimator.ofFloat(alarmLabel, View.ALPHA, 1f), 212 ObjectAnimator.ofFloat(daysOfWeek, View.ALPHA, 1f), 213 ObjectAnimator.ofFloat(upcomingInstanceLabel, View.ALPHA, 1f), 214 ObjectAnimator.ofFloat(preemptiveDismissButton, View.ALPHA, 1f), 215 ObjectAnimator.ofFloat(hairLine, View.ALPHA, 1f)); 216 final long standardDelay = (long) (duration * ANIM_STANDARD_DELAY_MULTIPLIER); 217 alphaAnimatorSet.setDuration(standardDelay); 218 alphaAnimatorSet.setStartDelay(duration - standardDelay); 219 220 final View oldView = oldHolder.itemView; 221 final View newView = itemView; 222 final Animator boundsAnimator = AnimatorUtils.getBoundsAnimator(newView, oldView, newView) 223 .setDuration(duration); 224 boundsAnimator.setInterpolator(AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN); 225 226 final View oldArrow = oldHolder.arrow; 227 final Rect oldArrowRect = new Rect(0, 0, oldArrow.getWidth(), oldArrow.getHeight()); 228 final Rect newArrowRect = new Rect(0, 0, arrow.getWidth(), arrow.getHeight()); 229 ((ViewGroup) newView).offsetDescendantRectToMyCoords(arrow, newArrowRect); 230 ((ViewGroup) oldView).offsetDescendantRectToMyCoords(oldArrow, oldArrowRect); 231 final float arrowTranslationY = oldArrowRect.bottom - newArrowRect.bottom; 232 arrow.setTranslationY(arrowTranslationY); 233 arrow.setVisibility(View.VISIBLE); 234 clock.setVisibility(View.VISIBLE); 235 onOff.setVisibility(View.VISIBLE); 236 237 final Animator arrowAnimation = ObjectAnimator.ofFloat(arrow, View.TRANSLATION_Y, 0f) 238 .setDuration(duration); 239 arrowAnimation.setInterpolator(AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN); 240 241 final AnimatorSet animatorSet = new AnimatorSet(); 242 animatorSet.playTogether(alphaAnimatorSet, boundsAnimator, arrowAnimation); 243 animatorSet.addListener(new AnimatorListenerAdapter() { 244 @Override 245 public void onAnimationStart(Animator animator) { 246 AnimatorUtils.startDrawableAnimation(arrow); 247 } 248 }); 249 return animatorSet; 250 } 251 252 private void setChangingViewsAlpha(float alpha) { 253 alarmLabel.setAlpha(alpha); 254 daysOfWeek.setAlpha(alpha); 255 upcomingInstanceLabel.setAlpha(alpha); 256 hairLine.setAlpha(alpha); 257 preemptiveDismissButton.setAlpha(alpha); 258 } 259 260 public static class Factory implements ItemAdapter.ItemViewHolder.Factory { 261 private final LayoutInflater mLayoutInflater; 262 263 public Factory(LayoutInflater layoutInflater) { 264 mLayoutInflater = layoutInflater; 265 } 266 267 @Override 268 public ItemAdapter.ItemViewHolder<?> createViewHolder(ViewGroup parent, int viewType) { 269 return new CollapsedAlarmViewHolder(mLayoutInflater.inflate( 270 viewType, parent, false /* attachToRoot */)); 271 } 272 } 273 }