Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions app/src/main/kotlin/org/stypox/dicio/eval/SkillHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import org.dicio.skill.skill.Skill
import org.dicio.skill.skill.SkillInfo
import org.stypox.dicio.di.LocaleManager
import org.stypox.dicio.di.SkillContextImpl
Expand Down Expand Up @@ -73,7 +72,7 @@ class SkillHandler @Inject constructor(

private val _skillRanker = MutableStateFlow(
// an initial dummy value, will be overwritten directly by the launched job
SkillRanker(listOf(), buildSkillFromInfo(fallbackSkillInfoList[0]))
SkillRanker(listOf(), fallbackSkillInfoList[0].build(skillContext)!!)
)
val skillRanker: StateFlow<SkillRanker> = _skillRanker

Expand All @@ -87,21 +86,17 @@ class SkillHandler @Inject constructor(

val newEnabledSkillsInfo = allSkillInfoList
.filter { enabledSkills.getOrDefault(it.id, true) }
.filter { it.isAvailable(skillContext) }
.mapNotNull { info -> info.build(skillContext)?.let { skill -> Pair(info, skill) } }

_enabledSkillsInfo.value = newEnabledSkillsInfo
_enabledSkillsInfo.value = newEnabledSkillsInfo.map { (info, _skill) -> info }
_skillRanker.value = SkillRanker(
newEnabledSkillsInfo.map(::buildSkillFromInfo),
buildSkillFromInfo(fallbackSkillInfoList[0]),
newEnabledSkillsInfo.map { (_info, skill) -> skill },
fallbackSkillInfoList[0].build(skillContext)!!,
)
}
}
}

private fun buildSkillFromInfo(skillInfo: SkillInfo): Skill<*> {
return skillInfo.build(skillContext)
}

