1/*
2 * Copyright (C) 2011 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
17package com.android.internal.widget.multiwaveview;
18
19import android.content.res.Resources;
20import android.graphics.Canvas;
21import android.graphics.ColorFilter;
22import android.graphics.drawable.Drawable;
23import android.graphics.drawable.StateListDrawable;
24import android.util.Log;
25
26public class TargetDrawable {
27    private static final String TAG = "TargetDrawable";
28    private static final boolean DEBUG = false;
29
30    public static final int[] STATE_ACTIVE =
31            { android.R.attr.state_enabled, android.R.attr.state_active };
32    public static final int[] STATE_INACTIVE =
33            { android.R.attr.state_enabled, -android.R.attr.state_active };
34    public static final int[] STATE_FOCUSED =
35            { android.R.attr.state_enabled, -android.R.attr.state_active,
36                android.R.attr.state_focused };
37
38    private float mTranslationX = 0.0f;
39    private float mTranslationY = 0.0f;
40    private float mPositionX = 0.0f;
41    private float mPositionY = 0.0f;
42    private float mScaleX = 1.0f;
43    private float mScaleY = 1.0f;
44    private float mAlpha = 1.0f;
45    private Drawable mDrawable;
46    private boolean mEnabled = true;
47    private final int mResourceId;
48
49    /* package */ static class DrawableWithAlpha extends Drawable {
50        private float mAlpha = 1.0f;
51        private Drawable mRealDrawable;
52        public DrawableWithAlpha(Drawable realDrawable) {
53            mRealDrawable = realDrawable;
54        }
55        public void setAlpha(float alpha) {
56            mAlpha = alpha;
57        }
58        public float getAlpha() {
59            return mAlpha;
60        }
61        public void draw(Canvas canvas) {
62            mRealDrawable.setAlpha((int) Math.round(mAlpha * 255f));
63            mRealDrawable.draw(canvas);
64        }
65        @Override
66        public void setAlpha(int alpha) {
67            mRealDrawable.setAlpha(alpha);
68        }
69        @Override
70        public void setColorFilter(ColorFilter cf) {
71            mRealDrawable.setColorFilter(cf);
72        }
73        @Override
74        public int getOpacity() {
75            return mRealDrawable.getOpacity();
76        }
77    }
78
79    public TargetDrawable(Resources res, int resId) {
80        mResourceId = resId;
81        setDrawable(res, resId);
82    }
83
84    public void setDrawable(Resources res, int resId) {
85        // Note we explicitly don't set mResourceId to resId since we allow the drawable to be
86        // swapped at runtime and want to re-use the existing resource id for identification.
87        Drawable drawable = resId == 0 ? null : res.getDrawable(resId);
88        // Mutate the drawable so we can animate shared drawable properties.
89        mDrawable = drawable != null ? drawable.mutate() : null;
90        resizeDrawables();
91        setState(STATE_INACTIVE);
92    }
93
94    public TargetDrawable(TargetDrawable other) {
95        mResourceId = other.mResourceId;
96        // Mutate the drawable so we can animate shared drawable properties.
97        mDrawable = other.mDrawable != null ? other.mDrawable.mutate() : null;
98        resizeDrawables();
99        setState(STATE_INACTIVE);
100    }
101
102    public void setState(int [] state) {
103        if (mDrawable instanceof StateListDrawable) {
104            StateListDrawable d = (StateListDrawable) mDrawable;
105            d.setState(state);
106        }
107    }
108
109    public boolean hasState(int [] state) {
110        if (mDrawable instanceof StateListDrawable) {
111            StateListDrawable d = (StateListDrawable) mDrawable;
112            // TODO: this doesn't seem to work
113            return d.getStateDrawableIndex(state) != -1;
114        }
115        return false;
116    }
117
118    /**
119     * Returns true if the drawable is a StateListDrawable and is in the focused state.
120     *
121     * @return
122     */
123    public boolean isActive() {
124        if (mDrawable instanceof StateListDrawable) {
125            StateListDrawable d = (StateListDrawable) mDrawable;
126            int[] states = d.getState();
127            for (int i = 0; i < states.length; i++) {
128                if (states[i] == android.R.attr.state_focused) {
129                    return true;
130                }
131            }
132        }
133        return false;
134    }
135
136    /**
137     * Returns true if this target is enabled. Typically an enabled target contains a valid
138     * drawable in a valid state. Currently all targets with valid drawables are valid.
139     *
140     * @return
141     */
142    public boolean isEnabled() {
143        return mDrawable != null && mEnabled;
144    }
145
146    /**
147     * Makes drawables in a StateListDrawable all the same dimensions.
148     * If not a StateListDrawable, then justs sets the bounds to the intrinsic size of the
149     * drawable.
150     */
151    private void resizeDrawables() {
152        if (mDrawable instanceof StateListDrawable) {
153            StateListDrawable d = (StateListDrawable) mDrawable;
154            int maxWidth = 0;
155            int maxHeight = 0;
156            for (int i = 0; i < d.getStateCount(); i++) {
157                Drawable childDrawable = d.getStateDrawable(i);
158                maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth());
159                maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight());
160            }
161            if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: "
162                        + maxWidth + "x" + maxHeight);
163            d.setBounds(0, 0, maxWidth, maxHeight);
164            for (int i = 0; i < d.getStateCount(); i++) {
165                Drawable childDrawable = d.getStateDrawable(i);
166                if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: "
167                            + maxWidth + "x" + maxHeight);
168                childDrawable.setBounds(0, 0, maxWidth, maxHeight);
169            }
170        } else if (mDrawable != null) {
171            mDrawable.setBounds(0, 0,
172                    mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
173        }
174    }
175
176    public void setX(float x) {
177        mTranslationX = x;
178    }
179
180    public void setY(float y) {
181        mTranslationY = y;
182    }
183
184    public void setScaleX(float x) {
185        mScaleX = x;
186    }
187
188    public void setScaleY(float y) {
189        mScaleY = y;
190    }
191
192    public void setAlpha(float alpha) {
193        mAlpha = alpha;
194    }
195
196    public float getX() {
197        return mTranslationX;
198    }
199
200    public float getY() {
201        return mTranslationY;
202    }
203
204    public float getScaleX() {
205        return mScaleX;
206    }
207
208    public float getScaleY() {
209        return mScaleY;
210    }
211
212    public float getAlpha() {
213        return mAlpha;
214    }
215
216    public void setPositionX(float x) {
217        mPositionX = x;
218    }
219
220    public void setPositionY(float y) {
221        mPositionY = y;
222    }
223
224    public float getPositionX() {
225        return mPositionX;
226    }
227
228    public float getPositionY() {
229        return mPositionY;
230    }
231
232    public int getWidth() {
233        return mDrawable != null ? mDrawable.getIntrinsicWidth() : 0;
234    }
235
236    public int getHeight() {
237        return mDrawable != null ? mDrawable.getIntrinsicHeight() : 0;
238    }
239
240    public void draw(Canvas canvas) {
241        if (mDrawable == null || !mEnabled) {
242            return;
243        }
244        canvas.save(Canvas.MATRIX_SAVE_FLAG);
245        canvas.scale(mScaleX, mScaleY, mPositionX, mPositionY);
246        canvas.translate(mTranslationX + mPositionX, mTranslationY + mPositionY);
247        canvas.translate(-0.5f * getWidth(), -0.5f * getHeight());
248        mDrawable.setAlpha((int) Math.round(mAlpha * 255f));
249        mDrawable.draw(canvas);
250        canvas.restore();
251    }
252
253    public void setEnabled(boolean enabled) {
254        mEnabled  = enabled;
255    }
256
257    public int getResourceId() {
258        return mResourceId;
259    }
260}
261