Enable checking silence scheduler for /say and /sonos endpoints

This commit is contained in:
2020-06-30 21:26:04 +02:00
parent 2fd0da2362
commit 42458d00c2
15 changed files with 191 additions and 15 deletions

View File

@@ -30,7 +30,7 @@
android:parentActivityName="com.bartlomiejpluta.ttsserver.ui.main.MainActivity" /> android:parentActivityName="com.bartlomiejpluta.ttsserver.ui.main.MainActivity" />
<activity <activity
android:name="com.bartlomiejpluta.ttsserver.ui.preference.PreferencesActivity" android:name="com.bartlomiejpluta.ttsserver.ui.preference.component.PreferencesActivity"
android:label="@string/title_activity_preferences" android:label="@string/title_activity_preferences"
android:parentActivityName="com.bartlomiejpluta.ttsserver.ui.main.MainActivity" /> android:parentActivityName="com.bartlomiejpluta.ttsserver.ui.main.MainActivity" />

View File

@@ -7,7 +7,7 @@ import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO
import com.bartlomiejpluta.ttsserver.core.web.endpoint.Endpoint import com.bartlomiejpluta.ttsserver.core.web.endpoint.Endpoint
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.service.state.ServiceState import com.bartlomiejpluta.ttsserver.service.state.ServiceState
import com.bartlomiejpluta.ttsserver.ui.preference.PreferenceKey import com.bartlomiejpluta.ttsserver.ui.preference.key.PreferenceKey
import com.vmichalak.sonoscontroller.Snapshot import com.vmichalak.sonoscontroller.Snapshot
import com.vmichalak.sonoscontroller.SonosDevice import com.vmichalak.sonoscontroller.SonosDevice
import com.vmichalak.sonoscontroller.SonosDiscovery import com.vmichalak.sonoscontroller.SonosDiscovery

View File

@@ -14,7 +14,7 @@ import com.bartlomiejpluta.ttsserver.core.tts.model.TTSStream
import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatus import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatus
import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatusHolder import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatusHolder
import com.bartlomiejpluta.ttsserver.core.util.AudioConverter import com.bartlomiejpluta.ttsserver.core.util.AudioConverter
import com.bartlomiejpluta.ttsserver.ui.preference.PreferenceKey import com.bartlomiejpluta.ttsserver.ui.preference.key.PreferenceKey
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream

View File

@@ -4,7 +4,7 @@ import android.content.Context
import android.content.Context.WIFI_SERVICE import android.content.Context.WIFI_SERVICE
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
import com.bartlomiejpluta.ttsserver.ui.preference.PreferenceKey import com.bartlomiejpluta.ttsserver.ui.preference.key.PreferenceKey
import java.net.InetAddress import java.net.InetAddress

View File

