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.apps.common;
17 
18 import android.content.Context;
19 import android.graphics.Bitmap;
20 import android.graphics.Color;
21 import android.util.AttributeSet;
22 import android.view.View;
23 import android.view.ViewGroup;
24 import android.view.animation.Animation;
25 import android.view.animation.AnimationUtils;
26 import android.view.animation.DecelerateInterpolator;
27 import android.widget.FrameLayout;
28 import android.widget.ImageView;
29 
30 import java.util.Objects;
31 
32 /**
33  * A view where updating the image will show certain animations. Current animations include fading
34  * in and scaling down the new image.
35  */
36 public class CrossfadeImageView extends FrameLayout {
37     private final CropAlignedImageView mImageView1;
38     private final CropAlignedImageView mImageView2;
39 
40     private CropAlignedImageView mActiveImageView;
41     private CropAlignedImageView mInactiveImageView;
42 
43     private Bitmap mCurrentBitmap = null;
44     private Integer mCurrentColor = null;
45     private Animation mImageInAnimation;
46     private Animation mImageOutAnimation;
47 
CrossfadeImageView(Context context)48     public CrossfadeImageView(Context context) {
49         this(context, null);
50     }
51 
CrossfadeImageView(Context context, AttributeSet attrs)52     public CrossfadeImageView(Context context, AttributeSet attrs) {
53         this(context, attrs, 0);
54     }
55 
CrossfadeImageView(Context context, AttributeSet attrs, int defStyleAttr)56     public CrossfadeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
57         this(context, attrs, defStyleAttr, 0);
58     }
59 
CrossfadeImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)60     public CrossfadeImageView(Context context, AttributeSet attrs,
61             int defStyleAttr, int defStyleRes) {
62         super(context, attrs, defStyleAttr, defStyleRes);
63         LayoutParams lp = new LayoutParams(
64                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
65         ImageView imageViewBackground = new ImageView(context, attrs, defStyleAttr, defStyleRes);
66         imageViewBackground.setLayoutParams(lp);
67         imageViewBackground.setBackgroundColor(Color.BLACK);
68         addView(imageViewBackground);
69         mImageView1 = new CropAlignedImageView(context, attrs, defStyleAttr, defStyleRes);
70         mImageView1.setLayoutParams(lp);
71         addView(mImageView1);
72         mImageView2 = new CropAlignedImageView(context, attrs, defStyleAttr, defStyleRes);
73         mImageView2.setLayoutParams(lp);
74         addView(mImageView2);
75 
76         mActiveImageView = mImageView1;
77         mInactiveImageView = mImageView2;
78 
79         mImageInAnimation = AnimationUtils.loadAnimation(context, R.anim.image_in);
80         mImageInAnimation.setInterpolator(new DecelerateInterpolator());
81         mImageOutAnimation = AnimationUtils.loadAnimation(context, R.anim.image_out);
82     }
83 
84     /**
85      * Sets the image to show on this view
86      *
87      * @param bitmap image to show
88      * @param showAnimation whether the transition should be animated or not.
89      */
setImageBitmap(Bitmap bitmap, boolean showAnimation)90     public void setImageBitmap(Bitmap bitmap, boolean showAnimation) {
91         if (Objects.equals(mCurrentBitmap, bitmap)) {
92             return;
93         }
94 
95         mCurrentBitmap = bitmap;
96         mCurrentColor = null;
97         mInactiveImageView.setImageBitmap(bitmap);
98         if (showAnimation) {
99             animateViews();
100         } else {
101             mActiveImageView.setImageBitmap(bitmap);
102         }
103     }
104 
105     /**
106      * Sets a plain color as background, with an animated transition
107      */
108     @Override
setBackgroundColor(int color)109     public void setBackgroundColor(int color) {
110         if (mCurrentColor != null && mCurrentColor == color) {
111             return;
112         }
113         mInactiveImageView.setImageBitmap(null);
114         mCurrentBitmap = null;
115         mCurrentColor = color;
116         mInactiveImageView.setBackgroundColor(color);
117         animateViews();
118     }
119 
120     private final Animation.AnimationListener mAnimationListener =
121             new Animation.AnimationListener() {
122                 @Override
123                 public void onAnimationEnd(Animation animation) {
124                     if (mInactiveImageView != null) {
125                         mInactiveImageView.setVisibility(View.GONE);
126                     }
127                 }
128 
129                 @Override
130                 public void onAnimationStart(Animation animation) { }
131 
132                 @Override
133                 public void onAnimationRepeat(Animation animation) { }
134             };
135 
animateViews()136     private void animateViews() {
137         mInactiveImageView.setVisibility(View.VISIBLE);
138         mInactiveImageView.startAnimation(mImageInAnimation);
139         mInactiveImageView.bringToFront();
140         mActiveImageView.startAnimation(mImageOutAnimation);
141         mImageOutAnimation.setAnimationListener(mAnimationListener);
142         if (mActiveImageView == mImageView1) {
143             mActiveImageView = mImageView2;
144             mInactiveImageView = mImageView1;
145         } else {
146             mActiveImageView = mImageView1;
147             mInactiveImageView = mImageView2;
148         }
149     }
150 
151     /**
152      * Sets the additional image scale. See {@link
153      * com.android.car.apps.common.CropAlignedImageView#setImageAdditionalScale(float)}
154      * for more details.
155      */
setImageAdditionalScale(float scale)156     public void setImageAdditionalScale(float scale) {
157         mImageView2.setImageAdditionalScale(scale);
158         mImageView1.setImageAdditionalScale(scale);
159     }
160 }
161