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