companion object {
fun newForPreviews(context: Context): SkillHandler {
return SkillHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,12 @@ fun SkillSettingsScreen(
}
}
items(skills) { skill ->
// Note: calling build() here is slightly wasteful as it constructs a skill object
// just to check availability, but it ensures correct results regardless of whether
// the skill is enabled or disabled by the user.
SkillSettingsItem(
skill = skill,
isAvailable = skill.isAvailable(viewModel.skillContext),
isAvailable = skill.build(viewModel.skillContext) != null,
enabled = enabledSkills.getOrDefault(skill.id, true),
setEnabled = { enabled -> viewModel.setSkillEnabled(skill.id, enabled) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.dicio.skill.context.SkillContext
import org.dicio.skill.skill.SkillInfo
import org.stypox.dicio.di.SkillContextInternal
import org.stypox.dicio.settings.datastore.UserSettings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,10 @@ object CalculatorInfo : SkillInfo("calculator") {
override fun icon() =
rememberVectorPainter(Icons.Default.Calculate)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Calculator[ctx.sentencesLanguage] != null &&
Sentences.CalculatorOperators[ctx.sentencesLanguage] != null &&
ctx.parserFormatter != null
}

override fun build(ctx: SkillContext): Skill<*> {
return CalculatorSkill(CalculatorInfo, Sentences.Calculator[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val sentences = Sentences.Calculator[ctx.sentencesLanguage] ?: return null
val operators = Sentences.CalculatorOperators[ctx.sentencesLanguage] ?: return null
if (ctx.parserFormatter == null) return null
return CalculatorSkill(CalculatorInfo, sentences, operators)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import org.stypox.dicio.sentences.Sentences.CalculatorOperators
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols

class CalculatorSkill(correspondingSkillInfo: SkillInfo, data: StandardRecognizerData<Calculator>)
: StandardRecognizerSkill<Calculator>(correspondingSkillInfo, data) {
class CalculatorSkill(
correspondingSkillInfo: SkillInfo,
data: StandardRecognizerData<Calculator>,
private val operatorRecognizerData: StandardRecognizerData<CalculatorOperators>,
) : StandardRecognizerSkill<Calculator>(correspondingSkillInfo, data) {

private fun getOperation(
ctx: SkillContext,
Expand Down Expand Up @@ -47,7 +50,6 @@ class CalculatorSkill(correspondingSkillInfo: SkillInfo, data: StandardRecognize
return CalculatorOutput(null, "", "")
}

val operatorRecognizerData = CalculatorOperators[ctx.sentencesLanguage]!!
var firstNumber: Number
var i: Int
if (textWithNumbers[0] is Number) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ object CurrentTimeInfo : SkillInfo("current_time") {
override fun icon() =
rememberVectorPainter(Icons.Default.Watch)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.CurrentTime[ctx.sentencesLanguage] != null
}

override fun build(ctx: SkillContext): Skill<*> {
return CurrentTimeSkill(CurrentTimeInfo, Sentences.CurrentTime[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.CurrentTime[ctx.sentencesLanguage] ?: return null
Comment thread
mretallack marked this conversation as resolved.
return CurrentTimeSkill(CurrentTimeInfo, data)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ object TextFallbackInfo : SkillInfo("text") {
override fun icon() =
rememberVectorPainter(Icons.Default.Warning)

override fun isAvailable(ctx: SkillContext): Boolean {
return true
}

override fun build(ctx: SkillContext): Skill<*> {
return TextFallbackSkill(TextFallbackInfo)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@ object FlashlightInfo : SkillInfo("flashlight") {
override fun icon() =
rememberVectorPainter(Icons.Default.FlashlightOn)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Flashlight[ctx.sentencesLanguage] != null &&
ctx.android.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)
}

@SuppressLint("NewApi") // since build() is not called if isAvailable() returned false
override fun build(ctx: SkillContext): Skill<*> {
return FlashlightSkill(FlashlightInfo, Sentences.Flashlight[ctx.sentencesLanguage]!!)
@SuppressLint("NewApi") // FlashlightSkill uses API 23+; guarded by FEATURE_CAMERA_FLASH check
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.Flashlight[ctx.sentencesLanguage] ?: return null
if (!ctx.android.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH))
return null
return FlashlightSkill(FlashlightInfo, data)
}
}
12 changes: 5 additions & 7 deletions app/src/main/kotlin/org/stypox/dicio/skills/joke/JokeInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ object JokeInfo : SkillInfo("Joke") {
override fun icon() =
rememberVectorPainter(Icons.Default.EmojiEmotions)

override fun isAvailable(ctx: SkillContext): Boolean {
return (Sentences.Joke[ctx.sentencesLanguage] != null) &&
LocaleUtils.isLocaleSupported(ctx.locale, JokeSkill.JOKE_SUPPORTED_LOCALES)
}

override fun build(ctx: SkillContext): Skill<*> {
return JokeSkill(JokeInfo, Sentences.Joke[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.Joke[ctx.sentencesLanguage] ?: return null
val locale = LocaleUtils.resolveSupportedLocale(ctx.locale, JokeSkill.JOKE_SUPPORTED_LOCALES)
?: return null
return JokeSkill(JokeInfo, data, locale)
}
}
16 changes: 7 additions & 9 deletions app/src/main/kotlin/org/stypox/dicio/skills/joke/JokeSkill.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,22 @@ import org.dicio.skill.standard.StandardRecognizerSkill
import org.json.JSONObject
import org.stypox.dicio.sentences.Sentences.Joke
import org.stypox.dicio.util.ConnectionUtils
import org.stypox.dicio.util.LocaleUtils

class JokeSkill(correspondingSkillInfo: SkillInfo, data: StandardRecognizerData<Joke>)
: StandardRecognizerSkill<Joke>(correspondingSkillInfo, data) {
class JokeSkill(
correspondingSkillInfo: SkillInfo,
data: StandardRecognizerData<Joke>,
private val resolvedLocale: String,
) : StandardRecognizerSkill<Joke>(correspondingSkillInfo, data) {
override suspend fun generateOutput(ctx: SkillContext, inputData: Joke): SkillOutput {
// we can use !! because the JokeInfo would have declared this skill unavailable
// if the current locale was not among the supported ones
val locale = LocaleUtils.resolveSupportedLocale(ctx.locale, JOKE_SUPPORTED_LOCALES)!!

if (locale == "en") {
if (resolvedLocale == "en") {
val joke: JSONObject = ConnectionUtils.getPageJson(RANDOM_JOKE_URL_EN)
return JokeOutput.Success(
setup = joke.getString("setup"),
delivery = joke.getString("punchline")
)
} else {
val joke: JSONObject = ConnectionUtils.getPageJson(
"$RANDOM_JOKE_URL?lang=$locale&safe-mode&type=twopart"
"$RANDOM_JOKE_URL?lang=$resolvedLocale&safe-mode&type=twopart"
)
return JokeOutput.Success(
setup = joke.getString("setup"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ class ListeningInfo(
override fun icon() =
rememberVectorPainter(Icons.Default.Hearing)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Listening[ctx.sentencesLanguage] != null
}

override fun build(ctx: SkillContext): Skill<*> {
return ListeningSkill(this, Sentences.Listening[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.Listening[ctx.sentencesLanguage] ?: return null
return ListeningSkill(this, data)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ object LyricsInfo : SkillInfo("lyrics") {
override fun icon() =
rememberVectorPainter(Icons.Default.MusicNote)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Lyrics[ctx.sentencesLanguage] != null
}

override fun build(ctx: SkillContext): Skill<*> {
return LyricsSkill(LyricsInfo, Sentences.Lyrics[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.Lyrics[ctx.sentencesLanguage] ?: return null
return LyricsSkill(LyricsInfo, data)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ object MediaInfo : SkillInfo("media") {
override fun icon() =
rememberVectorPainter(Icons.AutoMirrored.Filled.QueueMusic)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Media[ctx.sentencesLanguage] != null
}

override fun build(ctx: SkillContext): Skill<*> {
return MediaSkill(MediaInfo, Sentences.Media[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.Media[ctx.sentencesLanguage] ?: return null
return MediaSkill(MediaInfo, data)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,8 @@ object NavigationInfo : SkillInfo("navigation") {
override fun icon() =
rememberVectorPainter(Icons.Default.Directions)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Navigation[ctx.sentencesLanguage] != null
}

override fun build(ctx: SkillContext): Skill<*> {
return NavigationSkill(NavigationInfo, Sentences.Navigation[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.Navigation[ctx.sentencesLanguage] ?: return null
return NavigationSkill(NavigationInfo, data)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,11 @@ object NotifyInfo: SkillInfo("notify") {
override fun icon() =
rememberVectorPainter(Icons.Default.NotificationsActive)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Notify[ctx.sentencesLanguage] != null
}

override val neededPermissions: List<Permission>
= listOf(PERMISSION_NOTIFICATION_LISTENER)

override fun build(ctx: SkillContext): Skill<*> {
return NotifySkill(NotifyInfo, Sentences.Notify[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.Notify[ctx.sentencesLanguage] ?: return null
return NotifySkill(NotifyInfo, data)
}
}
9 changes: 3 additions & 6 deletions app/src/main/kotlin/org/stypox/dicio/skills/open/OpenInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,8 @@ object OpenInfo : SkillInfo("open") {
override fun icon() =
rememberVectorPainter(Icons.AutoMirrored.Filled.OpenInNew)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Open[ctx.sentencesLanguage] != null
}

override fun build(ctx: SkillContext): Skill<*> {
return OpenSkill(OpenInfo, Sentences.Open[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.Open[ctx.sentencesLanguage] ?: return null
return OpenSkill(OpenInfo, data)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ object SearchInfo : SkillInfo("search") {
override fun icon() =
rememberVectorPainter(Icons.Default.Search)

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Search[ctx.sentencesLanguage] != null
}

override fun build(ctx: SkillContext): Skill<*> {
return SearchSkill(SearchInfo, Sentences.Search[ctx.sentencesLanguage]!!)
override fun build(ctx: SkillContext): Skill<*>? {
val data = Sentences.Search[ctx.sentencesLanguage] ?: return null
return SearchSkill(SearchInfo, data)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.ui.unit.dp
import org.dicio.skill.context.SkillContext
import org.dicio.skill.skill.InteractionPlan
import org.dicio.skill.skill.SkillOutput
import org.dicio.skill.standard.StandardRecognizerData
import org.stypox.dicio.R
import org.stypox.dicio.io.graphical.Body
import org.stypox.dicio.io.graphical.Headline
Expand All @@ -18,14 +19,14 @@ import org.stypox.dicio.util.getString

class ConfirmCallOutput(
private val name: String,
private val number: String
private val number: String,
private val yesNoData: StandardRecognizerData<Sentences.UtilYesNo>,
) : SkillOutput {
override fun getSpeechOutput(ctx: SkillContext): String =
ctx.getString(R.string.skill_telephone_confirm_call, name)

override fun getInteractionPlan(ctx: SkillContext): InteractionPlan {
val yesNoSentences = Sentences.UtilYesNo[ctx.sentencesLanguage]!!
val confirmYesNoSkill = object : RecognizeYesNoSkill(TelephoneInfo, yesNoSentences) {
val confirmYesNoSkill = object : RecognizeYesNoSkill(TelephoneInfo, yesNoData) {
override suspend fun generateOutput(
ctx: SkillContext,
inputData: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ import org.dicio.skill.skill.Skill
import org.dicio.skill.skill.SkillOutput
import org.dicio.skill.skill.Specificity

class ContactChooserIndex internal constructor(private val contacts: List<Pair<String, String>>) :
Skill<Int>(TelephoneInfo, Specificity.HIGH) {
import org.dicio.skill.standard.StandardRecognizerData
import org.stypox.dicio.sentences.Sentences

class ContactChooserIndex internal constructor(
private val contacts: List<Pair<String, String>>,
private val yesNoData: StandardRecognizerData<Sentences.UtilYesNo>,
) : Skill<Int>(TelephoneInfo, Specificity.HIGH) {

override fun score(
ctx: SkillContext,
Expand All @@ -29,7 +34,7 @@ class ContactChooserIndex internal constructor(private val contacts: List<Pair<S
override suspend fun generateOutput(ctx: SkillContext, inputData: Int): SkillOutput {
if (inputData > 0 && inputData <= contacts.size) {
val contact = contacts[inputData - 1]
return ConfirmCallOutput(contact.first, contact.second)
return ConfirmCallOutput(contact.first, contact.second, yesNoData)
} else {
// impossible situation
return ConfirmedCallOutput(null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ import org.dicio.skill.skill.Score
import org.dicio.skill.skill.Skill
import org.dicio.skill.skill.SkillOutput
import org.dicio.skill.skill.Specificity
import org.dicio.skill.standard.StandardRecognizerData
import org.stypox.dicio.sentences.Sentences
import org.stypox.dicio.util.StringUtils

class ContactChooserName internal constructor(private val contacts: List<Pair<String, String>>) :
class ContactChooserName internal constructor(
private val contacts: List<Pair<String, String>>,
private val yesNoData: StandardRecognizerData<Sentences.UtilYesNo>,
) :
// use a low specificity to prefer the index-based contact chooser
Skill<Pair<String, String>?>(TelephoneInfo, Specificity.LOW) {

Expand Down Expand Up @@ -38,7 +43,7 @@ class ContactChooserName internal constructor(private val contacts: List<Pair<St

override suspend fun generateOutput(ctx: SkillContext, inputData: Pair<String, String>?): SkillOutput {
return inputData?.let {
ConfirmCallOutput(it.first, it.second)
ConfirmCallOutput(it.first, it.second, yesNoData)
}
// impossible situation
?: ConfirmedCallOutput(null)
Expand Down
Loading
Loading