Skip to content

Commit 69bd20c

Browse files
committed
add voice mode setting to settings. ask for permission in android and iOS
1 parent b141115 commit 69bd20c

File tree

18 files changed

+228
-34
lines changed

18 files changed

+228
-34
lines changed

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/QuestionFragment.kt

Lines changed: 55 additions & 4 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,6 +22,7 @@ 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 {
@@ -344,4 +346,53 @@ class QuestionFragment : BaseNameGameViewFragment<QuestionPresenter>(), Question
344346
4 -> button4
345347
else -> null//throw IllegalStateException("Invalid button index")
346348
}
349+
350+
351+
private fun startSpeechToText() {
352+
353+
val speechRecognizer = SpeechRecognizer.createSpeechRecognizer(activity!!)
354+
val speechRecognizerIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
355+
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
356+
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
357+
358+
speechRecognizer.setRecognitionListener(object : RecognitionListener {
359+
override fun onReadyForSpeech(bundle: Bundle) {}
360+
361+
override fun onBeginningOfSpeech() {}
362+
363+
override fun onRmsChanged(v: Float) {}
364+
365+
override fun onBufferReceived(bytes: ByteArray) {}
366+
367+
override fun onEndOfSpeech() {}
368+
369+
override fun onError(i: Int) {}
370+
371+
override fun onResults(bundle: Bundle) {
372+
val matches = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)//getting all the matches
373+
//displaying the first match
374+
// if (matches != null)
375+
}
376+
377+
override fun onPartialResults(bundle: Bundle) {}
378+
379+
override fun onEvent(i: Int, bundle: Bundle) {}
380+
})
381+
382+
btn_end_game.setOnTouchListener { view, motionEvent ->
383+
when (motionEvent.action) {
384+
MotionEvent.ACTION_UP -> {
385+
speechRecognizer.stopListening()
386+
// editText.hint = getString(R.string.text_hint)
387+
}
388+
389+
MotionEvent.ACTION_DOWN -> {
390+
speechRecognizer.startListening(speechRecognizerIntent)
391+
// editText.setText("")
392+
// editText.hint = "Listening..."
393+
}
394+
}
395+
false
396+
}
397+
}
347398
}

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

Lines changed: 42 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() {
@@ -58,4 +66,37 @@ class SettingsDialogFragment: DialogFragment(), SettingsView {
5866
numberPicker.value = viewState.numQuestions
5967
categoryPicker.value = QuestionCategoryId.values().indexOf(viewState.categoryId)
6068
}
69+
70+
override fun askForMicPermissions() {
71+
setupPermissions()
72+
}
73+
74+
private fun setupPermissions() {
75+
val permission = ContextCompat.checkSelfPermission(activity!!,
76+
Manifest.permission.RECORD_AUDIO)
77+
78+
if (permission != PackageManager.PERMISSION_GRANTED) {
79+
Logger.d("Permission to record denied")
80+
makeRequest()
81+
}
82+
}
83+
84+
private fun makeRequest() {
85+
requestPermissions(
86+
arrayOf(Manifest.permission.RECORD_AUDIO),
87+
MainActivity.RECORD_REQUEST_CODE)
88+
}
89+
90+
override fun onRequestPermissionsResult(requestCode: Int,
91+
permissions: Array<String>, grantResults: IntArray) {
92+
when (requestCode) {
93+
MainActivity.RECORD_REQUEST_CODE -> {
94+
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
95+
presenter.microphonePermissionDenied()
96+
} else {
97+
presenter.microphonePermissionGranted()
98+
}
99+
}
100+
}
101+
}
61102
}

android/src/main/res/layout/fragment_settings.xml

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
xmlns:app="http://schemas.android.com/apk/res-auto"
44
xmlns:tools="http://schemas.android.com/tools"
55
android:layout_width="300dp"
6-
android:layout_height="400dp"
6+
android:layout_height="450dp"
77
android:minWidth="300dp"
88
android:minHeight="400dp">
99

@@ -39,26 +39,37 @@
3939
app:layout_constraintStart_toStartOf="parent"
4040
app:layout_constraintTop_toTopOf="parent" />
4141

42-
<TextView
43-
style="@style/SettingsLabelText"
44-
android:id="@+id/txt_num_questions"
45-
android:layout_width="70dp"
42+
<Switch
43+
android:id="@+id/switch_mic"
44+
android:layout_width="180dp"
4645
android:layout_height="wrap_content"
47-
android:layout_marginTop="8dp"
48-
android:gravity="center"
49-
android:text="Number of Questions"
50-
app:layout_constraintEnd_toEndOf="@+id/numberPicker"
51-
app:layout_constraintStart_toStartOf="@+id/numberPicker"
52-
app:layout_constraintTop_toBottomOf="@+id/numberPicker" />
46+
android:layout_marginTop="16dp"
47+
android:text="Voice mode"
48+
app:layout_constraintEnd_toEndOf="parent"
49+
app:layout_constraintHorizontal_bias="0.5"
50+
app:layout_constraintStart_toStartOf="parent"
51+
app:layout_constraintTop_toBottomOf="@+id/txt_num_questions" />
5352

