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.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 }