blob: 98b1b5ee64c4d08b52b7b054d02279f90568fc23 [file] [log] [blame]
/*
* Copyright 2019 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.plausiblesoftware.drumthumper
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
class TriggerPad: View {
private val mDrawRect = RectF()
private val mPaint = Paint()
private val mUpColor = Color.LTGRAY
private val mDownColor = Color.DKGRAY
private var mIsDown = false
private var mText = "DrumPad"
private var mTextSizeSp = 28.0f
private val mTextColor = Color.BLACK
val DISPLAY_MASK = 0x00000003
val DISPLAY_RECT = 0x00000000
val DISPLAY_CIRCLE = 0x00000001
val DISPLAY_ROUND_RECT = 0x00000002
private var mDisplayFlags = DISPLAY_ROUND_RECT
interface DrumPadTriggerListener {
fun triggerDown(pad: TriggerPad)
fun triggerUp(pad: TriggerPad)
}
var mListeners = ArrayList<DrumPadTriggerListener>()
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
extractAttributes(attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int): super(context, attrs, defStyle) {
extractAttributes(attrs)
}
//
// Attributes
//
private fun extractAttributes(attrs: AttributeSet) {
val xmlns = "http://schemas.android.com/apk/res/android"
val textVal = attrs.getAttributeValue(xmlns, "text")
if (textVal != null) {
mText = textVal
}
}
//
// Layout Routines
//
private fun calcTextSizeInPixels(): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
mTextSizeSp,
resources.displayMetrics
)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
val padLeft = paddingLeft
val padRight = paddingRight
val padTop = paddingTop
val padBottom = paddingBottom
mDrawRect.set(padLeft.toFloat(),
padTop.toFloat(),
w - padRight.toFloat(),
h - padBottom.toFloat())
// mTextSize = mDrawRect.bottom / 4.0f
}
override fun onMeasure (widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = MeasureSpec.getSize(widthMeasureSpec)
val padTop = paddingTop
val padBottom = paddingBottom
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
var height = MeasureSpec.getSize(heightMeasureSpec)
val textSizePixels = calcTextSizeInPixels()
when (heightMode) {
MeasureSpec.AT_MOST -> run {
// mText = "AT_MOST"
val newHeight = (textSizePixels.toInt() * 2) + padTop + padBottom
height = minOf(height, newHeight) }
MeasureSpec.EXACTLY -> run {
/*mText = "EXACTLY"*/ }
MeasureSpec.UNSPECIFIED -> run {
// mText = "UNSPECIFIED"
height = textSizePixels.toInt() }
}
setMeasuredDimension(width, height)
}
//
// Drawing Routines
//
override fun onDraw(canvas: Canvas) {
// Face
if (mIsDown) {
mPaint.color = mDownColor
} else {
mPaint.color = mUpColor
}
when (mDisplayFlags and DISPLAY_MASK) {
DISPLAY_RECT -> canvas.drawRect(mDrawRect, mPaint)
DISPLAY_CIRCLE -> run {
val midX = mDrawRect.left + mDrawRect.width() / 2.0f
val midY = mDrawRect.top + mDrawRect.height() / 2.0f
val radius = minOf(mDrawRect.height() / 2.0f, mDrawRect.width() / 2.0f)
canvas.drawCircle(midX, midY, radius - 5.0f, mPaint)
}
DISPLAY_ROUND_RECT -> run {
val rad = minOf(mDrawRect.width() / 8.0f, mDrawRect.height() / 8.0f)
canvas.drawRoundRect(mDrawRect, rad, rad, mPaint)
}
}
// Text
val midX = mDrawRect.width() / 2
mPaint.textSize = calcTextSizeInPixels()
val textWidth = mPaint.measureText(mText)
mPaint.color = mTextColor
val textSizePixels = calcTextSizeInPixels()
canvas.drawText(mText, mDrawRect.left + midX - textWidth / 2,
mDrawRect.bottom/2 + textSizePixels/2, mPaint)
}
//
// Input Routines
//
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.actionMasked == MotionEvent.ACTION_DOWN ||
event.actionMasked == MotionEvent.ACTION_POINTER_DOWN) {
mIsDown = true
triggerDown()
invalidate()
return true
} else if (event.actionMasked == MotionEvent.ACTION_UP) {
mIsDown = false
triggerUp()
invalidate()
return true
}
return false
}
//
// Event Listeners
//
fun addListener(listener: DrumPadTriggerListener) {
mListeners.add(listener)
}
private fun triggerDown() {
for( listener in mListeners) {
listener.triggerDown(this)
}
}
private fun triggerUp() {
for( listener in mListeners) {
listener.triggerUp(this)
}
}
}