Skip to content

Commit 030c316

Browse files
authored
Merge pull request #26 from patjackson52/pj-speech
speech mode
2 parents b141115 + 6ae0cd4 commit 030c316

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+804
-290
lines changed

android/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
apply plugin: 'com.android.application'
23

34
apply plugin: 'kotlin-android'
@@ -34,6 +35,7 @@ android {
3435
tasks.lint.enabled = false
3536
}
3637

38+
3739
dependencies {
3840
implementation fileTree(dir: 'libs', include: ['*.jar'])
3941
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
@@ -53,8 +55,6 @@ dependencies {
5355
implementation "io.ktor:ktor-client-json-jvm:$ktorVersion"
5456
implementation group: 'org.slf4j', name: 'slf4j-android', version: '1.7.26'
5557

56-
implementation networkDependencies.values()
57-
5858
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
5959
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
6060
implementation project(':common')

android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package="com.willowtreeapps.namegame">
44

55
<uses-permission android:name="android.permission.INTERNET"/>
6+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
67

78
<application
89
android:name=".NameGameApp"

android/src/main/java/com/willowtreeapps/namegame/MainActivity.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class MyAppGlideModule : AppGlideModule()
1414

1515
class MainActivity : AppCompatActivity() {
1616

17+
companion object {
18+
const val RECORD_REQUEST_CODE = 52
19+
}
20+
1721
interface IOnBackPressed {
1822
fun onBackPressed(): Boolean
1923
}
@@ -37,4 +41,6 @@ class MainActivity : AppCompatActivity() {
3741
// (currentFragment as IOnBackPressed).onBackPressed()
3842
super.onBackPressed()
3943
}
44+
45+
4046
}

android/src/main/java/com/willowtreeapps/namegame/store/BaseNameGameViewFragment.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ package com.willowtreeapps.namegame.store
33
import android.os.Bundle
44
import androidx.fragment.app.Fragment
55
import com.willowtreeapps.common.Logger
6-
import com.willowtreeapps.common.Presenter
7-
import com.willowtreeapps.common.View
8-
import com.willowtreeapps.common.ui.GameResultsPresenter
6+
import com.willowtreeapps.common.ui.Presenter
7+
import com.willowtreeapps.common.ui.View
98
import com.willowtreeapps.namegame.NameGameApp
109
import kotlinx.coroutines.CoroutineScope
1110
import kotlinx.coroutines.Dispatchers

android/src/main/java/com/willowtreeapps/namegame/store/QuestionFragment.kt

Lines changed: 90 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ package com.willowtreeapps.namegame.store
22

33
import android.animation.AnimatorSet
44
import android.animation.ObjectAnimator
5+
import android.content.Intent
56
import android.graphics.Color
67
import android.os.Bundle
7-
import android.view.LayoutInflater
8-
import android.view.View
9-
import android.view.ViewGroup
10-
import android.view.ViewTreeObserver
8+
import android.speech.RecognitionListener
9+
import android.speech.RecognizerIntent
10+
import android.speech.SpeechRecognizer
11+
import android.view.*
1112
import android.view.animation.BounceInterpolator
1213
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
1314
import com.willowtreeapps.common.QuestionViewState
@@ -21,16 +22,77 @@ import androidx.interpolator.view.animation.FastOutSlowInInterpolator
2122
import com.willowtreeapps.common.Logger
2223
import com.willowtreeapps.common.ui.QuestionPresenter
2324
import com.willowtreeapps.namegame.*
25+
import java.util.*
2426

2527

2628
class QuestionFragment : BaseNameGameViewFragment<QuestionPresenter>(), QuestionView, MainActivity.IOnBackPressed {
29+
private lateinit var speechRecognizer: SpeechRecognizer
30+
private val speechRecognizerIntent by lazy {
31+
val speechRecIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
32+
speechRecIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
33+
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
34+
speechRecIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE,
35+
Locale.getDefault())
36+
speechRecIntent
37+
}
38+
39+
override fun openMic() {
40+
speechRecognizer.setRecognitionListener(object : RecognitionListener {
41+
override fun onReadyForSpeech(params: Bundle?) {
42+
}
43+
44+
override fun onRmsChanged(rmsdB: Float) {
45+
}
46+
47+
override fun onBufferReceived(buffer: ByteArray?) {
48+
}
49+
50+
override fun onPartialResults(partialResults: Bundle) {
51+
val matches = partialResults
52+
.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
53+
54+
//displaying the first match
55+
if (matches != null)
56+
presenter.namePicked(matches[0])
57+
}
58+
59+
override fun onEvent(eventType: Int, params: Bundle?) {
60+
}
61+
62+
override fun onBeginningOfSpeech() {
63+
}
64+
65+
override fun onEndOfSpeech() {
66+
}
67+
68+
override fun onError(error: Int) {
69+
}
70+
71+
override fun onResults(results: Bundle) {
72+
val matches = results
73+
.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
74+
75+
//displaying the first match
76+
if (matches != null)
77+
presenter.namePicked(matches[0])
78+
}
79+
80+
})
81+
speechRecognizer.startListening(speechRecognizerIntent)
82+
83+
}
84+
85+
override fun closeMic() {
86+
speechRecognizer.stopListening()
87+
}
2788

2889
private var restoreX: Float? = null
2990
private var restoreY: Float? = null
3091
private var lastCorrectBtn: Button? = null
3192
private var lastSelectedBtn: Button? = null
3293

3394
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
95+
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(activity!!)
3496
return inflater.inflate(R.layout.fragment_question, container, false)
3597
}
3698

@@ -115,12 +177,10 @@ class QuestionFragment : BaseNameGameViewFragment<QuestionPresenter>(), Question
115177
}
116178

