/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.calendar import android.content.Context import android.graphics.Canvas import android.graphics.drawable.Drawable import android.util.AttributeSet import android.util.Log import android.view.Gravity import android.widget.Button /** * A button with more than two states. When the button is pressed * or clicked, the state transitions automatically. * * **XML attributes** * See [ MultiStateButton Attributes][R.styleable.MultiStateButton], * [Button][android.R.styleable.Button], [TextView Attributes][android.R.styleable.TextView], * [ ][android.R.styleable.View] * */ class MultiStateButton(context: Context?, attrs: AttributeSet?, defStyle: Int) : Button(context, attrs, defStyle) { //The current state for this button, ranging from 0 to maxState-1 var mState = 0 private set //The maximum number of states allowed for this button. private var mMaxStates = 1 //The currently displaying resource ID. This gets set to a default on creation and remains //on the last set if the resources get set to null. private var mButtonResource = 0 //A list of all drawable resources used by this button in the order it uses them. private var mButtonResources: IntArray private var mButtonDrawable: Drawable? = null constructor(context: Context?) : this(context, null) {} constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0) {} override fun performClick(): Boolean { /* When clicked, toggle the state */ transitionState() return super.performClick() } fun transitionState() { mState = (mState + 1) % mMaxStates setButtonDrawable(mButtonResources[mState]) } /** * Allows for a new set of drawable resource ids to be set. * * This sets the maximum states allowed to the length of the resources array. It will also * set the current state to the maximum allowed if it's greater than the new max. */ //@Throws(IllegalArgumentException::class) fun setButtonResources(resources: IntArray?) { if (resources == null) { throw IllegalArgumentException("Button resources cannot be null") } mMaxStates = resources.size if (mState >= mMaxStates) { mState = mMaxStates - 1 } mButtonResources = resources } /** * Attempts to set the state. Returns true if successful, false otherwise. */ fun setState(state: Int): Boolean { if (state >= mMaxStates || state < 0) { //When moved out of Calendar the tag should be changed. Log.w("Cal", "MultiStateButton state set to value greater than maxState or < 0") return false } mState = state setButtonDrawable(mButtonResources[mState]) return true } /** * Set the background to a given Drawable, identified by its resource id. * * @param resid the resource id of the drawable to use as the background */ fun setButtonDrawable(resid: Int) { if (resid != 0 && resid == mButtonResource) { return } mButtonResource = resid var d: Drawable? = null if (mButtonResource != 0) { d = getResources().getDrawable(mButtonResource) } setButtonDrawable(d) } /** * Set the background to a given Drawable * * @param d The Drawable to use as the background */ fun setButtonDrawable(d: Drawable?) { if (d != null) { if (mButtonDrawable != null) { mButtonDrawable?.setCallback(null) unscheduleDrawable(mButtonDrawable) } d.setCallback(this) d.setState(getDrawableState()) d.setVisible(getVisibility() === VISIBLE, false) mButtonDrawable = d mButtonDrawable?.setState(getDrawableState()) setMinHeight(mButtonDrawable?.getIntrinsicHeight() ?: 0) setWidth(mButtonDrawable?.getIntrinsicWidth() ?: 0) } refreshDrawableState() } protected override fun onDraw(canvas: Canvas) { super.onDraw(canvas) if (mButtonDrawable != null) { val verticalGravity: Int = getGravity() and Gravity.VERTICAL_GRAVITY_MASK val horizontalGravity: Int = getGravity() and Gravity.HORIZONTAL_GRAVITY_MASK val height: Int = mButtonDrawable?.getIntrinsicHeight() ?: 0 val width: Int = mButtonDrawable?.getIntrinsicWidth() ?: 0 var y = 0 var x = 0 when (verticalGravity) { Gravity.BOTTOM -> y = getHeight() - height Gravity.CENTER_VERTICAL -> y = (getHeight() - height) / 2 } when (horizontalGravity) { Gravity.RIGHT -> x = getWidth() - width Gravity.CENTER_HORIZONTAL -> x = (getWidth() - width) / 2 } mButtonDrawable?.setBounds(x, y, x + width, y + height) mButtonDrawable?.draw(canvas) } } init { //Currently using the standard buttonStyle, will update when new resources are added. //TODO add a more generic default button mButtonResources = intArrayOf(R.drawable.widget_show) setButtonDrawable(mButtonResources[mState]) } }