1/*
2 * Copyright (C) 2010 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 */
16package com.android.calendar;
17
18import android.content.Context;
19import android.graphics.Canvas;
20import android.graphics.drawable.Drawable;
21import android.util.AttributeSet;
22import android.util.Log;
23import android.view.Gravity;
24import android.widget.Button;
25
26/**
27 * <p>
28 * A button with more than two states. When the button is pressed
29 * or clicked, the state transitions automatically.
30 * </p>
31 *
32 * <p><strong>XML attributes</strong></p>
33 * <p>
34 * See {@link R.styleable#MultiStateButton
35 * MultiStateButton Attributes}, {@link android.R.styleable#Button Button
36 * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link
37 * android.R.styleable#View View Attributes}
38 * </p>
39 */
40
41public class MultiStateButton extends Button {
42    //The current state for this button, ranging from 0 to maxState-1
43    private int mState;
44    //The maximum number of states allowed for this button.
45    private int mMaxStates;
46    //The currently displaying resource ID. This gets set to a default on creation and remains
47    //on the last set if the resources get set to null.
48    private int mButtonResource;
49    //A list of all drawable resources used by this button in the order it uses them.
50    private int[] mButtonResources;
51    private Drawable mButtonDrawable;
52
53    public MultiStateButton(Context context) {
54        this(context, null);
55    }
56
57    public MultiStateButton(Context context, AttributeSet attrs) {
58        this(context, attrs, 0);
59    }
60
61    public MultiStateButton(Context context, AttributeSet attrs, int defStyle) {
62        //Currently using the standard buttonStyle, will update when new resources are added.
63        super(context, attrs, defStyle);
64        mMaxStates = 1;
65        mState = 0;
66        //TODO add a more generic default button
67        mButtonResources = new int[] { R.drawable.widget_show };
68        setButtonDrawable(mButtonResources[mState]);
69    }
70
71    @Override
72    public boolean performClick() {
73        /* When clicked, toggle the state */
74        transitionState();
75        return super.performClick();
76    }
77
78    public void transitionState() {
79        mState = (mState + 1) % mMaxStates;
80        setButtonDrawable(mButtonResources[mState]);
81    }
82
83    /**
84     * Allows for a new set of drawable resource ids to be set.
85     *
86     * This sets the maximum states allowed to the length of the resources array. It will also
87     * set the current state to the maximum allowed if it's greater than the new max.
88     */
89    public void setButtonResources(int[] resources) throws IllegalArgumentException {
90        if(resources == null) {
91            throw new IllegalArgumentException("Button resources cannot be null");
92        }
93        mMaxStates = resources.length;
94        if(mState >= mMaxStates) {
95            mState = mMaxStates - 1;
96        }
97        mButtonResources = resources;
98    }
99
100    /**
101     * Attempts to set the state. Returns true if successful, false otherwise.
102     */
103    public boolean setState(int state){
104        if(state >= mMaxStates || state < 0) {
105            //When moved out of Calendar the tag should be changed.
106            Log.w("Cal", "MultiStateButton state set to value greater than maxState or < 0");
107            return false;
108        }
109        mState = state;
110        setButtonDrawable(mButtonResources[mState]);
111        return true;
112    }
113
114    public int getState() {
115        return mState;
116    }
117
118    /**
119     * Set the background to a given Drawable, identified by its resource id.
120     *
121     * @param resid the resource id of the drawable to use as the background
122     */
123    public void setButtonDrawable(int resid) {
124        if (resid != 0 && resid == mButtonResource) {
125            return;
126        }
127
128        mButtonResource = resid;
129
130        Drawable d = null;
131        if (mButtonResource != 0) {
132            d = getResources().getDrawable(mButtonResource);
133        }
134        setButtonDrawable(d);
135    }
136
137    /**
138     * Set the background to a given Drawable
139     *
140     * @param d The Drawable to use as the background
141     */
142    public void setButtonDrawable(Drawable d) {
143        if (d != null) {
144            if (mButtonDrawable != null) {
145                mButtonDrawable.setCallback(null);
146                unscheduleDrawable(mButtonDrawable);
147            }
148            d.setCallback(this);
149            d.setState(getDrawableState());
150            d.setVisible(getVisibility() == VISIBLE, false);
151            mButtonDrawable = d;
152            mButtonDrawable.setState(null);
153            setMinHeight(mButtonDrawable.getIntrinsicHeight());
154            setWidth(mButtonDrawable.getIntrinsicWidth());
155        }
156        refreshDrawableState();
157    }
158
159    @Override
160    protected void onDraw(Canvas canvas) {
161        super.onDraw(canvas);
162        if (mButtonDrawable != null) {
163            final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
164            final int horizontalGravity = getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK;
165            final int height = mButtonDrawable.getIntrinsicHeight();
166            final int width = mButtonDrawable.getIntrinsicWidth();
167
168            int y = 0;
169            int x = 0;
170
171            switch (verticalGravity) {
172                case Gravity.BOTTOM:
173                    y = getHeight() - height;
174                    break;
175                case Gravity.CENTER_VERTICAL:
176                    y = (getHeight() - height) / 2;
177                    break;
178            }
179            switch (horizontalGravity) {
180                case Gravity.RIGHT:
181                    x = getWidth() - width;
182                    break;
183                case Gravity.CENTER_HORIZONTAL:
184                    x = (getWidth() - width) / 2;
185                    break;
186            }
187
188            mButtonDrawable.setBounds(x, y, x + width, y + height);
189            mButtonDrawable.draw(canvas);
190        }
191    }
192}
193