117179
override fun showProfile(viewState: QuestionViewState) {
118-
activity?.runOnUiThread {
119-
if (btn_next.visibility == View.VISIBLE) {
120-
fadeNextButton { setProfileAndFadeIn(viewState) }
121-
} else {
122-
setProfileAndFadeIn(viewState)
123-
}
180+
if (btn_next.visibility == View.VISIBLE) {
181+
fadeNextButton { setProfileAndFadeIn(viewState) }
182+
} else {
183+
setProfileAndFadeIn(viewState)
124184
}
125185
}
126186

@@ -130,9 +190,7 @@ class QuestionFragment : BaseNameGameViewFragment<QuestionPresenter>(), Question
130190
}
131191

132192
override fun showWrongAnswer(viewState: QuestionViewState, isEndGame: Boolean) {
133-
activity?.runOnUiThread {
134-
wrongShakeAnimation(viewState) { hideButtonsShowNext(viewState, isEndGame) }
135-
}
193+
wrongShakeAnimation(viewState) { hideButtonsShowNext(viewState, isEndGame) }
136194
}
137195

138196
private val showButtonsAnimatorSet by lazy {
@@ -279,7 +337,7 @@ class QuestionFragment : BaseNameGameViewFragment<QuestionPresenter>(), Question
279337
}
280338

