From b0b1ca994fac2668184b5ad306d77dd5d47e2557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Sun, 10 May 2020 17:21:28 +0200 Subject: [PATCH] Force 3-characters wide spacing and refactor & reformat code accordingly to the new rules --- .idea/codeStyles/Project.xml | 10 ++ .../java/io/bartek/ExampleInstrumentedTest.kt | 18 +- app/src/main/java/io/bartek/MainActivity.kt | 142 ++++++++-------- .../main/java/io/bartek/help/HelpActivity.kt | 34 ++-- .../preference/IntEditTextPreference.kt | 39 ++--- .../java/io/bartek/preference/Preference.kt | 8 +- .../bartek/preference/PreferencesActivity.kt | 18 +- .../bartek/preference/PreferencesFragment.kt | 90 +++++----- .../service/ForegroundNotificationFactory.kt | 93 +++++----- .../io/bartek/service/ForegroundService.kt | 129 +++++++------- .../java/io/bartek/service/ServiceState.kt | 4 +- app/src/main/java/io/bartek/tts/TTS.kt | 105 ++++++------ app/src/main/java/io/bartek/web/WebServer.kt | 160 +++++++++--------- app/src/main/res/layout/activity_main.xml | 2 +- 14 files changed, 427 insertions(+), 425 deletions(-) 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 @@ + + + + \ 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" />