1 /* 2 * Copyright (C) 2016 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.launcher3.util; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ValueAnimator; 22 import android.animation.ValueAnimator.AnimatorUpdateListener; 23 import android.content.Context; 24 import android.view.View; 25 import android.view.accessibility.AccessibilityManager; 26 27 import java.util.Arrays; 28 29 /** 30 * A utility class which divides the alpha for a view across multiple states. 31 */ 32 public class MultiStateAlphaController { 33 34 private final View mTargetView; 35 private final float[] mAlphas; 36 private final AccessibilityManager mAm; 37 private int mZeroAlphaListenerCount = 0; 38 MultiStateAlphaController(View view, int stateCount)39 public MultiStateAlphaController(View view, int stateCount) { 40 mTargetView = view; 41 mAlphas = new float[stateCount]; 42 Arrays.fill(mAlphas, 1); 43 44 mAm = (AccessibilityManager) view.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); 45 } 46 setAlphaAtIndex(float alpha, int index)47 public void setAlphaAtIndex(float alpha, int index) { 48 mAlphas[index] = alpha; 49 updateAlpha(); 50 } 51 updateAlpha()52 private void updateAlpha() { 53 // Only update the alpha if no zero-alpha animation is running. 54 if (mZeroAlphaListenerCount > 0) { 55 return; 56 } 57 float finalAlpha = 1; 58 for (float a : mAlphas) { 59 finalAlpha = finalAlpha * a; 60 } 61 mTargetView.setAlpha(finalAlpha); 62 mTargetView.setVisibility(finalAlpha > 0 ? View.VISIBLE 63 : (mAm.isEnabled() ? View.GONE : View.INVISIBLE)); 64 } 65 66 /** 67 * Returns an animator which changes the alpha at the index {@param index} 68 * to {@param finalAlpha}. Alphas at other index are not affected. 69 */ animateAlphaAtIndex(float finalAlpha, final int index)70 public Animator animateAlphaAtIndex(float finalAlpha, final int index) { 71 final ValueAnimator anim; 72 73 if (Float.compare(finalAlpha, mAlphas[index]) == 0) { 74 // Return a dummy animator to avoid null checks. 75 anim = ValueAnimator.ofFloat(0, 0); 76 } else { 77 ValueAnimator animator = ValueAnimator.ofFloat(mAlphas[index], finalAlpha); 78 animator.addUpdateListener(new AnimatorUpdateListener() { 79 @Override 80 public void onAnimationUpdate(ValueAnimator valueAnimator) { 81 float value = (Float) valueAnimator.getAnimatedValue(); 82 setAlphaAtIndex(value, index); 83 } 84 }); 85 anim = animator; 86 } 87 88 if (Float.compare(finalAlpha, 0f) == 0) { 89 // In case when any channel is animating to 0, and the current alpha is also 0, do not 90 // update alpha of the target view while the animation is running. 91 // We special case '0' because if any channel is set to 0, values of other 92 // channels do not matter. 93 anim.addListener(new ZeroAlphaAnimatorListener()); 94 } 95 return anim; 96 } 97 98 private class ZeroAlphaAnimatorListener extends AnimatorListenerAdapter { 99 100 private boolean mStartedAtZero = false; 101 102 @Override onAnimationStart(Animator animation)103 public void onAnimationStart(Animator animation) { 104 mStartedAtZero = Float.compare(mTargetView.getAlpha(), 0f) == 0; 105 if (mStartedAtZero) { 106 mZeroAlphaListenerCount++; 107 mTargetView.setAlpha(0); 108 } 109 } 110 111 @Override onAnimationEnd(Animator animation)112 public void onAnimationEnd(Animator animation) { 113 if (mStartedAtZero) { 114 mZeroAlphaListenerCount--; 115 updateAlpha(); 116 } 117 } 118 } 119 } 120