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