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