Die.kt
/*
Copyright 2015 Hermann Krumrey
This file is part of dice-roller.
dice-roller is an Android app that allows a user to roll a virtual
die. Multiple configurations are supported
dice-roller is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dice-roller is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dice-roller. If not, see <http://www.gnu.org/licenses/>.
*/
package net.namibsun.dice.objects
import android.content.Context
import android.os.Vibrator
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import net.namibsun.dice.R
import net.namibsun.dice.activities.BaseActivity
import java.security.SecureRandom
import kotlin.concurrent.thread
/**
* Abstract class that defines common Dice behaviour for different dice types
* @param context: The Activity using the Die
* @param view: The View displaying the die
* @param theme: The theme that defines the look of the View
* @param storedValueKey: The key string used to store the current value in the shared preferences
* @param limit: The maximum value of the Die
* @param minimum: The minimum value of the Die
* @param initialValue: The initial value of the Die
* @param wiggleAnimationResource: Overrides the default wiggle animation if set
*/
abstract class Die(
protected val context: BaseActivity,
val view: View,
protected var theme: Theme,
protected val storedValueKey: String,
protected var limit: Int = 6,
protected var minimum: Int = 1,
initialValue: Int = 5,
protected val wiggleAnimationResource: Int = R.anim.wiggle
) {
/**
* Draws the Current Information as the View
*/
abstract fun draw()
/**
* The current value of the die
*/
protected var currentValue: Int = initialValue
/**
* The vibrator is used to vibrate the device while the animation is running,
* if the theme allows for this.
*/
private val vibrator = this.context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
/**
* An object that can generate random numbers
*/
protected val random = SecureRandom()
/**
* Indicates if the change animation is currently active
*/
private var changeAnimating = false
/**
* Indicates if the wiggle animation is currently active
*/
private var wiggleAnimating = false
/**
* Sets the OnClickListener for the image view
*/
init {
this.view.setOnClickListener { this.roll() }
this.currentValue = this.context.prefs!!.getInt(this.storedValueKey, initialValue)
this.draw()
}
/**
* Displays the next value in the view
* @param specificNumber: Instead of a random number, display a specific number
*/
open fun next(specificNumber: Int? = null) {
this.currentValue = specificNumber ?: this.nextRandomNumber()
this.context.prefs!!.edit().putInt(this.storedValueKey, this.currentValue).apply()
this.draw()
}
/**
* Updates the theme of the Die
* @param theme: The theme to change to
*/
fun updateTheme(theme: Theme) {
this.theme = theme
this.draw()
}
/**
* Rolls the Die according to the settings provided by the theme
*/
fun roll() {
if (this.theme.vibrate && !this.theme.wiggleAnimation && !this.theme.changeAnimation) {
this.vibrator.vibrate(100)
this.next()
} else if (this.theme.wiggleAnimation) {
this.animate(AnimationUtils.loadAnimation(this.context, this.wiggleAnimationResource))
} else if (this.theme.changeAnimation) {
this.changeAnimation(1000)
} else {
this.next()
}
}
/**
* @return The current value of the die
*/
fun getValue(): Int {
return this.currentValue
}
/**
* @return true if the die is currently being animated, false otherwise
*/
fun isAnimating(): Boolean {
return this.changeAnimating || this.wiggleAnimating
}
/**
* Generates a new random number within the limits
* @return The generated random number
*/
protected fun nextRandomNumber(): Int {
return this.random.nextInt(this.limit - this.minimum + 1) + this.minimum
}
/**
* Vibrates the device for a set amount of time, but only if vibrating is enabled
* in the settings.
* @param duration: The duration of the vibration
*/
private fun vibrate(duration: Long) {
if (this.theme.vibrate) {
this.vibrator.vibrate(duration)
}
}
/**
* Starts animating the die
* @param animation: The animation to use
*/
private fun animate(animation: Animation) {
if (this.theme.vibrate) {
this.vibrate(animation.duration)
}
if (this.theme.changeAnimation) {
this.changeAnimation(animation.duration * 10)
}
animation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(animation: Animation?) { }
override fun onAnimationStart(animation: Animation?) { }
override fun onAnimationEnd(animation: Animation?) {
this@Die.next()
this@Die.wiggleAnimating = false
}
})
this.vibrate(animation.duration * 10)
this.wiggleAnimating = true
this.view.startAnimation(animation)
}
/**
* Changes the currently displayed value of the Die for a specified period of time
*/
private fun changeAnimation(duration: Long, pause: Int = 100) {
this.changeAnimating = true
if (this.theme.vibrate) {
this.vibrate(duration)
}
thread(start = true) {
var runtime = 0
while (runtime < duration) {
this.context.runOnUiThread { this.next() }
Thread.sleep(pause.toLong())
runtime += pause
}
this.changeAnimating = false
}
}
}