clean code

This commit is contained in:
adrienmalin 2018-12-05 11:09:58 +01:00
parent d65944be2e
commit 5b41d35db3
8 changed files with 125 additions and 96 deletions

Binary file not shown.

View File

@ -1,25 +1,44 @@
package adrienmalin.pingpoints package adrienmalin.pingpoints
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.app.AppCompatDelegate
import android.view.View
import android.arch.lifecycle.ViewModelProviders import android.arch.lifecycle.ViewModelProviders
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.os.Bundle
import android.speech.RecognizerIntent import android.speech.RecognizerIntent
import android.speech.tts.TextToSpeech import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener
import android.support.design.widget.Snackbar 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.text.method.LinkMovementMethod
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.widget.* import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import java.util.* import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
class MatchActivity : AppCompatActivity(), TextToSpeech.OnInitListener { 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 REQ_CODE_SPEECH_INPUT = 1
val STT_RETRIES = 3
var matchModel: MatchModel? = null var matchModel: MatchModel? = null
var textScore: android.widget.TextView? = null var textScore: android.widget.TextView? = null
@ -29,6 +48,7 @@ class MatchActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
var tts: TextToSpeech? = null var tts: TextToSpeech? = null
var undo: MenuItem? = null var undo: MenuItem? = null
var redo: MenuItem? = null var redo: MenuItem? = null
var numSttCancelled:Int = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -56,17 +76,18 @@ class MatchActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
// Init or restore ViewModel // Init or restore ViewModel
matchModel = ViewModelProviders.of(this).get(MatchModel::class.java) matchModel = ViewModelProviders.of(this).get(MatchModel::class.java)
matchModel?.let { matchModel?.apply {
if (!it.matchStarted) { if (!matchStarted) {
intent.apply { intent.apply {
it.startMatch( startMatch(
getStringExtra("player1Name"), getStringExtra("player1Name"),
getStringExtra("player2Name"), getStringExtra("player2Name"),
getIntExtra("starterId", 0), getIntExtra("starterId", 0),
getBooleanExtra("enableTTS", false), getBooleanExtra("enableTTS", false),
getBooleanExtra("enableSTT", false) getBooleanExtra("enableSTT", false)
) )
for (player in it.players) player.pattern = Pattern.compile(getString(R.string.pattern, player.name)) for (player in players)
player.pattern = Pattern.compile(this@MatchActivity.getString(R.string.pattern, player.name))
} }
Snackbar.make( Snackbar.make(
findViewById(R.id.coordinatorLayout), findViewById(R.id.coordinatorLayout),
@ -74,18 +95,14 @@ class MatchActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
Snackbar.LENGTH_SHORT Snackbar.LENGTH_SHORT
).show() ).show()
} }
if (it.ttsEnabled) { if (ttsEnabled) {
tts = TextToSpeech(this, this) tts = TextToSpeech(this@MatchActivity, WaitForTtsInit())
if (it.sttEnabled) tts?.setOnUtteranceProgressListener(WaitForTTS(::launchStt)) if (sttEnabled) tts?.setOnUtteranceProgressListener(WaitForTtsSpeak())
} }
} }
updateUI() updateUI()
} }
override fun onInit(status: Int) {
ttsSpeak()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.match_menu, menu) menuInflater.inflate(R.menu.match_menu, menu)
undo = menu.findItem(R.id.action_undo) undo = menu.findItem(R.id.action_undo)
@ -229,20 +246,20 @@ class MatchActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
when (requestCode) { when (requestCode) {
REQ_CODE_SPEECH_INPUT -> { REQ_CODE_SPEECH_INPUT -> {
matchModel?.let { matchModel?.apply {
if (resultCode == RESULT_OK && data != null) { if (resultCode == RESULT_OK && data != null) {
var understood: Boolean = false var understood: Boolean = false
val result: String = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)[0] val result: String = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)[0]
for (player in it.players) { for (player in players) {
if (player.pattern?.matcher(result)?.find() == true) { if (player.pattern?.matcher(result)?.find() == true) {
understood = true understood = true
it.updateScore(player) updateScore(player)
updateUI() updateUI()
break break
} }
} }
if (!understood) { if (!understood) {
if (it.ttsEnabled) { if (ttsEnabled) {
tts?.speak( tts?.speak(
getString(R.string.not_understood), getString(R.string.not_understood),
TextToSpeech.QUEUE_FLUSH, TextToSpeech.QUEUE_FLUSH,
@ -258,6 +275,10 @@ class MatchActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
launchStt() launchStt()
} }
} }
} else {
numSttCancelled++
if (numSttCancelled >= STT_RETRIES)
sttEnabled = false
} }
} }
} }

View File

@ -34,12 +34,14 @@ class MatchModel : ViewModel() {
if ((players.sumBy { it.score } % 2 == 0) or (players.all { it.score >= 10 })) { if ((players.sumBy { it.score } % 2 == 0) or (players.all { it.score >= 10 })) {
serviceSide = relaunchSide.also { relaunchSide = serviceSide } serviceSide = relaunchSide.also { relaunchSide = serviceSide }
} }
saveState()
updateMatch()
}
fun updateMatch() {
val (minScore, maxScore) = players.map { it.score }.sorted() val (minScore, maxScore) = players.map { it.score }.sorted()
matchFinished = (maxScore >= 11) and (maxScore - minScore >= 2) matchFinished = (maxScore >= 11) and (maxScore - minScore >= 2)
matchPoint = (maxScore >= 10) and (maxScore - minScore >= 1) matchPoint = (maxScore >= 10) and (maxScore - minScore >= 1)
saveState()
} }
fun saveState() { fun saveState() {
@ -71,5 +73,6 @@ class MatchModel : ViewModel() {
else -> 0 else -> 0
} }
} }
updateMatch()
} }
} }