5453
<TextView
55-
style="@style/SettingsLabelText"
5654
android:id="@+id/txt_num_questions2"
55+
style="@style/SettingsLabelText"
5756
android:layout_width="wrap_content"
5857
android:layout_height="wrap_content"
5958
android:layout_marginTop="8dp"
6059
android:text="Category"
6160
app:layout_constraintEnd_toEndOf="@+id/categoryPicker"
6261
app:layout_constraintStart_toStartOf="@+id/categoryPicker"
6362
app:layout_constraintTop_toBottomOf="@+id/categoryPicker" />
63+
64+
<TextView
65+
android:id="@+id/txt_num_questions"
66+
style="@style/SettingsLabelText"
67+
android:layout_width="70dp"
68+
android:layout_height="wrap_content"
69+
android:layout_marginTop="8dp"
70+
android:gravity="center"
71+
android:text="Number of Questions"
72+
app:layout_constraintEnd_toEndOf="@+id/numberPicker"
73+
app:layout_constraintStart_toStartOf="@+id/numberPicker"
74+
app:layout_constraintTop_toBottomOf="@+id/numberPicker" />
6475
</androidx.constraintlayout.widget.ConstraintLayout>

android/src/test/java/com/willowtreeapps/namegame/ReducersTest.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,15 @@ class ReducersTest {
134134
val final = reducer(initial, Actions.ChangeNumQuestionsSettingsAction(10))
135135

136136
assertEquals(10, final.settings.numQuestions)
137+
}
138+
139+
@Test
140+
fun `ChangeMicrophoneModeAction should update settings`() {
141+
val initial = generateInitialTestState()
142+
143+
val final = reducer(initial, Actions.ChangeMicrophoneModeSettingsAction(true))
137144

145+
assertEquals(true, final.settings.microphoneMode)
138146
}
139147

140148
private fun generateInitialTestState(): AppState {

common/src/commonMain/kotlin/com/willowtreeapps/common/Actions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ sealed class Actions : Action {
2929
class SettingsLoadedAction(val settings: UserSettings)
3030
class ChangeNumQuestionsSettingsAction(val num: Int)
3131
class ChangeCategorySettingsAction(val categoryId: QuestionCategoryId)
32+
class ChangeMicrophoneModeSettingsAction(val enabled: Boolean)
3233

3334
}
3435

common/src/commonMain/kotlin/com/willowtreeapps/common/AppState.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ data class Item(val id: ItemId,
6363
fun matches(name: String): Boolean {
6464
return displayName() == name
6565
}
66-
6766
}
6867

6968
enum class QuestionCategoryId(val displayName: String) {
@@ -81,7 +80,8 @@ enum class QuestionCategoryId(val displayName: String) {
8180
}
8281

8382
data class UserSettings(val numQuestions: Int,
84-
val categoryId: QuestionCategoryId) {
83+
val categoryId: QuestionCategoryId,
84+
val microphoneMode: Boolean = false) {
8585
companion object {
8686
fun defaults() = UserSettings(3, categoryId = QuestionCategoryId.CATS)
8787
}

common/src/commonMain/kotlin/com/willowtreeapps/common/GameEngine.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ class GameEngine(navigator: Navigator,
3838
fun <T : Presenter<*>> attachView(view: View<T>) = presenterFactory.attachView(view as View<Presenter<*>>)
3939

4040
fun detachView(view: View<*>) = presenterFactory.detachView(view)
41-
}
41+
}

common/src/commonMain/kotlin/com/willowtreeapps/common/Reducers.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ fun reducer(state: AppState, action: Any): AppState =
4444

4545
is ChangeNumQuestionsSettingsAction -> state.copy(settings = state.settings.copy(numQuestions = action.num))
4646
is ChangeCategorySettingsAction -> state.copy(settings = state.settings.copy(categoryId = action.categoryId))
47+
is ChangeMicrophoneModeSettingsAction -> state.copy(settings = state.settings.copy(microphoneMode = action.enabled))
4748
is SettingsLoadedAction -> state.copy(settings = action.settings)
4849

4950
else -> throw AssertionError("Action ${action::class.simpleName} not handled")

0 commit comments

Comments
 (0)