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