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