This commit is contained in:
adrienmalin 2018-12-05 00:17:15 +01:00
parent 3a34e9a390
commit aab005447d
14 changed files with 205 additions and 94 deletions

View File

@ -5,18 +5,22 @@ import android.support.v7.app.AppCompatActivity
import android.support.v7.app.AppCompatDelegate
import android.view.View
import android.arch.lifecycle.ViewModelProviders
import android.content.ActivityNotFoundException
import android.content.Intent
import android.speech.RecognizerIntent
import android.speech.tts.TextToSpeech
import android.support.design.widget.Snackbar
import android.text.method.LinkMovementMethod
import android.view.Menu
import android.view.MenuItem
import android.widget.*
const val REQ_CODE_SPEECH_INPUT = 1
import java.util.*
import java.util.regex.Pattern
class MatchActivity : AppCompatActivity() {
val REQ_CODE_SPEECH_INPUT = 1
var matchModel: MatchModel? = null
var textScore: android.widget.TextView? = null
var textService: android.widget.TextView? = null
@ -63,15 +67,16 @@ class MatchActivity : AppCompatActivity() {
getBooleanExtra("enableSTT", false)
)
}
if (it.ttsEnabled) {
tts = TextToSpeech(this, TextToSpeech.OnInitListener { fun onInit(status: Int) {} })
}
Snackbar.make(
findViewById(R.id.coordinatorLayout),
R.string.button_hint,
Snackbar.LENGTH_SHORT
).show()
}
if (it.ttsEnabled) {
tts = TextToSpeech(this, TextToSpeech.OnInitListener { fun onInit(status: Int) {} })
if (it.sttEnabled) tts?.setOnUtteranceProgressListener(WaitForTTS(::launchStt))
}
}
updateUI()
}
@ -100,19 +105,19 @@ class MatchActivity : AppCompatActivity() {
}
fun updateUI() {
matchModel?.let {
matchModel?.apply {
textScore?.text = getString(
R.string.score,
it.players[it.serviceSide].score,
it.players[it.relaunchSide].score
players[serviceSide].score,
players[relaunchSide].score
)
textService?.text = getString(R.string.service, it.players[it.serviceSide].name)
textService?.text = getString(R.string.service, players[serviceSide].name)
for ((button, player) in buttons.zip(it.players)) {
for ((button, player) in buttons.zip(players)) {
button.text = fromHtml(getString(R.string.button_text, player.name, player.score))
}
when (it.serviceSide) {
when (serviceSide) {
0 -> {
imageViews[0]?.setImageResource(R.drawable.ic_service_0)
imageViews[1]?.setImageResource(0)
@ -123,50 +128,107 @@ class MatchActivity : AppCompatActivity() {
}
}
undo?.isVisible = when (it.playId) {
undo?.isVisible = when (playId) {
0 -> false
else -> true
}
redo?.isVisible = when (it.playId) {
it.history.size - 1 -> false
redo?.isVisible = when (playId) {
history.size - 1 -> false
else -> true
}
if (it.ttsEnabled) {
if (it.matchFinished()) {
val (loser, winner) = it.players.sortedBy { player -> player.score }
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 {
tts?.speak(
getString(
R.string.update_score_speech,
players[serviceSide].score,
players[relaunchSide].score,
players[serviceSide].name
),
TextToSpeech.QUEUE_FLUSH,
hashMapOf(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID to "MessageId")
)
if (matchPoint()) {
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 {
tts?.speak(
getString(
R.string.update_score_speech,
it.players[it.serviceSide].score,
it.players[it.relaunchSide].score,
it.players[it.serviceSide].name
),
TextToSpeech.QUEUE_FLUSH,
getString(R.string.match_point),
TextToSpeech.QUEUE_ADD,
hashMapOf(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID to "MessageId")
)
if (it.matchPoint()) {
tts?.speak(
getString(R.string.match_point),
TextToSpeech.QUEUE_ADD,
hashMapOf(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID to "MessageId")
)
}
}
}
}
}
if (it.matchFinished()) endMatch()
fun launchStt() {
matchModel?.apply {
if (sttEnabled) {
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault().getDisplayLanguage())
intent.putExtra(
RecognizerIntent.EXTRA_PROMPT,
getString(
R.string.STT_hint,
players[0].name,
players[1].name
)
)
try {
startActivityForResult(intent, 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?.let {
var understood: Boolean = false
if (resultCode == RESULT_OK && data != null) {
val result: String = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)[0]
for (player in it.players) {
if (Pattern.compile(getString(R.string.pattern, player.name)).matcher(result).find()) {
it.updateScore(player)
understood = true
break
}
}
}
if (!understood) launchStt()
}
}
else -> {
}
}
}
@ -175,7 +237,7 @@ class MatchActivity : AppCompatActivity() {
if (!matchFinished()) {
for (side in 0..1) {
if (view == buttons[side]) {
updateScore(side)
updateScore(players[side])
}
}
updateUI()
@ -189,8 +251,9 @@ class MatchActivity : AppCompatActivity() {
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("score", getString(R.string.score_only, it.players[0].score, it.players[1].score))
putExtra("player2Score", it.players[1].score)
}
)
}

View File

@ -15,7 +15,7 @@ class MatchModel : ViewModel() {
fun startMatch(player1Name: String, player2Name:String, starterId: Int, enableTTS: Boolean, enableSTT: Boolean) {
matchStarted = true
players = listOf(Player(player1Name), Player(player2Name))
players = listOf(Player(player1Name, 0), Player(player2Name, 0))
serviceSide = starterId
relaunchSide = when(serviceSide) {
0 -> 1
@ -26,9 +26,9 @@ class MatchModel : ViewModel() {
saveState()
}
fun updateScore(scorerId: Int) {
fun updateScore(scorer: player) {
playId++
players[scorerId].score++
scorer.score++
if ((players.sumBy { it.score } % 2 == 0) or (players.all { it.score >= 10 })) {
serviceSide = relaunchSide.also { relaunchSide = serviceSide }
}

View File

@ -2,5 +2,5 @@ package adrienmalin.pingpoints
data class Player (
var name: String,
var score: Int = 0
var score: Int
)

View File

@ -14,16 +14,14 @@ import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatActivity
import android.text.method.LinkMovementMethod
import android.view.View
import android.widget.*
const val CHECK_TTS = 1
const val ASK_PERMISSIONS_RECORD_AUDIO = 2
class StarterNameActivity : AppCompatActivity() {
val CHECK_TTS = 1
val ASK_PERMISSIONS_RECORD_AUDIO = 2
var player1NameInput: AutoCompleteTextView? = null
var player2NameInput: AutoCompleteTextView? = null
var starterRadioGroup: RadioGroup? = null

View File

@ -31,9 +31,16 @@ class VictoryActivity : AppCompatActivity() {
if (!it.matchFinished) {
it.matchFinished = true
it.winnerName = intent.getStringExtra("winnerName")
it.player1Name = intent.getStringExtra("player1Name")
it.player2Name = intent.getStringExtra("player2Name")
it.score = intent.getStringExtra("score")
it.players = listOf(
Player(
intent.getStringExtra("player1Name"),
intent.getIntExtra("player1Score", 0)
),
Player(
intent.getStringExtra("player2Name"),
intent.getIntExtra("player2Score", 0)
)
)
it.previousMatches = previousMatch.getString("previousMatches", "")
previousMatch.edit().apply {
@ -41,10 +48,11 @@ class VictoryActivity : AppCompatActivity() {
"previousMatches",
getString(
R.string.result,
it.player1Name,
it.score,
it.player2Name
) + '\n' + it.previousMatches
it.players[0].name,
it.players[0].score,
it.players[1].score,
it.players[1].name
) + it.previousMatches
)
commit()
}
@ -54,12 +62,18 @@ class VictoryActivity : AppCompatActivity() {
findViewById<TextView>(R.id.congrats).text = getString(R.string.congrats, it.winnerName)
findViewById<GridView>(R.id.resultGrid).adapter = ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
arrayOf(it.player1Name, it.score, it.player2Name)
R.layout.grid_item,
R.id.grid_item_text,
arrayOf<String>(
it.players[0].name,
it.players[0].score.toString() + " - " + it.players[1].score.toString(),
it.players[1].name
)
)
findViewById<GridView>(R.id.previousMatchesGrid).adapter = ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
R.layout.grid_item,
R.id.grid_item_text,
it.previousMatches.split("\t|\n".toRegex())
)
}
@ -80,18 +94,19 @@ class VictoryActivity : AppCompatActivity() {
Intent.EXTRA_SUBJECT,
getString(
R.string.share_subject,
it.player1Name,
it.player2Name
it.players[0].name,
it.players[1].name
)
)
putExtra(
Intent.EXTRA_TEXT,
getString(
R.string.share_message,
it.player1Name,
it.player2Name,
it.players[0].name,
it.players[1].name,
it.winnerName,
it.score
it.players[0].score,
it.players[1].score
)
)
type = "text/plain"

View File

@ -5,8 +5,6 @@ import android.arch.lifecycle.ViewModel
class VictoryModel : ViewModel() {
var matchFinished: Boolean = false
var winnerName:String = ""
var player1Name = ""
var player2Name = ""
var score = ""
var players: List<Player> = emptyList()
var previousMatches: String = ""
}

View File

@ -0,0 +1,11 @@
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

@ -88,7 +88,6 @@
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="1"
android:background="@color/colorAccent"
android:bufferType="spannable"
android:onClick="updateScore"
android:textAllCaps="false"
@ -98,7 +97,8 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/imgService0"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/button_text" android:layout_marginRight="8dp"/>
tools:text="@string/button_text" android:layout_marginRight="8dp"
android:theme="@style/ScoreButton"/>
<Button
android:id="@+id/buttonPlayer1"
@ -111,7 +111,6 @@
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="1"
android:background="@color/colorAccent"
android:bufferType="spannable"
android:onClick="updateScore"
android:textAllCaps="false"
@ -121,7 +120,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/imgService1"
app:layout_constraintStart_toEndOf="@+id/buttonPlayer0"
tools:text="@string/button_text"/>
tools:text="@string/button_text" android:theme="@style/ScoreButton"/>
<ImageView
android:id="@+id/imgService1"

View File

@ -82,7 +82,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="8dp"
android:background="@color/colorAccent"
android:bufferType="spannable"
android:onClick="updateScore"
android:textAllCaps="false"
@ -92,7 +91,9 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/imgService0"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/button_text" android:layout_margin="8dp" android:layout_weight="1"/>
tools:text="@string/button_text" android:layout_margin="8dp" android:layout_weight="1"
android:clickable="true" android:focusableInTouchMode="true"
android:theme="@style/ScoreButton"/>
<Button
android:id="@+id/buttonPlayer1"
@ -100,7 +101,6 @@
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:background="@color/colorAccent"
android:bufferType="spannable"
android:onClick="updateScore"
android:textAllCaps="false"
@ -110,7 +110,9 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/imgService1"
app:layout_constraintStart_toEndOf="@+id/buttonPlayer0"
tools:text="@string/button_text" android:layout_margin="8dp" android:layout_weight="1"/>
tools:text="@string/button_text" android:layout_margin="8dp" android:layout_weight="1"
android:clickable="true" android:focusableInTouchMode="true"
android:theme="@style/ScoreButton"/>
<ImageView
android:id="@+id/imgService1"

View File

@ -37,11 +37,13 @@
<GridView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp"
android:layout_marginRight="8dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/congrats" android:id="@+id/resultGrid"
android:numColumns="3"/>
android:numColumns="3"
android:textFilterEnabled="false" android:gravity="center" app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="24dp" android:layout_marginStart="24dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"/>
<TextView
android:text="@string/previous_matches"
android:layout_width="wrap_content"
@ -71,12 +73,16 @@
android:drawableStart="@drawable/ic_share" android:onClick="share"/>
<GridView
android:layout_width="0dp"
android:layout_height="0dp" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/textView3" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:layout_marginRight="8dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/textView3"
android:layout_marginBottom="8dp" app:layout_constraintBottom_toBottomOf="parent"
android:numColumns="5" android:id="@+id/previousMatchesGrid" android:clickable="false"/>
android:numColumns="3" android:id="@+id/previousMatchesGrid" android:clickable="false"
android:gravity="center" app:layout_constraintVertical_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginRight="8dp"/>
</android.support.constraint.ConstraintLayout>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="TextView"
android:layout_width="0dp"
android:layout_height="wrap_content" tools:layout_editor_absoluteY="254dp"
android:id="@+id/grid_item_text" android:gravity="center" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp" android:layout_marginRight="8dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"/>
</android.support.constraint.ConstraintLayout>

View File

@ -35,7 +35,8 @@ Vous pouvez à tout moment changer la permission dans les paramètres Android."
<string name="share">Partager</string>
<string name="victory_speech">%s gagne par %d à %d.</string>
<string name="share_subject">Match Ping Points : %s contre %s</string>
<string name="share_message">"%s contre %s:\n%s a gagné par %s\nPing Points est disponible sur Google Play\n "</string>
<string name="score_only">%d - %d</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="STT_hint">Dîtes : \"Point pour %s\"\nou \"Point pour %s\"</string>
<string name="pattern">Point pour %s</string>
</resources>

View File

@ -38,10 +38,11 @@
<string name="previous_matches">Previous matches</string>
<string name="new_match">New match</string>
<string name="share">Share</string>
<string name="result" translatable="false">"%s\t%s\t%s"</string>
<string name="result" translatable="false">"%s\t%d - %d\t%s\n"</string>
<string name="victory_speech">%s wins by %d to %d.</string>
<string name="share_subject">Ping Points Match: %s vs. %s</string>
<string name="share_message">%s vs. %s:\n%s won by %s\nGet Ping Points on Google Play</string>
<string name="score_only">"%d\t-\t%d"</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="STT_hint">Say: \"Point for %s\"\nor \"Point for %s\"</string>
<string name="pattern">Point for %s</string>
</resources>

View File

@ -3,4 +3,7 @@
<style name="AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="ScoreButton">
<item name="colorButtonNormal">@color/colorAccent</item>
</style>
</resources>