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 package com.android.launcher3.allapps;
17 
18 import android.animation.ObjectAnimator;
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.graphics.Canvas;
22 import android.graphics.ColorFilter;
23 import android.graphics.PixelFormat;
24 import android.graphics.Rect;
25 import android.graphics.drawable.Drawable;
26 
27 import android.view.Gravity;
28 import com.android.launcher3.R;
29 
30 /**
31  * A helper class to positon and orient a drawable to be drawn.
32  */
33 class TransformedImageDrawable {
34     private Drawable mImage;
35     private float mXPercent;
36     private float mYPercent;
37     private int mGravity;
38     private int mAlpha;
39 
40     /**
41      * @param gravity If one of the Gravity center values, the x and y offset will take the width
42      *                and height of the image into account to center the image to the offset.
43      */
TransformedImageDrawable(Resources res, int resourceId, float xPct, float yPct, int gravity)44     public TransformedImageDrawable(Resources res, int resourceId, float xPct, float yPct,
45             int gravity) {
46         mImage = res.getDrawable(resourceId);
47         mXPercent = xPct;
48         mYPercent = yPct;
49         mGravity = gravity;
50     }
51 
setAlpha(int alpha)52     public void setAlpha(int alpha) {
53         mImage.setAlpha(alpha);
54         mAlpha = alpha;
55     }
56 
getAlpha()57     public int getAlpha() {
58         return mAlpha;
59     }
60 
updateBounds(Rect bounds)61     public void updateBounds(Rect bounds) {
62         int width = mImage.getIntrinsicWidth();
63         int height = mImage.getIntrinsicHeight();
64         int left = bounds.left + (int) (mXPercent * bounds.width());
65         int top = bounds.top + (int) (mYPercent * bounds.height());
66         if ((mGravity & Gravity.CENTER_HORIZONTAL) == Gravity.CENTER_HORIZONTAL) {
67             left -= (width / 2);
68         }
69         if ((mGravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
70             top -= (height / 2);
71         }
72         mImage.setBounds(left, top, left + width, top + height);
73     }
74 
draw(Canvas canvas)75     public void draw(Canvas canvas) {
76         int c = canvas.save(Canvas.MATRIX_SAVE_FLAG);
77         mImage.draw(canvas);
78         canvas.restoreToCount(c);
79     }
80 }
81 
82 /**
83  * This is a custom composite drawable that has a fixed virtual size and dynamically lays out its
84  * children images relatively within its bounds.  This way, we can reduce the memory usage of a
85  * single, large sparsely populated image.
86  */
87 public class AllAppsBackgroundDrawable extends Drawable {
88 
89     private final TransformedImageDrawable mHand;
90     private final TransformedImageDrawable[] mIcons;
91     private final int mWidth;
92     private final int mHeight;
93 
94     private ObjectAnimator mBackgroundAnim;
95 
AllAppsBackgroundDrawable(Context context)96     public AllAppsBackgroundDrawable(Context context) {
97         Resources res = context.getResources();
98         mHand = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_hand,
99                 0.575f, 0.1f, Gravity.CENTER_HORIZONTAL);
100         mIcons = new TransformedImageDrawable[4];
101         mIcons[0] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_1,
102                 0.375f, 0, Gravity.CENTER_HORIZONTAL);
103         mIcons[1] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_2,
104                 0.3125f, 0.25f, Gravity.CENTER_HORIZONTAL);
105         mIcons[2] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_3,
106                 0.475f, 0.4f, Gravity.CENTER_HORIZONTAL);
107         mIcons[3] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_4,
108                 0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
109         mWidth = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_width);
110         mHeight = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_height);
111     }
112 
113     /**
114      * Animates the background alpha.
115      */
animateBgAlpha(float finalAlpha, int duration)116     public void animateBgAlpha(float finalAlpha, int duration) {
117         int finalAlphaI = (int) (finalAlpha * 255f);
118         if (getAlpha() != finalAlphaI) {
119             mBackgroundAnim = cancelAnimator(mBackgroundAnim);
120             mBackgroundAnim = ObjectAnimator.ofInt(this, "alpha", finalAlphaI);
121             mBackgroundAnim.setDuration(duration);
122             mBackgroundAnim.start();
123         }
124     }
125 
126     /**
127      * Sets the background alpha immediately.
128      */
setBgAlpha(float finalAlpha)129     public void setBgAlpha(float finalAlpha) {
130         int finalAlphaI = (int) (finalAlpha * 255f);
131         if (getAlpha() != finalAlphaI) {
132             mBackgroundAnim = cancelAnimator(mBackgroundAnim);
133             setAlpha(finalAlphaI);
134         }
135     }
136 
137     @Override
getIntrinsicWidth()138     public int getIntrinsicWidth() {
139         return mWidth;
140     }
141 
142     @Override
getIntrinsicHeight()143     public int getIntrinsicHeight() {
144         return mHeight;
145     }
146 
147     @Override
draw(Canvas canvas)148     public void draw(Canvas canvas) {
149         mHand.draw(canvas);
150         for (int i = 0; i < mIcons.length; i++) {
151             mIcons[i].draw(canvas);
152         }
153     }
154 
155     @Override
onBoundsChange(Rect bounds)156     protected void onBoundsChange(Rect bounds) {
157         super.onBoundsChange(bounds);
158         mHand.updateBounds(bounds);
159         for (int i = 0; i < mIcons.length; i++) {
160             mIcons[i].updateBounds(bounds);
161         }
162         invalidateSelf();
163     }
164 
165     @Override
setAlpha(int alpha)166     public void setAlpha(int alpha) {
167         mHand.setAlpha(alpha);
168         for (int i = 0; i < mIcons.length; i++) {
169             mIcons[i].setAlpha(alpha);
170         }
171         invalidateSelf();
172     }
173 
174     @Override
getAlpha()175     public int getAlpha() {
176         return mHand.getAlpha();
177     }
178 
179     @Override
setColorFilter(ColorFilter colorFilter)180     public void setColorFilter(ColorFilter colorFilter) {
181         // Do nothing
182     }
183 
184     @Override
getOpacity()185     public int getOpacity() {
186         return PixelFormat.TRANSLUCENT;
187     }
188 
cancelAnimator(ObjectAnimator animator)189     private ObjectAnimator cancelAnimator(ObjectAnimator animator) {
190         if (animator != null) {
191             animator.removeAllListeners();
192             animator.cancel();
193         }
194         return null;
195     }
196 }
197