281339
override fun setTimerText(viewState: QuestionViewState) {
282-
activity?.runOnUiThread {
340+
if (view != null) {
283341
txt_timer.scaleX = 0f
284342
txt_timer.scaleY = 0f
285343
txt_timer.alpha = 1f
@@ -301,27 +359,24 @@ class QuestionFragment : BaseNameGameViewFragment<QuestionPresenter>(), Question
301359
}
302360

303361
override fun showTimesUp(viewState: QuestionViewState, isEndGame: Boolean) {
304-
activity?.runOnUiThread {
305-
txt_timer.scaleX = 0f
306-
txt_timer.scaleY = 0f
307-
txt_timer.text = viewState.timerText
308-
val restoreColor = txt_timer.currentTextColor
309-
txt_timer.setTextColor(ResourcesCompat.getColor(context?.resources!!, R.color.red, activity?.theme))
310-
txt_timer.animate()
311-
.scaleX(1f)
312-
.scaleY(1f)
313-
.setInterpolator(FastOutSlowInInterpolator())
314-
.setDuration(500)
315-
.withEndAction {
316-
showWrongAnswer(viewState, isEndGame)
317-
txt_timer?.animate()?.alpha(0f)
318-
?.withEndAction {
319-
txt_timer?.visibility = View.VISIBLE
320-
txt_timer?.setTextColor(restoreColor)
321-
}
322-
}
323-
324-
}
362+
txt_timer.scaleX = 0f
363+
txt_timer.scaleY = 0f
364+
txt_timer.text = viewState.timerText
365+
val restoreColor = txt_timer.currentTextColor
366+
txt_timer.setTextColor(ResourcesCompat.getColor(context?.resources!!, R.color.red, activity?.theme))
367+
txt_timer.animate()
368+
.scaleX(1f)
369+
.scaleY(1f)
370+
.setInterpolator(FastOutSlowInInterpolator())
371+
.setDuration(500)
372+
.withEndAction {
373+
showWrongAnswer(viewState, isEndGame)
374+
txt_timer?.animate()?.alpha(0f)
375+
?.withEndAction {
376+
txt_timer?.visibility = View.VISIBLE
377+
txt_timer?.setTextColor(restoreColor)
378+
}
379+
}
325380
}
326381

327382
private fun celebrate() {

android/src/main/java/com/willowtreeapps/namegame/store/SettingsDialogFragment.kt

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
package com.willowtreeapps.namegame.store
22

3+
import android.Manifest
4+
import android.content.pm.PackageManager
35
import android.os.Bundle
46
import android.view.LayoutInflater
57
import android.view.View
68
import android.view.ViewGroup
9+
import androidx.core.content.ContextCompat
710
import androidx.fragment.app.DialogFragment
11+
import com.willowtreeapps.common.Logger
812
import com.willowtreeapps.common.QuestionCategoryId
913
import com.willowtreeapps.common.SettingsViewState
1014
import com.willowtreeapps.common.ui.SettingsPresenter
1115
import com.willowtreeapps.common.ui.SettingsView
16+
import com.willowtreeapps.namegame.MainActivity
1217
import com.willowtreeapps.namegame.NameGameApp
1318
import com.willowtreeapps.namegame.R
1419
import kotlinx.android.synthetic.main.fragment_settings.*
1520

16-
class SettingsDialogFragment: DialogFragment(), SettingsView {
21+
class SettingsDialogFragment : DialogFragment(), SettingsView {
1722

1823
override lateinit var presenter: SettingsPresenter
1924

@@ -42,6 +47,9 @@ class SettingsDialogFragment: DialogFragment(), SettingsView {
4247
presenter.categoryChanged(QuestionCategoryId.fromOrdinal(newVal))
4348
}
4449
btn_ok.setOnClickListener { dismiss() }
50+
switch_mic.setOnCheckedChangeListener { buttonView, isChecked ->
51+
presenter.microphoneModeChanged(isChecked)
52+
}
4553
}
4654

4755
override fun onResume() {
@@ -57,5 +65,37 @@ class SettingsDialogFragment: DialogFragment(), SettingsView {
5765
override fun showSettings(viewState: SettingsViewState) {
5866
numberPicker.value = viewState.numQuestions
5967
categoryPicker.value = QuestionCategoryId.values().indexOf(viewState.categoryId)
68+
switch_mic.isChecked = viewState.isMicModeEnabled
69+
}
70+
71+
override fun askForMicPermissions() {
72+
setupPermissions()
73+
}
74+
75+
private fun setupPermissions() {
76+
val permission = ContextCompat.checkSelfPermission(activity!!,
77+
Manifest.permission.RECORD_AUDIO)
78+
79+
if (permission != PackageManager.PERMISSION_GRANTED) {
80+
Logger.d("Permission to record denied")
81+
requestPermissions(
82+
arrayOf(Manifest.permission.RECORD_AUDIO),
83+
MainActivity.RECORD_REQUEST_CODE)
84+
} else {
85+
presenter.microphonePermissionGranted()
86+
}
87+
}
88+
89+
override fun onRequestPermissionsResult(requestCode: Int,
90+
permissions: Array<String>, grantResults: IntArray) {
91+
when (requestCode) {
92+
MainActivity.RECORD_REQUEST_CODE -> {
93+
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
94+
presenter.microphonePermissionDenied()
95+
} else {
96+
presenter.microphonePermissionGranted()
97+
}
98+
}
99+
}
60100
}
61101
}

android/src/main/java/com/willowtreeapps/namegame/store/StartFragment.kt

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,10 @@ class StartFragment : Fragment(), CoroutineScope, StartView {
2626

2727
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
2828
btn_start.setOnClickListener {
29-
activity?.runOnUiThread {
30-
presenter?.startGame()
31-
}
29+
presenter?.startGame()
3230
}
3331
btn_settings.setOnClickListener {
34-
activity?.runOnUiThread {
35-
presenter?.settingsTapped()
36-
}
32+
presenter?.settingsTapped()
3733
}
3834
}
3935

@@ -48,20 +44,14 @@ class StartFragment : Fragment(), CoroutineScope, StartView {
4844
}
4945

5046
override fun hideLoading() {
51-
activity?.runOnUiThread {
52-
loading_spinner.visibility = View.GONE
53-
}
47+
loading_spinner.visibility = View.GONE
5448
}
5549

5650
override fun showLoading() {
57-
activity?.runOnUiThread {
58-
loading_spinner.visibility = View.VISIBLE
59-
}
51+
loading_spinner.visibility = View.VISIBLE
6052
}
6153

6254
override fun showError(msg: String) {
63-
activity?.runOnUiThread {
64-
txt_error.text = msg
65-
}
55+
txt_error.text = msg
6656
}
6757
}

0 commit comments

Comments
 (0)