View File

@ -1,7 +1,6 @@
package adrienmalin.pingpoints package adrienmalin.pingpoints
import android.Manifest import android.Manifest
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
@ -30,7 +29,6 @@ class StarterNameActivity : AppCompatActivity() {
var previousMatch: SharedPreferences? = null var previousMatch: SharedPreferences? = null
var previousPlayers: Set<String> = emptySet() var previousPlayers: Set<String> = emptySet()
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_starter_name) setContentView(R.layout.activity_starter_name)
@ -42,32 +40,36 @@ class StarterNameActivity : AppCompatActivity() {
starterRadioGroup = findViewById(R.id.starterRadioGroup) starterRadioGroup = findViewById(R.id.starterRadioGroup)
enableTtsSwitch = findViewById(R.id.enableTtsSwitch) enableTtsSwitch = findViewById(R.id.enableTtsSwitch)
enableSttSwitch = findViewById(R.id.enableSttSwitch) enableSttSwitch = findViewById(R.id.enableSttSwitch)
// Check if function is available on switch checked or swapped // Check if function is available on switch checked or swapped
enableTtsSwitch?.setOnCheckedChangeListener { view, isChecked -> checkTTS() } enableTtsSwitch?.setOnCheckedChangeListener { view, isChecked -> checkTTS() }
enableTtsSwitch?.setOnTouchListener { view, event -> checkTTS(); false} enableTtsSwitch?.setOnTouchListener { view, event -> checkTTS(); false}
enableSttSwitch?.setOnCheckedChangeListener { view, isChecked -> checkSTT() } enableSttSwitch?.setOnCheckedChangeListener { view, isChecked -> checkSTT() }
enableSttSwitch?.setOnTouchListener { view, event -> checkSTT(); false} enableSttSwitch?.setOnTouchListener { view, event -> checkSTT(); false}
// Restore // Restore previous data
previousMatch = getPreferences(Context.MODE_PRIVATE) previousMatch = getPreferences(Context.MODE_PRIVATE)
previousMatch?.let { previousMatch?.apply {
previousPlayers = it.getStringSet("previousPlayers", emptySet()) previousPlayers = getStringSet("previousPlayers", emptySet())
val adapter = ArrayAdapter<String>( this, android.R.layout.simple_list_item_1, previousPlayers.toList()) val adapter = ArrayAdapter<String>(
this@StarterNameActivity,
android.R.layout.simple_list_item_1,
previousPlayers.toList())
player1NameInput?.run { player1NameInput?.run {
setText( setText(
it.getString("previousPlayer2", getString(R.string.player_1_default_name)), getString("previousPlayer2", getString(R.string.player_1_default_name)),
TextView.BufferType.EDITABLE) TextView.BufferType.EDITABLE)
setAdapter(adapter) setAdapter(adapter)
} }
player2NameInput?.run{ player2NameInput?.run{
setText( setText(
it.getString("previousPlayer1", getString(R.string.player_2_default_name)), getString("previousPlayer1", getString(R.string.player_2_default_name)),
TextView.BufferType.EDITABLE) TextView.BufferType.EDITABLE)
setAdapter(adapter) setAdapter(adapter)
} }
starterRadioGroup?.check(it.getInt("previousStarterId", R.id.radioPlayer1Starts)) starterRadioGroup?.check(getInt("previousStarterId", R.id.radioPlayer1Starts))
enableTtsSwitch?.isChecked = it.getBoolean("enableTTS", false) enableTtsSwitch?.isChecked = getBoolean("enableTTS", false)
enableSttSwitch?.isChecked = it.getBoolean("enableSTT", false) enableSttSwitch?.isChecked = getBoolean("enableSTT", false)
} }
} }
@ -78,12 +80,10 @@ class StarterNameActivity : AppCompatActivity() {
} }
fun checkTTS(){ fun checkTTS(){
enableTtsSwitch?.let { if (enableTtsSwitch?.isChecked == true) {
if (it.isChecked) { Intent().apply {
Intent().run { action = TextToSpeech.Engine.ACTION_CHECK_TTS_DATA
action = TextToSpeech.Engine.ACTION_CHECK_TTS_DATA startActivityForResult(this, CHECK_TTS)
startActivityForResult(this, CHECK_TTS)
}
} }
} }
} }
@ -99,7 +99,7 @@ class StarterNameActivity : AppCompatActivity() {
Snackbar.LENGTH_SHORT Snackbar.LENGTH_SHORT
).show() ).show()
enableTtsSwitch?.isChecked = false enableTtsSwitch?.isChecked = false
Intent().run { Intent().apply {
action = TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA action = TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA
startActivity(this) startActivity(this)
} }
@ -110,35 +110,51 @@ class StarterNameActivity : AppCompatActivity() {
} }
fun checkSTT(){ fun checkSTT(){
enableSttSwitch?.let { enableSttSwitch?.apply {
if (it.isChecked) { if (isChecked) {
if (!SpeechRecognizer.isRecognitionAvailable(this)) { if (!SpeechRecognizer.isRecognitionAvailable(this@StarterNameActivity)) {
Snackbar.make( Snackbar.make(
findViewById(R.id.coordinatorLayout), findViewById(R.id.coordinatorLayout),
R.string.STT_unavailable, R.string.STT_unavailable,
Snackbar.LENGTH_SHORT Snackbar.LENGTH_SHORT
).show() ).show()
enableSttSwitch?.isChecked = false isChecked = false
} else { } else {
// Ask for record audio permission // Ask for record audio permission
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(
this@StarterNameActivity,
Manifest.permission.RECORD_AUDIO
) != PackageManager.PERMISSION_GRANTED) {
// Permission is not granted // Permission is not granted
// Should we show an explanation? // Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { if (ActivityCompat.shouldShowRequestPermissionRationale(
AlertDialog.Builder(this) this@StarterNameActivity,
.setTitle(R.string.STT) Manifest.permission.RECORD_AUDIO
.setMessage(R.string.explain_record_audio_request) )
.setPositiveButton(R.string.OK) { dialog, id -> ) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), ASK_PERMISSIONS_RECORD_AUDIO) AlertDialog.Builder(this@StarterNameActivity).apply {
setTitle(R.string.STT)
setMessage(R.string.explain_record_audio_request)
setPositiveButton(R.string.OK) { dialog, id ->
ActivityCompat.requestPermissions(
this@StarterNameActivity,
arrayOf(Manifest.permission.RECORD_AUDIO),
ASK_PERMISSIONS_RECORD_AUDIO
)
} }
.setNegativeButton(R.string.cancel) { dialog, id -> setNegativeButton(R.string.cancel) { dialog, id ->
enableSttSwitch?.isChecked = false isChecked = false
} }
.create() create()
.show() show()
}
} else { } else {
// No explanation needed, we can request the permission. // No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), ASK_PERMISSIONS_RECORD_AUDIO) ActivityCompat.requestPermissions(
this@StarterNameActivity,
arrayOf(Manifest.permission.RECORD_AUDIO),
ASK_PERMISSIONS_RECORD_AUDIO
)
} }
} }
} }

