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.tv.common.ui.setup.animation; 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.TypeEvaluator; 24 import android.content.Context; 25 import android.transition.Transition; 26 import android.transition.TransitionSet; 27 import android.view.Gravity; 28 import android.view.View; 29 import android.widget.ImageView; 30 31 import com.android.tv.common.R; 32 33 /** 34 * A helper class for setup animation. 35 */ 36 public final class SetupAnimationHelper { 37 public static final long DELAY_BETWEEN_SIBLINGS_MS = applyAnimationTimeScale(33); 38 39 private static final float ANIMATION_TIME_SCALE = 1.0f; 40 41 private static boolean sInitialized; 42 private static long sFragmentTransitionDuration; 43 private static int sFragmentTransitionLongDistance; 44 private static int sFragmentTransitionShortDistance; 45 SetupAnimationHelper()46 private SetupAnimationHelper() { } 47 48 /** 49 * Load initial parameters. This method should be called before using this class. 50 */ initialize(Context context)51 public static void initialize(Context context) { 52 if (sInitialized) { 53 return; 54 } 55 sFragmentTransitionDuration = context.getResources() 56 .getInteger(R.integer.setup_fragment_transition_duration); 57 sFragmentTransitionLongDistance = context.getResources() 58 .getDimensionPixelOffset(R.dimen.setup_fragment_transition_long_distance); 59 sFragmentTransitionShortDistance = context.getResources() 60 .getDimensionPixelOffset(R.dimen.setup_fragment_transition_short_distance); 61 sInitialized = true; 62 } 63 checkInitialized()64 private static void checkInitialized() { 65 if (!sInitialized) { 66 throw new IllegalStateException("SetupAnimationHelper not initialized"); 67 } 68 } 69 70 public static class TransitionBuilder { 71 private int mSlideEdge = Gravity.START; 72 private final int mDistance = sFragmentTransitionLongDistance; 73 private long mDuration = sFragmentTransitionDuration; 74 private int[] mParentIdForDelay; 75 private int[] mExcludeIds; 76 TransitionBuilder()77 public TransitionBuilder() { 78 checkInitialized(); 79 } 80 81 /** 82 * Sets the edge of the slide transition. 83 * 84 * @see android.transition.Slide#setSlideEdge 85 */ setSlideEdge(int slideEdge)86 public TransitionBuilder setSlideEdge(int slideEdge) { 87 mSlideEdge = slideEdge; 88 return this; 89 } 90 91 /** 92 * Sets the duration of the transition. 93 */ setDuration(long duration)94 public TransitionBuilder setDuration(long duration) { 95 mDuration = duration; 96 return this; 97 } 98 99 /** 100 * Sets the ID of the view whose descendants will perform delayed move. 101 * 102 * @see android.view.ViewGroup#isTransitionGroup 103 */ setParentIdsForDelay(int[] parentIdForDelay)104 public TransitionBuilder setParentIdsForDelay(int[] parentIdForDelay) { 105 mParentIdForDelay = parentIdForDelay; 106 return this; 107 } 108 109 /** 110 * Sets the ID's of the views which will not be included in the transition. 111 */ setExcludeIds(int[] excludeIds)112 public TransitionBuilder setExcludeIds(int[] excludeIds) { 113 mExcludeIds = excludeIds; 114 return this; 115 } 116 117 /** 118 * Builds and returns the {@link android.transition.Transition}. 119 */ build()120 public Transition build() { 121 FadeAndShortSlide transition = new FadeAndShortSlide(mSlideEdge, mParentIdForDelay); 122 transition.setDistance(mDistance); 123 transition.setDuration(mDuration); 124 if (mExcludeIds != null) { 125 for (int id : mExcludeIds) { 126 transition.excludeTarget(id, true); 127 } 128 } 129 return transition; 130 } 131 } 132 133 /** 134 * Changes the move distance of the {@code transition} to long distance. 135 */ setLongDistance(FadeAndShortSlide transition)136 public static void setLongDistance(FadeAndShortSlide transition) { 137 checkInitialized(); 138 transition.setDistance(sFragmentTransitionLongDistance); 139 } 140 141 /** 142 * Changes the move distance of the {@code transition} to short distance. 143 */ setShortDistance(FadeAndShortSlide transition)144 public static void setShortDistance(FadeAndShortSlide transition) { 145 checkInitialized(); 146 transition.setDistance(sFragmentTransitionShortDistance); 147 } 148 149 /** 150 * Applies the animation scale to the given {@code animator}. 151 */ applyAnimationTimeScale(Animator animator)152 public static Animator applyAnimationTimeScale(Animator animator) { 153 if (animator instanceof AnimatorSet) { 154 for (Animator child : ((AnimatorSet) animator).getChildAnimations()) { 155 applyAnimationTimeScale(child); 156 } 157 } 158 if (animator.getDuration() > 0) { 159 animator.setDuration((long) (animator.getDuration() * ANIMATION_TIME_SCALE)); 160 } 161 animator.setStartDelay((long) (animator.getStartDelay() * ANIMATION_TIME_SCALE)); 162 return animator; 163 } 164 165 /** 166 * Applies the animation scale to the given {@code transition}. 167 */ applyAnimationTimeScale(Transition transition)168 public static Transition applyAnimationTimeScale(Transition transition) { 169 if (transition instanceof TransitionSet) { 170 TransitionSet set = (TransitionSet) transition; 171 int count = set.getTransitionCount(); 172 for (int i = 0; i < count; ++i) { 173 applyAnimationTimeScale(set.getTransitionAt(i)); 174 } 175 } 176 if (transition.getDuration() > 0) { 177 transition.setDuration((long) (transition.getDuration() * ANIMATION_TIME_SCALE)); 178 } 179 transition.setStartDelay((long) (transition.getStartDelay() * ANIMATION_TIME_SCALE)); 180 return transition; 181 } 182 183 /** 184 * Applies the animation scale to the given {@code time}. 185 */ applyAnimationTimeScale(long time)186 public static long applyAnimationTimeScale(long time) { 187 return (long) (time * ANIMATION_TIME_SCALE); 188 } 189 190 /** 191 * Returns an animator which animates the source image of the {@link ImageView}. 192 * 193 * <p>The frame rate is 60 fps. 194 */ createFrameAnimator(ImageView imageView, int[] frames)195 public static ObjectAnimator createFrameAnimator(ImageView imageView, int[] frames) { 196 return createFrameAnimatorWithDelay(imageView, frames, 0); 197 } 198 199 /** 200 * Returns an animator which animates the source image of the {@link ImageView} with start delay. 201 * 202 * <p>The frame rate is 60 fps. 203 */ createFrameAnimatorWithDelay(ImageView imageView, int[] frames, long startDelay)204 public static ObjectAnimator createFrameAnimatorWithDelay(ImageView imageView, int[] frames, 205 long startDelay) { 206 ObjectAnimator animator = ObjectAnimator.ofInt(imageView, "imageResource", frames); 207 // Make it 60 fps. 208 animator.setDuration(frames.length * 1000 / 60); 209 animator.setInterpolator(null); 210 animator.setStartDelay(startDelay); 211 animator.setEvaluator(new TypeEvaluator<Integer>() { 212 @Override 213 public Integer evaluate(float fraction, Integer startValue, Integer endValue) { 214 return startValue; 215 } 216 }); 217 return animator; 218 } 219 220 /** 221 * Creates a fade out animator. 222 * 223 * @param view The view which will be animated. 224 * @param duration The duration of the animation. 225 * @param makeVisibleAfterAnimation If {@code true}, the view will become visible after the 226 * animation ends. 227 */ createFadeOutAnimator(final View view, long duration, boolean makeVisibleAfterAnimation)228 public static Animator createFadeOutAnimator(final View view, long duration, 229 boolean makeVisibleAfterAnimation) { 230 ObjectAnimator animator = 231 ObjectAnimator.ofFloat(view, View.ALPHA, 1.0f, 0.0f).setDuration(duration); 232 if (makeVisibleAfterAnimation) { 233 animator.addListener(new AnimatorListenerAdapter() { 234 @Override 235 public void onAnimationEnd(Animator animation) { 236 view.setAlpha(1.0f); 237 } 238 }); 239 } 240 return animator; 241 } 242 } 243