@@ -18,13 +18,15 @@ import com.bartlomiejpluta.ttsserver.core.web.exception.WebException
import com.bartlomiejpluta.ttsserver.core.web.mime.MimeType import com.bartlomiejpluta.ttsserver.core.web.mime.MimeType
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.service.state.ServiceState import com.bartlomiejpluta.ttsserver.service.state.ServiceState
import com.bartlomiejpluta.ttsserver.ui.preference.PreferenceKey import com.bartlomiejpluta.ttsserver.ui.preference.key.PreferenceKey
import com.bartlomiejpluta.ttsserver.ui.preference.model.TimeRange
import fi.iki.elonen.NanoHTTPD import fi.iki.elonen.NanoHTTPD
import fi.iki.elonen.NanoHTTPD.Response.Status.* import fi.iki.elonen.NanoHTTPD.Response.Status.*
import org.json.JSONObject import org.json.JSONObject
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.util.*
class WebServer( class WebServer(
@@ -34,6 +36,20 @@ class WebServer(
private val tts: TTSEngine, private val tts: TTSEngine,
private val sonos: SonosQueue private val sonos: SonosQueue
) : NanoHTTPD(port) { ) : NanoHTTPD(port) {
private val speakersSilenceSchedulerEnabled: Boolean
get() = preferences.getBoolean(PreferenceKey.ENABLE_SPEAKERS_SILENCE_SCHEDULER, false)
private val sonosSilenceSchedulerEnabled: Boolean
get() = preferences.getBoolean(PreferenceKey.ENABLE_SONOS_SILENCE_SCHEDULER, false)
private val speakersSilenceSchedule: TimeRange
get() = preferences.getString(PreferenceKey.SPEAKERS_SILENCE_SCHEDULE, "")!!
.let { TimeRange.parse(it) }
private val sonosSilenceSchedule: TimeRange
get() = preferences.getString(PreferenceKey.SONOS_SILENCE_SCHEDULE, "")!!
.let { TimeRange.parse(it) }
override fun serve(session: IHTTPSession?): Response { override fun serve(session: IHTTPSession?): Response {
try { try {
assertThatTTSIsReady() assertThatTTSIsReady()
@@ -97,6 +113,10 @@ class WebServer(
throw WebException(BAD_REQUEST, "Only JSON data is accepted") throw WebException(BAD_REQUEST, "Only JSON data is accepted")
} }
if (speakersSilenceSchedulerEnabled && speakersSilenceSchedule.inRange(Calendar.getInstance())) {
return newFixedLengthResponse(NO_CONTENT, MIME_JSON, "")
}
val dto = extractBody(session) { BaseDTO(it) } val dto = extractBody(session) { BaseDTO(it) }
tts.performTTS(dto.text, dto.language) tts.performTTS(dto.text, dto.language)
@@ -142,6 +162,10 @@ class WebServer(
throw WebException(BAD_REQUEST, "Only JSON data is accepted") throw WebException(BAD_REQUEST, "Only JSON data is accepted")
} }
if (sonosSilenceSchedulerEnabled && sonosSilenceSchedule.inRange(Calendar.getInstance())) {
return newFixedLengthResponse(NO_CONTENT, MIME_JSON, "")
}
val dto = extractBody(session) { SonosDTO(it) } val dto = extractBody(session) { SonosDTO(it) }
sonos.push(dto) sonos.push(dto)

View File

@@ -4,7 +4,7 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import com.bartlomiejpluta.ttsserver.core.sonos.queue.SonosQueue import com.bartlomiejpluta.ttsserver.core.sonos.queue.SonosQueue
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.ui.preference.PreferenceKey import com.bartlomiejpluta.ttsserver.ui.preference.key.PreferenceKey
class WebServerFactory( class WebServerFactory(
private val preferences: SharedPreferences, private val preferences: SharedPreferences,

View File

@@ -3,7 +3,7 @@ package com.bartlomiejpluta.ttsserver.di.module
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.ui.help.HelpActivity import com.bartlomiejpluta.ttsserver.ui.help.HelpActivity
import com.bartlomiejpluta.ttsserver.ui.main.MainActivity import com.bartlomiejpluta.ttsserver.ui.main.MainActivity
import com.bartlomiejpluta.ttsserver.ui.preference.PreferencesActivity import com.bartlomiejpluta.ttsserver.ui.preference.component.PreferencesActivity
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector

View File

@@ -14,7 +14,7 @@ import com.bartlomiejpluta.ttsserver.core.util.NetworkUtil
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.service.state.ServiceState import com.bartlomiejpluta.ttsserver.service.state.ServiceState
import com.bartlomiejpluta.ttsserver.ui.help.HelpActivity import com.bartlomiejpluta.ttsserver.ui.help.HelpActivity
import com.bartlomiejpluta.ttsserver.ui.preference.PreferencesActivity import com.bartlomiejpluta.ttsserver.ui.preference.component.PreferencesActivity
import dagger.android.support.DaggerAppCompatActivity import dagger.android.support.DaggerAppCompatActivity
import javax.inject.Inject import javax.inject.Inject

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.ttsserver.ui.preference package com.bartlomiejpluta.ttsserver.ui.preference.component
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -11,7 +11,9 @@ class PreferencesActivity : AppCompatActivity() {
setContentView(R.layout.activity_preferences) setContentView(R.layout.activity_preferences)
supportFragmentManager supportFragmentManager
.beginTransaction() .beginTransaction()
.replace(R.id.preferences, PreferencesFragment()) .replace(R.id.preferences,
PreferencesFragment()
)
.commit() .commit()
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
} }

View File

@@ -1,5 +1,6 @@
package com.bartlomiejpluta.ttsserver.ui.preference package com.bartlomiejpluta.ttsserver.ui.preference.component
import android.app.TimePickerDialog
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@@ -15,6 +16,9 @@ import com.bartlomiejpluta.R
import com.bartlomiejpluta.ttsserver.core.web.mime.MimeType import com.bartlomiejpluta.ttsserver.core.web.mime.MimeType
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.service.state.ServiceState import com.bartlomiejpluta.ttsserver.service.state.ServiceState
import com.bartlomiejpluta.ttsserver.ui.preference.model.TimeRange
import com.bartlomiejpluta.ttsserver.ui.preference.key.PreferenceKey
import com.bartlomiejpluta.ttsserver.ui.preference.custom.IntEditTextPreference
class PreferencesFragment : PreferenceFragmentCompat() { class PreferencesFragment : PreferenceFragmentCompat() {
@@ -25,6 +29,8 @@ class PreferencesFragment : PreferenceFragmentCompat() {
private lateinit var httpDebugPreference: SwitchPreference private lateinit var httpDebugPreference: SwitchPreference
private lateinit var enableGongPreference: SwitchPreference private lateinit var enableGongPreference: SwitchPreference
private lateinit var ttsEnginePreference: Preference private lateinit var ttsEnginePreference: Preference
private lateinit var enableSonosSilenceScheduler: SwitchPreference
private lateinit var enableSpeakersSilenceScheduler: SwitchPreference
private lateinit var clearSonosCachePreference: Preference private lateinit var clearSonosCachePreference: Preference
private val receiver = object : BroadcastReceiver() { private val receiver = object : BroadcastReceiver() {
@@ -75,6 +81,82 @@ class PreferencesFragment : PreferenceFragmentCompat() {
startActivity(Intent(ANDROID_TTS_SETTINGS)) startActivity(Intent(ANDROID_TTS_SETTINGS))
true true
} }
enableSpeakersSilenceScheduler = findPreference(PreferenceKey.ENABLE_SPEAKERS_SILENCE_SCHEDULER)!!
enableSpeakersSilenceScheduler.setOnPreferenceClickListener { preference ->
if(!enableSpeakersSilenceScheduler.isChecked) {
return@setOnPreferenceClickListener true
}
enableSpeakersSilenceScheduler.isChecked = false
val schedule = preference
.sharedPreferences
.getString(
PreferenceKey.SPEAKERS_SILENCE_SCHEDULE,
DEFAULT_SCHEDULE
)!!
.let {
TimeRange.parse(
it
)
}
TimePickerDialog(context, { _, newBeginHour, newBeginMinute ->
TimePickerDialog(context, { _, newEndHour, newEndMinute ->
preference.sharedPreferences.edit()?.let { editor ->
val newSchedule =
TimeRange(
newBeginHour,
newBeginMinute,
newEndHour,
newEndMinute
)
editor.putString(PreferenceKey.SPEAKERS_SILENCE_SCHEDULE, newSchedule.toString())
editor.apply()
enableSpeakersSilenceScheduler.isChecked = true
}
}, schedule.endHour, schedule.endMinute, true).show()
}, schedule.beginHour, schedule.beginMinute, true).show()
true
}
enableSonosSilenceScheduler = findPreference(PreferenceKey.ENABLE_SONOS_SILENCE_SCHEDULER)!!
enableSonosSilenceScheduler.setOnPreferenceClickListener { preference ->
if(!enableSonosSilenceScheduler.isChecked) {
return@setOnPreferenceClickListener true
}
enableSonosSilenceScheduler.isChecked = false
val schedule = preference
.sharedPreferences
.getString(
PreferenceKey.SONOS_SILENCE_SCHEDULE,
DEFAULT_SCHEDULE
)!!
.let {
TimeRange.parse(
it
)
}
TimePickerDialog(context, { _, newBeginHour, newBeginMinute ->
TimePickerDialog(context, { _, newEndHour, newEndMinute ->
preference.sharedPreferences.edit()?.let { editor ->
val newSchedule =
TimeRange(
newBeginHour,
newBeginMinute,
newEndHour,
newEndMinute
)
editor.putString(PreferenceKey.SONOS_SILENCE_SCHEDULE, newSchedule.toString())
editor.apply()
enableSonosSilenceScheduler.isChecked = true
}
}, schedule.endHour, schedule.endMinute, true).show()
}, schedule.beginHour, schedule.beginMinute, true).show()
true
}
clearSonosCachePreference = findPreference(PreferenceKey.INVALIDATE_SONOS_CACHE)!! clearSonosCachePreference = findPreference(PreferenceKey.INVALIDATE_SONOS_CACHE)!!
clearSonosCachePreference.setOnPreferenceClickListener { clearSonosCachePreference.setOnPreferenceClickListener {
context?.cacheDir?.listFiles()?.forEach { it.delete() } context?.cacheDir?.listFiles()?.forEach { it.delete() }
@@ -94,7 +176,9 @@ class PreferencesFragment : PreferenceFragmentCompat() {
.apply { type = MimeType.WAV.mimeType } .apply { type = MimeType.WAV.mimeType }
.let { Intent.createChooser(it, getString(R.string.preference_gong_picker_prompt)) } .let { Intent.createChooser(it, getString(R.string.preference_gong_picker_prompt)) }
startActivityForResult(intent, PICKFILE_RESULT_CODE) startActivityForResult(intent,
PICKFILE_RESULT_CODE
)
} }
} }
@@ -118,5 +202,6 @@ class PreferencesFragment : PreferenceFragmentCompat() {
companion object { companion object {
private const val ANDROID_TTS_SETTINGS = "com.android.settings.TTS_SETTINGS" private const val ANDROID_TTS_SETTINGS = "com.android.settings.TTS_SETTINGS"
private const val PICKFILE_RESULT_CODE = 1 private const val PICKFILE_RESULT_CODE = 1
private val DEFAULT_SCHEDULE = "22:00-07:00"
} }
} }

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.ttsserver.ui.preference package com.bartlomiejpluta.ttsserver.ui.preference.custom
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.ttsserver.ui.preference package com.bartlomiejpluta.ttsserver.ui.preference.key
object PreferenceKey { object PreferenceKey {
@@ -10,5 +10,9 @@ object PreferenceKey {
const val ENABLE_GONG = "preference_enable_gong" const val ENABLE_GONG = "preference_enable_gong"
const val GONG = "preference_gong" const val GONG = "preference_gong"
const val TTS = "preference_tts" const val TTS = "preference_tts"
const val ENABLE_SPEAKERS_SILENCE_SCHEDULER = "preference_enable_speakers_silence_scheduler"
const val SPEAKERS_SILENCE_SCHEDULE = "preference_speakers_silence_schedule"
const val ENABLE_SONOS_SILENCE_SCHEDULER = "preference_enable_sonos_silence_scheduler"
const val SONOS_SILENCE_SCHEDULE = "preference_sonos_silence_schedule"
const val INVALIDATE_SONOS_CACHE = "preference_invalidate_sonos_cache" const val INVALIDATE_SONOS_CACHE = "preference_invalidate_sonos_cache"
} }

View File

@@ -0,0 +1,42 @@
package com.bartlomiejpluta.ttsserver.ui.preference.model
import java.util.*
data class TimeRange(val beginHour: Int, val beginMinute: Int, val endHour: Int, val endMinute: Int) {
private val begin: Int
get() = beginHour * 60 + beginMinute
private val end: Int
get() = endHour * 60 + endMinute
fun inRange(calendar: Calendar): Boolean {
val current = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE)
return when {
begin <= end -> current in begin..end
else -> !(current in end..begin)
}
}
override fun toString() = "$beginHour:$beginMinute-$endHour:$endMinute"
companion object {
fun parse(range: String): TimeRange {
val (beginTime, endTime) = range.splitAndMap("-") { it }
val (beginHour, beginMinute) = beginTime.splitAndMap(":") { it.toInt() }
val (endHour, endMinute) = endTime.splitAndMap(":") { it.toInt() }
return TimeRange(
beginHour,
beginMinute,
endHour,
endMinute
)
}
private fun <T> String.splitAndMap(delimiter: String, mapper: (String) -> T): List<T> {
return this.split(delimiter)
.map { mapper(it) }
.takeIf { it.size == 2 }
?: throw IllegalArgumentException("Expected format: HH:mm-HH:mm")
}
}
}

View File

@@ -30,11 +30,16 @@
<string name="preference_enable_gong_title">Enable gong</string> <string name="preference_enable_gong_title">Enable gong</string>
<string name="preference_tts_summary">Go to platform\'s TTS engine settings and adjust its parameters</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_tts_title">TTS engine settings</string>
<string name="preference_enable_speakers_silence_scheduler_summary">Determine a time range when /say endpoint should be disabled</string>
<string name="preference_enable_speakers_silence_scheduler_title">Enable silence scheduler</string>
<string name="preference_enable_sonos_silence_scheduler_summary">Determine a time range when /sonos endpoint should be disabled</string>
<string name="preference_enable_sonos_silence_scheduler_title">Enable silence scheduler</string>
<string name="preference_invalidate_sonos_cache_summary">Clear application cache directory from already generated Sonos TTS data for given sentences. You typically want to do it when you change the TTS engine\'s voice settings.</string> <string name="preference_invalidate_sonos_cache_summary">Clear application cache directory from already generated Sonos TTS data for given sentences. You typically want to do it when you change the TTS engine\'s voice settings.</string>
<string name="preference_invalidate_sonos_cache_title">Invalidate Sonos cache</string> <string name="preference_invalidate_sonos_cache_title">Invalidate Sonos cache</string>
<string name="preference_category_server">Server</string> <string name="preference_category_server">Server</string>
<string name="preference_category_features">Features</string> <string name="preference_category_features">Features</string>
<string name="preference_category_tts">TTS engine</string> <string name="preference_category_tts">TTS engine</string>
<string name="preference_category_speakers">Speakers</string>
<string name="preference_category_sonos">Sonos</string> <string name="preference_category_sonos">Sonos</string>
<string name="preference_invalidate_sonos_cache_toast">Sonos cache has been invalidated</string> <string name="preference_invalidate_sonos_cache_toast">Sonos cache has been invalidated</string>

View File

@@ -2,7 +2,7 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory android:title="@string/preference_category_server" app:iconSpaceReserved="false"> <PreferenceCategory android:title="@string/preference_category_server" app:iconSpaceReserved="false">
<com.bartlomiejpluta.ttsserver.ui.preference.IntEditTextPreference <com.bartlomiejpluta.ttsserver.ui.preference.custom.IntEditTextPreference
android:defaultValue="8080" android:defaultValue="8080"
android:inputType="number" android:inputType="number"
android:key="preference_port" android:key="preference_port"
@@ -54,7 +54,21 @@
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/preference_category_speakers" app:iconSpaceReserved="false">
<SwitchPreference
android:key="preference_enable_speakers_silence_scheduler"
android:summary="@string/preference_enable_speakers_silence_scheduler_summary"
app:title="@string/preference_enable_speakers_silence_scheduler_title"
app:iconSpaceReserved="false" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/preference_category_sonos" app:iconSpaceReserved="false"> <PreferenceCategory android:title="@string/preference_category_sonos" app:iconSpaceReserved="false">
<SwitchPreference
android:key="preference_enable_sonos_silence_scheduler"
android:summary="@string/preference_enable_sonos_silence_scheduler_summary"
app:title="@string/preference_enable_sonos_silence_scheduler_title"
app:iconSpaceReserved="false" />
<Preference <Preference
android:key="preference_invalidate_sonos_cache" android:key="preference_invalidate_sonos_cache"
android:summary="@string/preference_invalidate_sonos_cache_summary" android:summary="@string/preference_invalidate_sonos_cache_summary"