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