View File

@ -27,11 +27,11 @@ class VictoryActivity : AppCompatActivity() {
// Init victoryModel // Init victoryModel
victoryModel = ViewModelProviders.of(this).get(VictoryModel::class.java) victoryModel = ViewModelProviders.of(this).get(VictoryModel::class.java)
victoryModel?.let { victoryModel?.apply {
if (!it.matchFinished) { if (!matchFinished) {
it.matchFinished = true matchFinished = true
it.winnerName = intent.getStringExtra("winnerName") winnerName = intent.getStringExtra("winnerName")
it.players = listOf( players = listOf(
Player( Player(
intent.getStringExtra("player1Name"), intent.getStringExtra("player1Name"),
intent.getIntExtra("player1Score", 0) intent.getIntExtra("player1Score", 0)
@ -42,36 +42,36 @@ class VictoryActivity : AppCompatActivity() {
) )
) )
it.previousMatches = previousMatch.getString("previousMatches", "") previousMatches = previousMatch.getString("previousMatches", "")
previousMatch.edit().apply { previousMatch.edit().apply {
putString( putString(
"previousMatches", "previousMatches",
getString( getString(
R.string.result, R.string.result,
it.players[0].name, players[0].name,
it.players[0].score, players[0].score,
it.players[1].score, players[1].score,
it.players[1].name players[1].name
) + it.previousMatches ) + previousMatches
) )
commit() commit()
} }
} }
// UpdateUI // UpdateUI
findViewById<TextView>(R.id.congrats).text = getString(R.string.congrats, it.winnerName) findViewById<TextView>(R.id.congrats).text = getString(R.string.congrats, winnerName)
findViewById<TextView>(R.id.player1NameTextView).text = it.players[0].name findViewById<TextView>(R.id.player1NameTextView).text = players[0].name
findViewById<TextView>(R.id.scoreTextView).text = getString( findViewById<TextView>(R.id.scoreTextView).text = getString(
R.string.score, R.string.score,
it.players[0].score, players[0].score,
it.players[1].score players[1].score
) )
findViewById<TextView>(R.id.player2NameTextView).text = it.players[1].name findViewById<TextView>(R.id.player2NameTextView).text = players[1].name
findViewById<GridView>(R.id.previousMatchesGrid).adapter = ArrayAdapter<String>( findViewById<GridView>(R.id.previousMatchesGrid).adapter = ArrayAdapter<String>(
this, this@VictoryActivity,
R.layout.grid_item, R.layout.grid_item,
R.id.grid_item_text, R.id.grid_item_text,
it.previousMatches.split("\t|\n".toRegex()) previousMatches.split("\t|\n".toRegex())
) )
} }
} }
@ -83,7 +83,7 @@ class VictoryActivity : AppCompatActivity() {
} }
fun share(view: View) { fun share(view: View) {
victoryModel?.let { victoryModel?.apply {
startActivity( startActivity(
Intent().apply { Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
@ -91,19 +91,19 @@ class VictoryActivity : AppCompatActivity() {
Intent.EXTRA_SUBJECT, Intent.EXTRA_SUBJECT,
getString( getString(
R.string.share_subject, R.string.share_subject,
it.players[0].name, players[0].name,
it.players[1].name players[1].name
) )
) )
putExtra( putExtra(
Intent.EXTRA_TEXT, Intent.EXTRA_TEXT,
getString( getString(
R.string.share_message, R.string.share_message,
it.players[0].name, players[0].name,
it.players[1].name, players[1].name,
it.winnerName, winnerName,
it.players[0].score, players[0].score,
it.players[1].score players[1].score
) )
) )
type = "text/plain" type = "text/plain"

View File

@ -1,11 +0,0 @@
package adrienmalin.pingpoints
import android.speech.tts.UtteranceProgressListener
class WaitForTTS(val callback: () -> Unit) : UtteranceProgressListener() {
override fun onDone(id: String) {
callback()
}
override fun onStart(id: String) {}
override fun onError(id: String) {}
}

View File

@ -38,6 +38,6 @@ Vous pouvez à tout moment changer la permission dans les paramètres Android."
<string name="share_message">"%s contre %s:\n%s a gagné par %d à %d\nPing Points est disponible sur Google Play\n "</string> <string name="share_message">"%s contre %s:\n%s a gagné par %d à %d\nPing Points est disponible sur Google Play\n "</string>
<string name="match_point">Balle de match</string> <string name="match_point">Balle de match</string>
<string name="STT_hint">Dîtes : \"Point pour %s\"\nou \"Point pour %s\"</string> <string name="STT_hint">Dîtes : \"Point pour %s\"\nou \"Point pour %s\"</string>
<string name="pattern">point pour %s</string> <string name="pattern">(?i:Point pour %s)</string>
<string name="not_understood">Pouvez-vous répéter ?</string> <string name="not_understood">Pouvez-vous répéter ?</string>
</resources> </resources>

View File

@ -44,7 +44,7 @@
<string name="share_message">%s vs. %s:\n%s won by %d to %d\nGet Ping Points on Google Play</string> <string name="share_message">%s vs. %s:\n%s won by %d to %d\nGet Ping Points on Google Play</string>
<string name="match_point">Match point</string> <string name="match_point">Match point</string>
<string name="STT_hint">Say: \"Point for %s\"\nor \"Point for %s\"</string> <string name="STT_hint">Say: \"Point for %s\"\nor \"Point for %s\"</string>
<string name="pattern">point for %s</string> <string name="pattern">(?i:Point for %s)</string>
<string name="not_understood">Can you repeat?</string> <string name="not_understood">Can you repeat?</string>
<string name="score" translatable="false">%d - %d</string> <string name="score" translatable="false">%d - %d</string>
</resources> </resources>