diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 88ea3aa..4513aa3 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -3,6 +3,12 @@
+
+
+
+
+
+
@@ -117,6 +123,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/androidTest/java/io/bartek/ExampleInstrumentedTest.kt b/app/src/androidTest/java/io/bartek/ExampleInstrumentedTest.kt
index 00563f4..41a46b1 100644
--- a/app/src/androidTest/java/io/bartek/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/io/bartek/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
package io.bartek
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
@@ -15,10 +13,10 @@ import org.junit.Assert.*
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("io.bartek", appContext.packageName)
- }
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("io.bartek", appContext.packageName)
+ }
}
diff --git a/app/src/main/java/io/bartek/MainActivity.kt b/app/src/main/java/io/bartek/MainActivity.kt
index 1d2535e..300d88c 100644
--- a/app/src/main/java/io/bartek/MainActivity.kt
+++ b/app/src/main/java/io/bartek/MainActivity.kt
@@ -20,88 +20,84 @@ import io.bartek.service.ServiceState
class MainActivity : AppCompatActivity() {
- private lateinit var controlServerButton: AppCompatImageButton
- private lateinit var promptText: TextView
+ private lateinit var serverControlButton: AppCompatImageButton
+ private lateinit var promptText: TextView
- private val receiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- intent?.let {
- updateViewAccordingToServiceState(
- ServiceState.valueOf(
- it.getStringExtra(ForegroundService.STATE) ?: ServiceState.STOPPED.name
- )
- )
- }
- }
- }
+ private val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ (intent?.getStringExtra(ForegroundService.STATE) ?: ServiceState.STOPPED.name)
+ .let { ServiceState.valueOf(it) }
+ .let { updateViewAccordingToServiceState(it) }
+ }
+ }
- override fun onCreateOptionsMenu(menu: Menu?): Boolean {
- menuInflater.inflate(R.menu.menu_main, menu)
- return true
- }
+ 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))
- R.id.open_help -> startActivity(Intent(this, HelpActivity::class.java))
- }
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.open_preferences -> startActivity(Intent(this, PreferencesActivity::class.java))
+ R.id.open_help -> startActivity(Intent(this, HelpActivity::class.java))
+ }
- return super.onOptionsItemSelected(item)
- }
+ return super.onOptionsItemSelected(item)
+ }
- private fun updateViewAccordingToServiceState(newState: ServiceState) {
- controlServerButton.isEnabled = true
- when (newState) {
- ServiceState.STOPPED -> {
- controlServerButton.setImageResource(R.drawable.ic_power_off)
- promptText.text = getString(R.string.main_activity_prompt_to_run)
- }
- ServiceState.RUNNING -> {
- controlServerButton.setImageResource(R.drawable.ic_power_on)
- promptText.text = getString(R.string.main_activity_prompt_to_stop)
- }
- }
- }
+ private fun updateViewAccordingToServiceState(newState: ServiceState) {
+ serverControlButton.isEnabled = true
+ when (newState) {
+ ServiceState.STOPPED -> {
+ serverControlButton.setImageResource(R.drawable.ic_power_off)
+ promptText.text = getString(R.string.main_activity_prompt_to_run)
+ }
+ ServiceState.RUNNING -> {
+ serverControlButton.setImageResource(R.drawable.ic_power_on)
+ promptText.text = getString(R.string.main_activity_prompt_to_stop)
+ }
+ }
+ }
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- controlServerButton = findViewById(R.id.control_server_button)
- promptText = findViewById(R.id.prompt_text)
- }
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ serverControlButton = findViewById(R.id.server_control_button)
+ promptText = findViewById(R.id.prompt_text)
+ }
- override fun onResume() {
- super.onResume()
- LocalBroadcastManager
- .getInstance(this)
- .registerReceiver(receiver, IntentFilter(ForegroundService.CHANGE_STATE))
- updateViewAccordingToServiceState(ForegroundService.state)
- }
+ override fun onResume() {
+ super.onResume()
+ LocalBroadcastManager
+ .getInstance(this)
+ .registerReceiver(receiver, IntentFilter(ForegroundService.CHANGE_STATE))
+ updateViewAccordingToServiceState(ForegroundService.state)
+ }
- override fun onPause() {
- LocalBroadcastManager
- .getInstance(this)
- .unregisterReceiver(receiver)
- super.onPause()
- }
+ override fun onPause() {
+ LocalBroadcastManager
+ .getInstance(this)
+ .unregisterReceiver(receiver)
+ super.onPause()
+ }
- fun controlServer(view: View) {
- controlServerButton.isEnabled = false
- when (ForegroundService.state) {
- ServiceState.STOPPED -> actionOnService(ForegroundService.START)
- ServiceState.RUNNING -> actionOnService(ForegroundService.STOP)
- }
- }
+ fun controlServer(view: View) {
+ serverControlButton.isEnabled = false
+ when (ForegroundService.state) {
+ ServiceState.STOPPED -> actionOnService(ForegroundService.START)
+ ServiceState.RUNNING -> actionOnService(ForegroundService.STOP)
+ }
+ }
- private fun actionOnService(action: String) {
- Intent(this, ForegroundService::class.java).also {
- it.action = action
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(it)
- return
- }
+ private fun actionOnService(action: String) {
+ Intent(this, ForegroundService::class.java).also {
+ it.action = action
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForegroundService(it)
+ return
+ }
- startService(it)
- }
- }
+ startService(it)
+ }
+ }
}
diff --git a/app/src/main/java/io/bartek/help/HelpActivity.kt b/app/src/main/java/io/bartek/help/HelpActivity.kt
index 9d84010..82560cb 100644
--- a/app/src/main/java/io/bartek/help/HelpActivity.kt
+++ b/app/src/main/java/io/bartek/help/HelpActivity.kt
@@ -7,24 +7,24 @@ import io.bartek.R
import java.util.*
class HelpActivity : AppCompatActivity() {
- private lateinit var helpView: WebView
+ private lateinit var helpView: WebView
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_help)
- helpView = findViewById(R.id.help_view)
- loadHelp()
- }
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_help)
+ helpView = findViewById(R.id.help_view)
+ loadHelp()
+ }
- private fun loadHelp() {
- val lang = Locale.getDefault().language
- val file = HELP_FILE.format(".$lang")
- .takeIf { resources.assets.list("help")?.contains(it) == true }
- ?: HELP_FILE.format("")
- helpView.loadUrl("file:///android_asset/help/${file}")
- }
+ private fun loadHelp() {
+ val lang = Locale.getDefault().language
+ val file = HELP_FILE.format(".$lang")
+ .takeIf { resources.assets.list("help")?.contains(it) == true }
+ ?: HELP_FILE.format("")
+ helpView.loadUrl("file:///android_asset/help/${file}")
+ }
- companion object {
- private const val HELP_FILE = "help%s.html"
- }
+ companion object {
+ private const val HELP_FILE = "help%s.html"
+ }
}
diff --git a/app/src/main/java/io/bartek/preference/IntEditTextPreference.kt b/app/src/main/java/io/bartek/preference/IntEditTextPreference.kt
index bc8e68a..94b5765 100644
--- a/app/src/main/java/io/bartek/preference/IntEditTextPreference.kt
+++ b/app/src/main/java/io/bartek/preference/IntEditTextPreference.kt
@@ -3,30 +3,31 @@ package io.bartek.preference
import android.content.Context
import android.util.AttributeSet
import androidx.preference.EditTextPreference
-import java.lang.Integer.parseInt
class IntEditTextPreference : EditTextPreference {
- constructor(
- context: Context?,
- attrs: AttributeSet?,
- defStyleAttr: Int,
- defStyleRes: Int
- ) : super(context, attrs, defStyleAttr, defStyleRes)
+ constructor(
+ context: Context?,
+ attrs: AttributeSet?,
+ defStyleAttr: Int,
+ defStyleRes: Int
+ ) : super(context, attrs, defStyleAttr, defStyleRes)
- constructor(
- context: Context?,
- attrs: AttributeSet?,
- defStyleAttr: Int
- ) : super(context, attrs, defStyleAttr)
+ constructor(
+ context: Context?,
+ attrs: AttributeSet?,
+ defStyleAttr: Int
+ ) : super(context, attrs, defStyleAttr)
- constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
- constructor(context: Context?) : super(context)
-
- override fun getPersistedString(defaultReturnValue: String?) =
- getPersistedInt(Integer.valueOf(defaultReturnValue ?: "-1")).toString()
-
- override fun persistString(value: String?) = persistInt(Integer.valueOf(value ?: "-1"))
+ constructor(context: Context?) : super(context)
+ override fun getPersistedString(defaultReturnValue: String?) = (defaultReturnValue ?: "-1")
+ .let { Integer.valueOf(it) }
+ .let { getPersistedInt(it) }
+ .toString()
+ override fun persistString(value: String?) = (value ?: "-1")
+ .let { Integer.valueOf(it) }
+ .let { persistInt(it) }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/bartek/preference/Preference.kt b/app/src/main/java/io/bartek/preference/Preference.kt
index 6a22bd8..7013344 100644
--- a/app/src/main/java/io/bartek/preference/Preference.kt
+++ b/app/src/main/java/io/bartek/preference/Preference.kt
@@ -2,8 +2,8 @@ package io.bartek.preference
object PreferenceKey {
- const val PORT = "preference_port"
- const val ENABLE_SAY_ENDPOINT = "preference_enable_say_endpoint"
- const val ENABLE_WAVE_ENDPOINT = "preference_enable_wave_endpoint"
- const val TTS = "preference_tts"
+ const val PORT = "preference_port"
+ const val ENABLE_SAY_ENDPOINT = "preference_enable_say_endpoint"
+ const val ENABLE_WAVE_ENDPOINT = "preference_enable_wave_endpoint"
+ const val TTS = "preference_tts"
}
\ No newline at end of file
diff --git a/app/src/main/java/io/bartek/preference/PreferencesActivity.kt b/app/src/main/java/io/bartek/preference/PreferencesActivity.kt
index 8e252cf..9e154b3 100644
--- a/app/src/main/java/io/bartek/preference/PreferencesActivity.kt
+++ b/app/src/main/java/io/bartek/preference/PreferencesActivity.kt
@@ -6,13 +6,13 @@ 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)
- }
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_preferences)
+ supportFragmentManager
+ .beginTransaction()
+ .replace(R.id.preferences, PreferencesFragment())
+ .commit()
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/bartek/preference/PreferencesFragment.kt b/app/src/main/java/io/bartek/preference/PreferencesFragment.kt
index 6d6f9d1..13087b5 100644
--- a/app/src/main/java/io/bartek/preference/PreferencesFragment.kt
+++ b/app/src/main/java/io/bartek/preference/PreferencesFragment.kt
@@ -15,57 +15,53 @@ import io.bartek.service.ForegroundService
import io.bartek.service.ServiceState
class PreferencesFragment : PreferenceFragmentCompat() {
- private lateinit var portPreference: IntEditTextPreference
- private lateinit var sayEndpointPreference: SwitchPreference
- private lateinit var waveEndpointPreference: SwitchPreference
- private lateinit var ttsEnginePreference: Preference
+ private lateinit var portPreference: IntEditTextPreference
+ private lateinit var sayEndpointPreference: SwitchPreference
+ private lateinit var waveEndpointPreference: SwitchPreference
+ private lateinit var ttsEnginePreference: Preference
- private val receiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- intent?.let {
- updateViewAccordingToServiceState(
- ServiceState.valueOf(
- it.getStringExtra(ForegroundService.STATE) ?: ServiceState.STOPPED.name
- )
- )
- }
- }
- }
+ private val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ (intent?.getStringExtra(ForegroundService.STATE) ?: ServiceState.STOPPED.name)
+ .let { ServiceState.valueOf(it) }
+ .let { updateViewAccordingToServiceState(it) }
+ }
+ }
- private fun updateViewAccordingToServiceState(state: ServiceState) {
- portPreference.isEnabled = state == ServiceState.STOPPED
- }
+ private fun updateViewAccordingToServiceState(state: ServiceState) {
+ portPreference.isEnabled = state == ServiceState.STOPPED
+ }
- override fun onResume() {
- super.onResume()
- LocalBroadcastManager
- .getInstance(context!!)
- .registerReceiver(receiver, IntentFilter(ForegroundService.CHANGE_STATE))
- updateViewAccordingToServiceState(ForegroundService.state)
- }
+ override fun onResume() {
+ super.onResume()
+ LocalBroadcastManager
+ .getInstance(context!!)
+ .registerReceiver(receiver, IntentFilter(ForegroundService.CHANGE_STATE))
+ updateViewAccordingToServiceState(ForegroundService.state)
+ }
- override fun onPause() {
- LocalBroadcastManager
- .getInstance(context!!)
- .unregisterReceiver(receiver)
- super.onPause()
- }
+ override fun onPause() {
+ LocalBroadcastManager
+ .getInstance(context!!)
+ .unregisterReceiver(receiver)
+ super.onPause()
+ }
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- setPreferencesFromResource(R.xml.preferences, rootKey)
- portPreference = findPreference(PreferenceKey.PORT)!!
- portPreference.setOnBindEditTextListener { it.inputType = InputType.TYPE_CLASS_NUMBER }
- sayEndpointPreference = findPreference(PreferenceKey.ENABLE_SAY_ENDPOINT)!!
- waveEndpointPreference = findPreference(PreferenceKey.ENABLE_WAVE_ENDPOINT)!!
- ttsEnginePreference = findPreference(PreferenceKey.TTS)!!
- ttsEnginePreference.setOnPreferenceClickListener {
- startActivity(Intent(ANDROID_TTS_SETTINGS))
- true
- }
- updateViewAccordingToServiceState(ForegroundService.state)
- }
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ setPreferencesFromResource(R.xml.preferences, rootKey)
+ portPreference = findPreference(PreferenceKey.PORT)!!
+ portPreference.setOnBindEditTextListener { it.inputType = InputType.TYPE_CLASS_NUMBER }
+ sayEndpointPreference = findPreference(PreferenceKey.ENABLE_SAY_ENDPOINT)!!
+ waveEndpointPreference = findPreference(PreferenceKey.ENABLE_WAVE_ENDPOINT)!!
+ ttsEnginePreference = findPreference(PreferenceKey.TTS)!!
+ ttsEnginePreference.setOnPreferenceClickListener {
+ startActivity(Intent(ANDROID_TTS_SETTINGS))
+ true
+ }
+ updateViewAccordingToServiceState(ForegroundService.state)
+ }
- companion object {
- private const val ANDROID_TTS_SETTINGS = "com.android.settings.TTS_SETTINGS"
- }
+ companion object {
+ private const val ANDROID_TTS_SETTINGS = "com.android.settings.TTS_SETTINGS"
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/bartek/service/ForegroundNotificationFactory.kt b/app/src/main/java/io/bartek/service/ForegroundNotificationFactory.kt
index 6107904..9d48461 100644
--- a/app/src/main/java/io/bartek/service/ForegroundNotificationFactory.kt
+++ b/app/src/main/java/io/bartek/service/ForegroundNotificationFactory.kt
@@ -12,58 +12,55 @@ import io.bartek.MainActivity
import io.bartek.R
class ForegroundNotificationFactory(private val context: Context) {
- private val oreo: Boolean
- get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+ private val oreo: Boolean
+ get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
- fun createForegroundNotification(port: Int): Notification {
- createNotificationChannel()
+ fun createForegroundNotification(port: Int): Notification {
+ createNotificationChannel()
+ return buildNotification(port, createPendingIntent())
+ }
- val pendingIntent = createPendingIntent()
+ @Suppress("DEPRECATION")
+ private fun buildNotification(port: Int, pendingIntent: PendingIntent?) =
+ provideNotificationBuilder()
+ .setContentTitle(context.resources.getString(R.string.service_notification_title))
+ .setContentText(context.resources.getString(R.string.service_notification_text, port))
+ .setContentIntent(pendingIntent)
+ .setSmallIcon(R.drawable.ic_foreground_service)
+ .setTicker(context.getString(R.string.service_notification_text, port))
+ .setPriority(Notification.PRIORITY_HIGH) // for under android 26 compatibility
+ .build()
- return buildNotification(port, pendingIntent)
- }
+ @SuppressLint("NewApi")
+ private fun createNotificationChannel() {
+ if (oreo) {
+ val manager =
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ val channel = NotificationChannel(
+ NOTIFICATION_CHANNEL_ID,
+ context.resources.getString(R.string.service_notification_category_name),
+ NotificationManager.IMPORTANCE_HIGH
+ ).let {
+ it.description =
+ context.resources.getString(R.string.service_notification_category_description)
+ it
+ }
+ manager.createNotificationChannel(channel)
+ }
+ }
- @Suppress("DEPRECATION")
- private fun buildNotification(port: Int, pendingIntent: PendingIntent?) =
- provideNotificationBuilder()
- .setContentTitle(context.resources.getString(R.string.service_notification_title))
- .setContentText(context.resources.getString(R.string.service_notification_text, port))
- .setContentIntent(pendingIntent)
- .setSmallIcon(R.drawable.ic_foreground_service)
- .setTicker(context.getString(R.string.service_notification_text))
- .setPriority(Notification.PRIORITY_HIGH) // for under android 26 compatibility
- .build()
+ private fun createPendingIntent() =
+ Intent(context, MainActivity::class.java).let { notificationIntent ->
+ PendingIntent.getActivity(context, 0, notificationIntent, 0)
+ }
- @SuppressLint("NewApi")
- private fun createNotificationChannel() {
- if (oreo) {
- val manager =
- context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- val channel = NotificationChannel(
- NOTIFICATION_CHANNEL_ID,
- context.resources.getString(R.string.service_notification_category_name),
- NotificationManager.IMPORTANCE_HIGH
- ).let {
- it.description =
- context.resources.getString(R.string.service_notification_category_description)
- it
- }
- manager.createNotificationChannel(channel)
- }
- }
+ @Suppress("DEPRECATION")
+ @SuppressLint("NewApi")
+ private fun provideNotificationBuilder() =
+ if (oreo) Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
+ else Notification.Builder(context)
- private fun createPendingIntent() =
- Intent(context, MainActivity::class.java).let { notificationIntent ->
- PendingIntent.getActivity(context, 0, notificationIntent, 0)
- }
-
- @Suppress("DEPRECATION")
- @SuppressLint("NewApi")
- private fun provideNotificationBuilder() =
- if (oreo) Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
- else Notification.Builder(context)
-
- companion object {
- private const val NOTIFICATION_CHANNEL_ID = "TTSService.NOTIFICATION_CHANNEL"
- }
+ companion object {
+ private const val NOTIFICATION_CHANNEL_ID = "TTSService.NOTIFICATION_CHANNEL"
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/bartek/service/ForegroundService.kt b/app/src/main/java/io/bartek/service/ForegroundService.kt
index 52af31e..4bbdd32 100644
--- a/app/src/main/java/io/bartek/service/ForegroundService.kt
+++ b/app/src/main/java/io/bartek/service/ForegroundService.kt
@@ -1,5 +1,6 @@
package io.bartek.service
+import android.annotation.SuppressLint
import android.app.Service
import android.content.Context
import android.content.Intent
@@ -7,78 +8,80 @@ import android.content.SharedPreferences
import android.os.PowerManager
import androidx.preference.PreferenceManager
import io.bartek.preference.PreferenceKey
-import io.bartek.web.TTSServer
+import io.bartek.web.WebServer
class ForegroundService : Service() {
- 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() = preferences.getInt(PreferenceKey.PORT, 8080)
- private val notificationFactory = ForegroundNotificationFactory(this)
+ private lateinit var preferences: SharedPreferences
+ private var wakeLock: PowerManager.WakeLock? = null
+ private var isServiceStarted = false
+ private var webServer: WebServer? = null
+ private val port: Int
+ get() = preferences.getInt(PreferenceKey.PORT, 8080)
+ private val notificationFactory = ForegroundNotificationFactory(this)
- override fun onCreate() {
- super.onCreate()
- preferences = PreferenceManager.getDefaultSharedPreferences(this)
- startForeground(1, notificationFactory.createForegroundNotification(port))
- }
- override fun onBind(intent: Intent) = null
+ override fun onCreate() {
+ super.onCreate()
+ preferences = PreferenceManager.getDefaultSharedPreferences(this)
+ startForeground(1, notificationFactory.createForegroundNotification(port))
+ }
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- intent?.let {
- when(it.action) {
- START -> startService()
- STOP -> stopService()
+ override fun onBind(intent: Intent) = null
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ intent?.let {
+ when (it.action) {
+ START -> startService()
+ STOP -> stopService()
+ }
+ }
+
+ return START_STICKY
+ }
+
+ override fun onDestroy() {
+ webServer = null
+ }
+
+ @SuppressLint("WakelockTimeout")
+ private fun startService() {
+ if (isServiceStarted) return
+ isServiceStarted = true
+ wakeLock =
+ (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
+ newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG).apply {
+ acquire()
}
- }
+ }
+ webServer = WebServer(port, this)
+ state = ServiceState.RUNNING
+ }
- return START_STICKY
- }
+ private fun stopService() {
+ webServer?.stop()
+ webServer = null
+ wakeLock?.let {
+ if (it.isHeld) {
+ it.release()
+ }
- override fun onDestroy() {
- ttsServer = null
- }
+ stopForeground(true)
+ stopSelf()
+ }
+ state = ServiceState.STOPPED
+ }
- private fun startService() {
- if(isServiceStarted) return
- isServiceStarted = true
- wakeLock =
- (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
- newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG).apply {
- acquire()
- }
- }
- ttsServer = TTSServer(port, this)
- state = ServiceState.RUNNING
- }
+ companion object {
+ // Disclaimer: I don't know the better way
+ // to check whether the service is already running
+ // than to place it as a static field
+ var state = ServiceState.STOPPED
- private fun stopService() {
- ttsServer?.stop()
- ttsServer = null
- wakeLock?.let {
- if(it.isHeld) {
- it.release()
- }
-
- stopForeground(true)
- stopSelf()
- }
- state = ServiceState.STOPPED
- }
-
- companion object {
- // Disclaimer: I don't know the better way
- // to check whether the service is already running
- // than to place it as a static field
- var state = ServiceState.STOPPED
-
- private const val WAKELOCK_TAG = "ForegroundService::lock"
- const val CHANGE_STATE = "io.bartek.service.CHANGE_STATE"
- const val STATE = "STATE"
- const val START = "START"
- const val STOP = "STOP"
- }
+ private const val WAKELOCK_TAG = "ForegroundService::lock"
+ const val CHANGE_STATE = "io.bartek.service.CHANGE_STATE"
+ const val STATE = "STATE"
+ const val START = "START"
+ const val STOP = "STOP"
+ }
}
diff --git a/app/src/main/java/io/bartek/service/ServiceState.kt b/app/src/main/java/io/bartek/service/ServiceState.kt
index ad1f38d..9a69e33 100644
--- a/app/src/main/java/io/bartek/service/ServiceState.kt
+++ b/app/src/main/java/io/bartek/service/ServiceState.kt
@@ -1,6 +1,6 @@
package io.bartek.service
enum class ServiceState {
- RUNNING,
- STOPPED
+ RUNNING,
+ STOPPED
}
\ No newline at end of file
diff --git a/app/src/main/java/io/bartek/tts/TTS.kt b/app/src/main/java/io/bartek/tts/TTS.kt
index f21fb3b..175ccc2 100644
--- a/app/src/main/java/io/bartek/tts/TTS.kt
+++ b/app/src/main/java/io/bartek/tts/TTS.kt
@@ -7,80 +7,81 @@ import io.bartek.exception.TTSException
import java.io.BufferedInputStream
import java.io.FileInputStream
import java.io.InputStream
-import java.lang.RuntimeException
import java.util.*
data class SpeechData(val stream: InputStream, val size: Long)
class TTS(context: Context, initListener: TextToSpeech.OnInitListener) {
- private val tts = TextToSpeech(context, initListener)
+ private val tts = TextToSpeech(context, initListener)
- fun fetchTTSStream(text: String, language: Locale): SpeechData {
- val file = createTempFile("tmp_tts_server", ".wav")
+ fun fetchTTSStream(text: String, language: Locale): SpeechData {
+ val file = createTempFile("tmp_tts_server", ".wav")
- val uuid = UUID.randomUUID().toString()
- val lock = Lock()
- tts.setOnUtteranceProgressListener(TTSProcessListener(uuid, lock))
+ val uuid = UUID.randomUUID().toString()
+ val lock = Lock()
+ tts.setOnUtteranceProgressListener(TTSProcessListener(uuid, lock))
- synchronized(lock) {
- tts.language = language
- tts.synthesizeToFile(text, null, file, uuid)
- lock.wait()
- }
+ synchronized(lock) {
+ tts.language = language
+ tts.synthesizeToFile(text, null, file, uuid)
+ lock.wait()
+ }
- if (!lock.success) {
- throw TTSException()
- }
+ if (!lock.success) {
+ throw TTSException()
+ }
- val stream = BufferedInputStream(FileInputStream(file))
- val length = file.length()
+ val stream = BufferedInputStream(FileInputStream(file))
+ val length = file.length()
- file.delete()
+ file.delete()
- return SpeechData(stream, length)
- }
+ return SpeechData(stream, length)
+ }
- fun performTTS(text: String, language: Locale) {
- val uuid = UUID.randomUUID().toString()
- val lock = Lock()
- tts.setOnUtteranceProgressListener(TTSProcessListener(uuid, lock))
+ fun performTTS(text: String, language: Locale) {
+ val uuid = UUID.randomUUID().toString()
+ val lock = Lock()
+ tts.setOnUtteranceProgressListener(TTSProcessListener(uuid, lock))
- synchronized(lock) {
- tts.language = language
- tts.speak(text, TextToSpeech.QUEUE_ADD, null, uuid)
- lock.wait()
- }
+ synchronized(lock) {
+ tts.language = language
+ tts.speak(text, TextToSpeech.QUEUE_ADD, null, uuid)
+ lock.wait()
+ }
- if(!lock.success) {
- throw TTSException()
- }
- }
+ if (!lock.success) {
+ throw TTSException()
+ }
+ }
}
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+// TODO: Investigate the Kotlin way to achieve the same
private data class Lock(var success: Boolean = false) : Object()
private class TTSProcessListener(
- private val uuid: String,
- private val lock: Lock
+ private val uuid: String,
+ private val lock: Lock
) : UtteranceProgressListener() {
- override fun onDone(utteranceId: String?) {
- if (utteranceId == uuid) {
- synchronized(lock) {
- lock.success = true
- lock.notifyAll()
- }
- }
- }
- override fun onError(utteranceId: String?) {
- if (utteranceId == uuid) {
- synchronized(lock) {
- lock.success = false
- lock.notifyAll()
- }
- }
- }
+ override fun onDone(utteranceId: String?) {
+ if (utteranceId == uuid) {
+ synchronized(lock) {
+ lock.success = true
+ lock.notifyAll()
+ }
+ }
+ }
- override fun onStart(utteranceId: String?) {}
+ override fun onError(utteranceId: String?) {
+ if (utteranceId == uuid) {
+ synchronized(lock) {
+ lock.success = false
+ lock.notifyAll()
+ }
+ }
+ }
+ override fun onStart(utteranceId: String?) {}
}
diff --git a/app/src/main/java/io/bartek/web/WebServer.kt b/app/src/main/java/io/bartek/web/WebServer.kt
index bcfe868..bcd9276 100644
--- a/app/src/main/java/io/bartek/web/WebServer.kt
+++ b/app/src/main/java/io/bartek/web/WebServer.kt
@@ -17,100 +17,100 @@ import java.util.*
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)
+class WebServer(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 {
- session?.let {
- return when (it.uri) {
- "/wave" -> wave(it)
- "/say" -> say(it)
- else -> throw ResponseException(NOT_FOUND, "")
- }
+ override fun serve(session: IHTTPSession?): Response {
+ try {
+ session?.let {
+ return when (it.uri) {
+ "/wave" -> wave(it)
+ "/say" -> say(it)
+ else -> throw ResponseException(NOT_FOUND, "")
}
+ }
- throw ResponseException(BAD_REQUEST, "")
- } catch (e: ResponseException) {
- throw e
- } catch (e: Exception) {
- throw ResponseException(INTERNAL_ERROR, e.toString(), e)
- }
- }
+ throw ResponseException(BAD_REQUEST, "")
+ } catch (e: ResponseException) {
+ throw e
+ } catch (e: Exception) {
+ throw ResponseException(INTERNAL_ERROR, e.toString(), e)
+ }
+ }
- private fun wave(session: IHTTPSession): Response {
- if (!preferences.getBoolean(PreferenceKey.ENABLE_WAVE_ENDPOINT, true)) {
- throw ResponseException(NOT_FOUND, "")
- }
+ private fun wave(session: IHTTPSession): Response {
+ if (!preferences.getBoolean(PreferenceKey.ENABLE_WAVE_ENDPOINT, true)) {
+ throw ResponseException(NOT_FOUND, "")
+ }
- if (session.method != Method.POST) {
- throw ResponseException(METHOD_NOT_ALLOWED, "")
- }
+ if (session.method != Method.POST) {
+ throw ResponseException(METHOD_NOT_ALLOWED, "")
+ }
- if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
- throw ResponseException(BAD_REQUEST, "")
- }
+ if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
+ throw ResponseException(BAD_REQUEST, "")
+ }
- val (text, language) = getRequestData(session)
- val (stream, size) = tts.fetchTTSStream(text, language)
- return newFixedLengthResponse(OK, MIME_WAVE, stream, size)
- }
+ val (text, language) = getRequestData(session)
+ val (stream, size) = tts.fetchTTSStream(text, language)
+ return newFixedLengthResponse(OK, MIME_WAVE, stream, size)
+ }
- private fun say(session: IHTTPSession): Response {
- if (!preferences.getBoolean(PreferenceKey.ENABLE_SAY_ENDPOINT, true)) {
- throw ResponseException(NOT_FOUND, "")
- }
+ private fun say(session: IHTTPSession): Response {
+ if (!preferences.getBoolean(PreferenceKey.ENABLE_SAY_ENDPOINT, true)) {
+ throw ResponseException(NOT_FOUND, "")
+ }
- if (session.method != Method.POST) {
- throw ResponseException(METHOD_NOT_ALLOWED, "")
- }
+ if (session.method != Method.POST) {
+ throw ResponseException(METHOD_NOT_ALLOWED, "")
+ }
- if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
- throw ResponseException(BAD_REQUEST, "")
- }
+ if (session.headers[CONTENT_TYPE]?.let { it != MIME_JSON } != false) {
+ throw ResponseException(BAD_REQUEST, "")
+ }
- val (text, language) = getRequestData(session)
- tts.performTTS(text, language)
- return newFixedLengthResponse(OK, MIME_PLAINTEXT, "")
- }
+ val (text, language) = getRequestData(session)
+ tts.performTTS(text, language)
+ return newFixedLengthResponse(OK, MIME_PLAINTEXT, "")
+ }
- private fun getRequestData(session: IHTTPSession): TTSRequestData {
- val map = mutableMapOf()
- session.parseBody(map)
- val json = JSONObject(map["postData"] ?: "{}")
- val language = json.optString("language")
- .takeIf { it.isNotBlank() }
- ?.let { Locale(it) }
- ?: Locale.US
- val text = json.optString("text") ?: throw ResponseException(BAD_REQUEST, "")
- return TTSRequestData(text, language)
- }
+ private fun getRequestData(session: IHTTPSession): TTSRequestData {
+ val map = mutableMapOf()
+ session.parseBody(map)
+ val json = JSONObject(map["postData"] ?: "{}")
+ val language = json.optString("language")
+ .takeIf { it.isNotBlank() }
+ ?.let { Locale(it) }
+ ?: Locale.US
+ val text = json.optString("text") ?: throw ResponseException(BAD_REQUEST, "")
+ return TTSRequestData(text, language)
+ }
- override fun onInit(status: Int) = start()
+ override fun onInit(status: Int) = start()
- override fun start() {
- super.start()
- LocalBroadcastManager
- .getInstance(context)
- .sendBroadcast(Intent(ForegroundService.CHANGE_STATE).also {
- it.putExtra(ForegroundService.STATE, ServiceState.RUNNING.name)
- })
- }
+ override fun start() {
+ super.start()
+ LocalBroadcastManager
+ .getInstance(context)
+ .sendBroadcast(Intent(ForegroundService.CHANGE_STATE).also {
+ it.putExtra(ForegroundService.STATE, ServiceState.RUNNING.name)
+ })
+ }
- override fun stop() {
- super.stop()
- LocalBroadcastManager
- .getInstance(context)
- .sendBroadcast(Intent(ForegroundService.CHANGE_STATE).also {
- it.putExtra(ForegroundService.STATE, ServiceState.STOPPED.name)
- })
- }
+ override fun stop() {
+ super.stop()
+ LocalBroadcastManager
+ .getInstance(context)
+ .sendBroadcast(Intent(ForegroundService.CHANGE_STATE).also {
+ it.putExtra(ForegroundService.STATE, ServiceState.STOPPED.name)
+ })
+ }
- companion object {
- private const val MIME_JSON = "application/json"
- private const val MIME_WAVE = "audio/x-wav"
- private const val CONTENT_TYPE = "content-type"
- }
+ companion object {
+ private const val MIME_JSON = "application/json"
+ private const val MIME_WAVE = "audio/x-wav"
+ private const val CONTENT_TYPE = "content-type"
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 21e289c..a212c2b 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -16,7 +16,7 @@
android:textAlignment="center" />