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 package com.android.car.media; 17 18 import android.content.Context; 19 import android.graphics.Bitmap; 20 import android.graphics.Color; 21 import android.graphics.ColorFilter; 22 import android.graphics.ColorMatrix; 23 import android.graphics.ColorMatrixColorFilter; 24 import android.util.AttributeSet; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.view.animation.Animation; 28 import android.view.animation.AnimationUtils; 29 import android.view.animation.DecelerateInterpolator; 30 import android.widget.FrameLayout; 31 import android.widget.ImageView; 32 33 /** 34 * A view where updating the image will show certain animations. Current animations include fading 35 * in and scaling down the new image. 36 */ 37 public class CrossfadeImageView extends FrameLayout { 38 // ColorFilters can't currently be modified (b/17262092) so creating a saturation fade with 39 // color filters would normally require creating a ton of small objects. We get around this by 40 // caching color filters and limit the saturation to increments of 0.1. 41 // 0-0.09 -> [0] 42 // 0.10-0.19 -> [1] 43 // ... 44 // 1.0 -> [10] 45 private final ColorFilter[] mSaturationColorFilters = new ColorFilter[11]; 46 private final ColorMatrix mColorMatrix = new ColorMatrix(); 47 private final ImageView mImageView1; 48 private final ImageView mImageView2; 49 50 private ImageView mActiveImageView; 51 private ImageView mInactiveImageView; 52 53 private Bitmap mCurrentBitmap = null; 54 private Integer mCurrentColor = null; 55 private Animation mImageInAnimation; 56 private Animation mImageOutAnimation; 57 58 public CrossfadeImageView(Context context) { 59 this(context, null); 60 } 61 62 public CrossfadeImageView(Context context, AttributeSet attrs) { 63 this(context, attrs, 0); 64 } 65 66 public CrossfadeImageView(Context context, AttributeSet attrs, int defStyleAttr) { 67 this(context, attrs, defStyleAttr, 0); 68 } 69 70 public CrossfadeImageView(Context context, AttributeSet attrs, 71 int defStyleAttr, int defStyleRes) { 72 super(context, attrs, defStyleAttr, defStyleRes); 73 LayoutParams lp = new LayoutParams( 74 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 75 ImageView imageViewBackground = new ImageView(context, attrs, defStyleAttr, defStyleRes); 76 imageViewBackground.setLayoutParams(lp); 77 imageViewBackground.setBackgroundColor(Color.BLACK); 78 addView(imageViewBackground); 79 mImageView1 = new ImageView(context, attrs, defStyleAttr, defStyleRes); 80 mImageView1.setLayoutParams(lp); 81 addView(mImageView1); 82 mImageView2 = new ImageView(context, attrs, defStyleAttr, defStyleRes); 83 mImageView2.setLayoutParams(lp); 84 addView(mImageView2); 85 86 mActiveImageView = mImageView1; 87 mInactiveImageView = mImageView2; 88 89 mImageInAnimation = AnimationUtils.loadAnimation(context, R.anim.image_in); 90 mImageInAnimation.setInterpolator(new DecelerateInterpolator()); 91 mImageOutAnimation = AnimationUtils.loadAnimation(context, R.anim.image_out); 92 } 93 94 public void setImageBitmap(Bitmap bitmap, boolean showAnimation) { 95 if (bitmap == null) { 96 return; 97 } 98 99 if (mCurrentBitmap != null && bitmap.sameAs(mCurrentBitmap)) { 100 return; 101 } 102 103 mCurrentBitmap = bitmap; 104 mCurrentColor = null; 105 mInactiveImageView.setImageBitmap(bitmap); 106 if (showAnimation) { 107 animateViews(); 108 } else { 109 mActiveImageView.setImageBitmap(bitmap); 110 } 111 } 112 113 @Override 114 public void setBackgroundColor(int color) { 115 if (mCurrentColor != null && mCurrentColor == color) { 116 return; 117 } 118 mInactiveImageView.setImageBitmap(null); 119 mCurrentBitmap = null; 120 mCurrentColor = color; 121 mInactiveImageView.setBackgroundColor(color); 122 animateViews(); 123 } 124 125 public void setSaturation(float saturation) { 126 int i = (int) ((saturation * 100) / 10); 127 ColorFilter cf = mSaturationColorFilters[i]; 128 if (cf == null) { 129 mColorMatrix.setSaturation((10 * i) / 100f); 130 cf = new ColorMatrixColorFilter(mColorMatrix); 131 mSaturationColorFilters[i] = cf; 132 } 133 134 mImageView1.setColorFilter(cf); 135 mImageView2.setColorFilter(cf); 136 } 137 138 private final Animation.AnimationListener mAnimationListener = 139 new Animation.AnimationListener() { 140 @Override 141 public void onAnimationEnd(Animation animation) { 142 if (mInactiveImageView != null) { 143 mInactiveImageView.setVisibility(View.GONE); 144 } 145 } 146 147 @Override 148 public void onAnimationStart(Animation animation) { } 149 150 @Override 151 public void onAnimationRepeat(Animation animation) { } 152 }; 153 154 private void animateViews() { 155 mInactiveImageView.setVisibility(View.VISIBLE); 156 mInactiveImageView.startAnimation(mImageInAnimation); 157 mInactiveImageView.bringToFront(); 158 mActiveImageView.startAnimation(mImageOutAnimation); 159 mImageOutAnimation.setAnimationListener(mAnimationListener); 160 if (mActiveImageView == mImageView1) { 161 mActiveImageView = mImageView2; 162 mInactiveImageView = mImageView1; 163 } else { 164 mActiveImageView = mImageView1; 165 mInactiveImageView = mImageView2; 166 } 167 } 168 } 169