Create preferences

This commit is contained in:
2020-05-09 14:48:39 +02:00
parent bb0a2a94ed
commit 0850390635
13 changed files with 177 additions and 51 deletions

View File

@@ -33,6 +33,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'org.nanohttpd:nanohttpd:2.2.0'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
implementation 'androidx.preference:preference:1.1.0-rc01'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

View File

@@ -13,6 +13,10 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".preference.PreferencesActivity"
android:label="@string/title_activity_preferences"
android:parentActivityName=".MainActivity"/>
<service
android:name=".service.ForegroundService"

View File

@@ -3,10 +3,13 @@ package io.bartek
import android.content.*
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import io.bartek.preference.PreferencesActivity
import io.bartek.service.ForegroundService
import io.bartek.service.ServiceState
@@ -24,6 +27,19 @@ class MainActivity : AppCompatActivity() {
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId) {
R.id.open_preferences -> startActivity(Intent(this, PreferencesActivity::class.java))
}
return super.onOptionsItemSelected(item)
}
private fun updateViewAccordingToServiceState(newState: ServiceState) {
controlServerButton.isEnabled = true
when (newState) {
@@ -53,8 +69,6 @@ class MainActivity : AppCompatActivity() {
super.onPause()
}
fun openTTSSettings(view: View) = startActivity(Intent("com.android.settings.TTS_SETTINGS"))
fun controlServer(view: View) {
controlServerButton.isEnabled = false
when (ForegroundService.state) {

View File

@@ -0,0 +1,19 @@
package io.bartek.preference
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceFragmentCompat
import io.bartek.R
class PreferencesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_preferences)
supportFragmentManager
.beginTransaction()
.replace(R.id.preferences, PreferencesFragment())
.commit()
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
}

View File

@@ -0,0 +1,28 @@
package io.bartek.preference
import android.content.Intent
import android.os.Bundle
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import io.bartek.R
class PreferencesFragment : PreferenceFragmentCompat() {
private lateinit var portPreference: EditTextPreference
private lateinit var sayEndpointPreference: SwitchPreference
private lateinit var waveEndpointPreference: SwitchPreference
private lateinit var ttsEnginePreference: Preference
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
portPreference = findPreference("preference_port")!!
sayEndpointPreference = findPreference("preference_enable_say_endpoint")!!
waveEndpointPreference = findPreference("preference_enable_wave_endpoint")!!
ttsEnginePreference = findPreference("preference_tts")!!
ttsEnginePreference.setOnPreferenceClickListener {
startActivity(Intent("com.android.settings.TTS_SETTINGS"))
true
}
}
}

View File

@@ -3,24 +3,30 @@ package io.bartek.service
import android.app.*
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Color
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.PowerManager
import androidx.preference.PreferenceManager
import io.bartek.MainActivity
import io.bartek.R
import io.bartek.web.TTSServer
import java.lang.Integer.parseInt
class ForegroundService : Service() {
private var port: Int = 8080
private lateinit var preferences: SharedPreferences
private var wakeLock: PowerManager.WakeLock? = null
private var isServiceStarted = false
private var ttsServer: TTSServer? = null
private val port: Int
get() = parseInt(preferences.getString("preference_port", "8080")!!)
override fun onCreate() {
super.onCreate()
preferences = PreferenceManager.getDefaultSharedPreferences(this)
startForeground(1, createNotification())
}
@@ -68,10 +74,7 @@ class ForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
intent?.let {
when(it.action) {
START -> {
port = it.getIntExtra(PORT, port)
startService()
}
START -> startService()
STOP -> stopService()
}
}

View File

@@ -3,11 +3,10 @@ package io.bartek.web
import android.content.Context
import android.content.Intent
import android.speech.tts.TextToSpeech
import android.widget.Toast
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.preference.PreferenceManager
import fi.iki.elonen.NanoHTTPD
import fi.iki.elonen.NanoHTTPD.Response.Status.*
import io.bartek.R
import io.bartek.service.ServiceState
import io.bartek.tts.TTS
import org.json.JSONObject
@@ -18,11 +17,19 @@ private data class TTSRequestData(val text: String, val language: Locale)
class TTSServer(port: Int, private val context: Context) : NanoHTTPD(port),
TextToSpeech.OnInitListener {
private val preferences = PreferenceManager.getDefaultSharedPreferences(context)
private val tts = TTS(context, this)
override fun serve(session: IHTTPSession?): Response {
try {
return tryToServe(session)
session?.let {
return when(it.uri) {
"/wave" -> wave(it)
else -> throw ResponseException(NOT_FOUND, "")
}
}
throw ResponseException(BAD_REQUEST, "")
} catch (e: ResponseException) {
throw e
} catch (e: Exception) {
@@ -30,18 +37,8 @@ class TTSServer(port: Int, private val context: Context) : NanoHTTPD(port),
}
}
private fun tryToServe(session: IHTTPSession?): Response {
val (text, language) = getRequestData(validateRequest(session))
val (stream, size) = tts.performTTS(text, language)
return newFixedLengthResponse(OK, "audio/x-wav", stream, size)
}
private fun validateRequest(session: IHTTPSession?): IHTTPSession {
if (session == null) {
throw ResponseException(BAD_REQUEST, "")
}
if (session.uri != "/") {
private fun wave(session: IHTTPSession): Response {
if(!preferences.getBoolean("preference_enable_wave_endpoint", true)) {
throw ResponseException(NOT_FOUND, "")
}
@@ -53,8 +50,9 @@ class TTSServer(port: Int, private val context: Context) : NanoHTTPD(port),
throw ResponseException(BAD_REQUEST, "")
}
return session
val (text, language) = getRequestData(session)
val (stream, size) = tts.performTTS(text, language)
return newFixedLengthResponse(OK, "audio/x-wav", stream, size)
}
private fun getRequestData(session: IHTTPSession): TTSRequestData {

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
</vector>

View File

@@ -1,30 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
<LinearLayout 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"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/control_server_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:onClick="controlServer"
android:text="@string/main_activity_run" />
</LinearLayout>
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/ttsSettings"
android:id="@+id/control_server_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="openTTSSettings"
android:text="@string/main_activity_tts_settings" />
</LinearLayout>
android:layout_height="0dp"
android:layout_weight="2"
android:onClick="controlServer"
android:text="@string/main_activity_run" />
</LinearLayout>

View File

@@ -0,0 +1,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/preferences"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/open_preferences" android:icon="@drawable/ic_settings" app:showAsAction="always" android:title="Preferences" />
</menu>

View File

@@ -1,12 +1,23 @@
<resources>
<string name="app_name">TTS Server</string>
<string name="main_activity_run">Run</string>
<string name="main_activity_stop">Stop</string>
<string name="service_notification_category_name">HTTP Server</string>
<string name="service_notification_category_description">The fixed notification keeping the HTTP server alive</string>
<string name="service_notification_title">Server is running</string>
<string name="service_notification_text">The HTTP server is listening on port %1$d</string>
<string name="main_activity_server_port">Server port</string>
<string name="main_activity_run">Run</string>
<string name="main_activity_stop">Stop</string>
<string name="main_activity_tts_settings">TTS Settings</string>
<string name="title_activity_preferences">Settings</string>
<string name="preference_port_summary">The desired port on which HTTP server is intended to listening</string>
<string name="preference_port_title">Server port</string>
<string name="preference_enable_say_endpoint_summary">Allow HTTP clients to use /say endpoint which enables them to say message using builtin speakers or external ones connected to the device</string>
<string name="preference_enable_say_endpoint_title">Enable /say endpoint</string>
<string name="preference_enable_wave_endpoint_summary">Allow HTTP clients to use /wave endpoint which enables them to retrieve TTS message as wave file</string>
<string name="preference_enable_wave_endpoint_title">Enable /wave endpoint</string>
<string name="preference_tts_summary">Go to platform\'s TTS engine settings and adjust its parameters</string>
<string name="preference_tts_title">TTS engine settings</string>
<string name="preference_category_tts">TTS engine</string>
<string name="preference_category_server">Server</string>
</resources>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory android:title="@string/preference_category_server" app:iconSpaceReserved="false">
<EditTextPreference
android:defaultValue="8080"
android:inputType="number"
android:key="preference_port"
android:summary="@string/preference_port_summary"
android:title="@string/preference_port_title"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="true"
android:key="preference_enable_say_endpoint"
android:summary="@string/preference_enable_say_endpoint_summary"
android:title="@string/preference_enable_say_endpoint_title"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="true"
android:key="preference_enable_wave_endpoint"
android:summary="@string/preference_enable_wave_endpoint_summary"
android:title="@string/preference_enable_wave_endpoint_title"
app:iconSpaceReserved="false" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/preference_category_tts" app:iconSpaceReserved="false">
<Preference
android:key="preference_tts"
android:summary="@string/preference_tts_summary"
android:title="@string/preference_tts_title"
app:iconSpaceReserved="false" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>