2018-12-05 20:17:41 +01:00

292 lines
10 KiB
Kotlin

package adrienmalin.pingpoints
import android.arch.lifecycle.ViewModelProviders
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.speech.RecognizerIntent
import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.support.v7.app.AppCompatDelegate
import android.text.method.LinkMovementMethod
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import java.util.*
import java.util.regex.Pattern
class MatchActivity : AppCompatActivity() {
inner class WaitForTtsInit : TextToSpeech.OnInitListener {
override fun onInit(status: Int) {
ttsSpeak()
}
}
inner class WaitForTtsSpeak : UtteranceProgressListener() {
override fun onDone(id: String) {
launchStt()
}
override fun onStart(id: String) {}
override fun onError(id: String) {}
}
val REQ_CODE_SPEECH_INPUT = 1
val STT_RETRIES = 3
var matchModel: MatchModel? = null
var textScore: android.widget.TextView? = null
var textService: android.widget.TextView? = null
var buttons: Array<Button> = emptyArray()
var imageViews: Array<ImageView?> = emptyArray()
var tts: TextToSpeech? = null
var numSttCancelled:Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
setContentView(R.layout.activity_match)
// Set HTML text for icons credits
findViewById<TextView>(R.id.iconsCredit).run {
setText(fromHtml(getString(R.string.iconCredits)))
movementMethod = LinkMovementMethod.getInstance()
}
// Find views
textScore = findViewById(R.id.textScore)
textService = findViewById(R.id.textService)
buttons = arrayOf(
findViewById(R.id.buttonPlayer0),
findViewById(R.id.buttonPlayer1)
)
imageViews = arrayOf(
findViewById(R.id.imgService0),
findViewById(R.id.imgService1)
)
// Init or restore ViewModel
matchModel = ViewModelProviders.of(this).get(MatchModel::class.java)
matchModel?.apply {
if (!matchStarted) {
intent.apply {
startMatch(
getStringExtra("player1Name"),
getStringExtra("player2Name"),
getIntExtra("starterId", 0),
getBooleanExtra("enableTTS", false),
getBooleanExtra("enableSTT", false)
)
for (player in players)
player.pattern = Pattern.compile(this@MatchActivity.getString(R.string.pattern, player.name))
}
Snackbar.make(
findViewById(R.id.coordinatorLayout),
R.string.button_hint,
Snackbar.LENGTH_SHORT
).show()
}
if (ttsEnabled) {
tts = TextToSpeech(this@MatchActivity, WaitForTtsInit())
if (sttEnabled) tts?.setOnUtteranceProgressListener(WaitForTtsSpeak())
}
}
updateUI()
}
override fun onBackPressed() {
if (matchModel?.pointId == 0)
super.onBackPressed()
else {
matchModel?.undo()
updateUI()
}
}
fun updateUI() {
matchModel?.apply {
textScore?.text = getString(
R.string.score_score,
players[serviceSide].score,
players[relaunchSide].score
)
textService?.text = getString(R.string.service, players[serviceSide].name)
imageViews[0]?.setImageResource(
when(serviceSide) {
0 -> R.drawable.ic_service_0
else -> 0
}
)
for ((button, player) in buttons.zip(players)) {
button.text = fromHtml(getString(R.string.button_text, player.name, player.score))
}
imageViews[1]?.setImageResource(
when(serviceSide) {
0 -> 0
else -> R.drawable.ic_service_1
}
)
if (ttsEnabled) ttsSpeak()
if (matchFinished) endMatch()
else if (sttEnabled and !ttsEnabled) launchStt()
}
}
fun ttsSpeak() {
matchModel?.apply {
if (matchFinished) {
val (loser, winner) = players.sortedBy { it.score }
tts?.speak(
getString(
R.string.victory_speech,
winner.name,
winner.score,
loser.score
),
TextToSpeech.QUEUE_FLUSH,
hashMapOf(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID to "Victory")
)
} else {
var scoreSpeech: String = getString(
R.string.update_score_speech,
players[serviceSide].score,
players[relaunchSide].score,
players[serviceSide].name
)
if (matchPoint)
scoreSpeech += getString(R.string.match_point)
tts?.speak(
scoreSpeech,
TextToSpeech.QUEUE_FLUSH,
hashMapOf(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID to "MessageId")
)
}
}
}
fun launchStt() {
matchModel?.apply {
if (sttEnabled and !matchFinished) {
try {
startActivityForResult(
Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
)
putExtra(
RecognizerIntent.EXTRA_LANGUAGE,
Locale.getDefault().displayLanguage
)
putExtra(
RecognizerIntent.EXTRA_PROMPT,
getString(
R.string.STT_hint,
players[0].name,
players[1].name
)
)
},
REQ_CODE_SPEECH_INPUT
)
} catch (e: ActivityNotFoundException) {
sttEnabled = false
Snackbar.make(
findViewById(R.id.coordinatorLayout),
R.string.STT_unavailable,
Snackbar.LENGTH_SHORT
).show()
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQ_CODE_SPEECH_INPUT -> {
matchModel?.apply {
if (resultCode == RESULT_OK && data != null) {
var understood: Boolean = false
val result: String = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)[0]
for (player in players) {
if (player.pattern?.matcher(result)?.find() == true) {
understood = true
updateScore(player)
updateUI()
break
}
}
if (!understood) {
if (ttsEnabled) {
tts?.speak(
getString(R.string.not_understood),
TextToSpeech.QUEUE_FLUSH,
hashMapOf(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID to "MessageId")
)
}
else {
Snackbar.make(
findViewById(R.id.coordinatorLayout),
R.string.not_understood,
Snackbar.LENGTH_SHORT
).show()
launchStt()
}
}
} else {
numSttCancelled++
if (numSttCancelled >= STT_RETRIES) {
sttEnabled = false
Snackbar.make(
findViewById(R.id.coordinatorLayout),
R.string.STT_disabled,
Snackbar.LENGTH_SHORT
).show()
}
}
}
}
else -> {
}
}
}
fun updateScore(view: View) {
matchModel?.apply {
if (!matchFinished) {
for (side in 0..1) {
if (view == buttons[side]) {
updateScore(players[side])
}
}
updateUI()
}
}
}
fun endMatch() {
matchModel?.let {
startActivity(
Intent(this, VictoryActivity::class.java).apply {
putExtra("winnerName", it.players.maxBy{ player -> player.score }?.name)
putExtra("player1Name", it.players[0].name)
putExtra("player1Score", it.players[0].score)
putExtra("player2Name", it.players[1].name)
putExtra("player2Score", it.players[1].score)
}
)
}
}
}