1 /* 2 * Copyright (C) 2021 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 * A button with more than two states. When the button is pressed 28 * or clicked, the state transitions automatically. 29 * 30 * **XML attributes** 31 * See [ MultiStateButton Attributes][R.styleable.MultiStateButton], 32 * [Button][android.R.styleable.Button], [TextView Attributes][android.R.styleable.TextView], 33 * [ ][android.R.styleable.View] 34 * 35 */ 36 class MultiStateButton(context: Context?, attrs: AttributeSet?, defStyle: Int) : 37 Button(context, attrs, defStyle) { 38 //The current state for this button, ranging from 0 to maxState-1 39 var mState = 0 40 private set 41 42 //The maximum number of states allowed for this button. 43 private var mMaxStates = 1 44 45 //The currently displaying resource ID. This gets set to a default on creation and remains 46 //on the last set if the resources get set to null. 47 private var mButtonResource = 0 48 49 //A list of all drawable resources used by this button in the order it uses them. 50 private var mButtonResources: IntArray 51 private var mButtonDrawable: Drawable? = null 52 53 constructor(context: Context?) : this(context, null) {} 54 constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0) {} 55 performClicknull56 override fun performClick(): Boolean { 57 /* When clicked, toggle the state */ 58 transitionState() 59 return super.performClick() 60 } 61 transitionStatenull62 fun transitionState() { 63 mState = (mState + 1) % mMaxStates 64 setButtonDrawable(mButtonResources[mState]) 65 } 66 67 /** 68 * Allows for a new set of drawable resource ids to be set. 69 * 70 * This sets the maximum states allowed to the length of the resources array. It will also 71 * set the current state to the maximum allowed if it's greater than the new max. 72 */ 73 //@Throws(IllegalArgumentException::class) setButtonResourcesnull74 fun setButtonResources(resources: IntArray?) { 75 if (resources == null) { 76 throw IllegalArgumentException("Button resources cannot be null") 77 } 78 mMaxStates = resources.size 79 if (mState >= mMaxStates) { 80 mState = mMaxStates - 1 81 } 82 mButtonResources = resources 83 } 84 85 /** 86 * Attempts to set the state. Returns true if successful, false otherwise. 87 */ setStatenull88 fun setState(state: Int): Boolean { 89 if (state >= mMaxStates || state < 0) { 90 //When moved out of Calendar the tag should be changed. 91 Log.w("Cal", "MultiStateButton state set to value greater than maxState or < 0") 92 return false 93 } 94 mState = state 95 setButtonDrawable(mButtonResources[mState]) 96 return true 97 } 98 99 /** 100 * Set the background to a given Drawable, identified by its resource id. 101 * 102 * @param resid the resource id of the drawable to use as the background 103 */ setButtonDrawablenull104 fun setButtonDrawable(resid: Int) { 105 if (resid != 0 && resid == mButtonResource) { 106 return 107 } 108 mButtonResource = resid 109 var d: Drawable? = null 110 if (mButtonResource != 0) { 111 d = getResources().getDrawable(mButtonResource) 112 } 113 setButtonDrawable(d) 114 } 115 116 /** 117 * Set the background to a given Drawable 118 * 119 * @param d The Drawable to use as the background 120 */ setButtonDrawablenull121 fun setButtonDrawable(d: Drawable?) { 122 if (d != null) { 123 if (mButtonDrawable != null) { 124 mButtonDrawable?.setCallback(null) 125 unscheduleDrawable(mButtonDrawable) 126 } 127 d.setCallback(this) 128 d.setState(getDrawableState()) 129 d.setVisible(getVisibility() === VISIBLE, false) 130 mButtonDrawable = d 131 mButtonDrawable?.setState(getDrawableState()) 132 setMinHeight(mButtonDrawable?.getIntrinsicHeight() ?: 0) 133 setWidth(mButtonDrawable?.getIntrinsicWidth() ?: 0) 134 } 135 refreshDrawableState() 136 } 137 onDrawnull138 protected override fun onDraw(canvas: Canvas) { 139 super.onDraw(canvas) 140 if (mButtonDrawable != null) { 141 val verticalGravity: Int = getGravity() and Gravity.VERTICAL_GRAVITY_MASK 142 val horizontalGravity: Int = getGravity() and Gravity.HORIZONTAL_GRAVITY_MASK 143 val height: Int = mButtonDrawable?.getIntrinsicHeight() ?: 0 144 val width: Int = mButtonDrawable?.getIntrinsicWidth() ?: 0 145 var y = 0 146 var x = 0 147 when (verticalGravity) { 148 Gravity.BOTTOM -> y = getHeight() - height 149 Gravity.CENTER_VERTICAL -> y = (getHeight() - height) / 2 150 } 151 when (horizontalGravity) { 152 Gravity.RIGHT -> x = getWidth() - width 153 Gravity.CENTER_HORIZONTAL -> x = (getWidth() - width) / 2 154 } 155 mButtonDrawable?.setBounds(x, y, x + width, y + height) 156 mButtonDrawable?.draw(canvas) 157 } 158 } 159 160 init { 161 //Currently using the standard buttonStyle, will update when new resources are added. 162 //TODO add a more generic default button 163 mButtonResources = intArrayOf(R.drawable.widget_show) 164 setButtonDrawable(mButtonResources[mState]) 165 } 166 }