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  */
16 package com.android.calendar;
17 
18 import android.content.Context;
19 import android.graphics.Canvas;
20 import android.graphics.drawable.Drawable;
21 import android.util.AttributeSet;
22 import android.util.Log;
23 import android.view.Gravity;
24 import 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 
41 public 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 
MultiStateButton(Context context)53     public MultiStateButton(Context context) {
54         this(context, null);
55     }
56 
MultiStateButton(Context context, AttributeSet attrs)57     public MultiStateButton(Context context, AttributeSet attrs) {
58         this(context, attrs, 0);
59     }
60 
MultiStateButton(Context context, AttributeSet attrs, int defStyle)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
performClick()72     public boolean performClick() {
73         /* When clicked, toggle the state */
74         transitionState();
75         return super.performClick();
76     }
77 
transitionState()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      */
setButtonResources(int[] resources)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      */
setState(int state)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 
getState()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      */
setButtonDrawable(int resid)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      */
setButtonDrawable(Drawable d)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
onDraw(Canvas canvas)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