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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ Currently Dicio answers questions about:
- **open**: opens an app on your device - _Open NewPipe_
- **calculator**: evaluates basic calculations - _What is four thousand and two times three minus a million divided by three hundred?_
- **telephone**: view and call contacts - _Call Tom_
- **timer**: set, query and cancel timers - _Set a timer for five minutes_
- **timer**: set timers - _Set a timer for eleven minutes_
- **alarm**: set alarms - _Set an alarm for 6pm_
- **current time**: query current time - _What time is it?_
- **navigation**: opens the navigation app at the requested position - _Take me to New York, fifteenth avenue_
- **jokes**: tells you a joke - _Tell me a joke_
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-feature android:name="android.hardware.telephony" android:required="false" />

<!-- the timer and alarm skills need to set alarms/timers in the clock app -->
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />

<!-- allowBackup=false because of a critical nasty bug: https://medium.com/p/924c91bafcac -->
<application
android:name=".App"
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/kotlin/org/stypox/dicio/eval/SkillHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.stypox.dicio.di.SkillContextImpl
import org.stypox.dicio.di.SkillContextInternal
import org.stypox.dicio.settings.datastore.UserSettings
import org.stypox.dicio.settings.datastore.UserSettingsModule
import org.stypox.dicio.skills.alarm.AlarmInfo
import org.stypox.dicio.skills.calculator.CalculatorInfo
import org.stypox.dicio.skills.current_time.CurrentTimeInfo
import org.stypox.dicio.skills.fallback.text.TextFallbackInfo
Expand Down Expand Up @@ -50,6 +51,7 @@ class SkillHandler @Inject constructor(
NavigationInfo,
TelephoneInfo,
TimerInfo,
AlarmInfo,
CurrentTimeInfo,
MediaInfo,
JokeInfo,
Expand Down
38 changes: 38 additions & 0 deletions app/src/main/kotlin/org/stypox/dicio/skills/alarm/AlarmInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.stypox.dicio.skills.alarm

import android.content.Context
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Alarm
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import org.dicio.skill.skill.Skill
import org.dicio.skill.context.SkillContext
import org.dicio.skill.skill.Permission
import org.dicio.skill.skill.SkillInfo
import org.stypox.dicio.R
import org.stypox.dicio.sentences.Sentences
import org.stypox.dicio.util.PERMISSION_SET_ALARM

object AlarmInfo : SkillInfo("alarm") {
override fun name(context: Context) =
context.getString(R.string.skill_name_alarm)

override fun sentenceExample(context: Context) =
context.getString(R.string.skill_sentence_example_alarm)

@Composable
override fun icon() =
rememberVectorPainter(Icons.Default.Alarm)

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

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

override fun build(ctx: SkillContext): Skill<*> {
return AlarmSkill(AlarmInfo, Sentences.Alarm[ctx.sentencesLanguage]!!)
}
}
95 changes: 95 additions & 0 deletions app/src/main/kotlin/org/stypox/dicio/skills/alarm/AlarmOutput.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.stypox.dicio.skills.alarm

import androidx.compose.runtime.Composable
import org.dicio.skill.context.SkillContext
import org.dicio.skill.skill.AlwaysBestScore
import org.dicio.skill.skill.AlwaysWorstScore
import org.dicio.skill.skill.InteractionPlan
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.stypox.dicio.R
import org.stypox.dicio.io.graphical.Headline
import org.stypox.dicio.io.graphical.HeadlineSpeechSkillOutput
import org.stypox.dicio.util.getString
import java.time.LocalDateTime

sealed interface AlarmOutput : SkillOutput {
class Set(
private val hour: Int,
private val minute: Int,
) : AlarmOutput {
override fun getSpeechOutput(ctx: SkillContext): String {
val timeString = formatTime(hour, minute)
return ctx.getString(R.string.skill_alarm_set, timeString)
}

@Composable
override fun GraphicalOutput(ctx: SkillContext) {
val timeString = formatTime(hour, minute)
Headline(text = ctx.getString(R.string.skill_alarm_graphical_set, timeString))
}

private fun formatTime(hour: Int, minute: Int): String {
val period = if (hour < 12) "AM" else "PM"
val displayHour = when {
hour == 0 -> 12
hour > 12 -> hour - 12
else -> hour
}
return String.format("%d:%02d %s", displayHour, minute, period)
}
}

class SetAskTime(
private val onGotTime: suspend (LocalDateTime) -> SkillOutput,
) : AlarmOutput, HeadlineSpeechSkillOutput {
override fun getSpeechOutput(ctx: SkillContext): String =
ctx.getString(R.string.skill_alarm_what_time)

override fun getInteractionPlan(ctx: SkillContext): InteractionPlan {
val timeSkill = object : Skill<LocalDateTime?>(AlarmInfo, Specificity.HIGH) {
override fun score(
ctx: SkillContext,
input: String
): Pair<Score, LocalDateTime?> {
val time = ctx.parserFormatter!!
.extractDateTime(input)
.first

return Pair(
if (time == null) AlwaysWorstScore else AlwaysBestScore,
time
)
}

override suspend fun generateOutput(
ctx: SkillContext,
inputData: LocalDateTime?
): SkillOutput {
return if (inputData == null) {
throw RuntimeException("AlwaysWorstScore still triggered generateOutput")
} else {
onGotTime(inputData)
}
}
}

return InteractionPlan.StartSubInteraction(
reopenMicrophone = true,
nextSkills = listOf(timeSkill),
)
}
}

class OpeningClockApp : AlarmOutput, HeadlineSpeechSkillOutput {
override fun getSpeechOutput(ctx: SkillContext): String =
ctx.getString(R.string.skill_alarm_opening_clock_app)
}

class NoClockApp : AlarmOutput, HeadlineSpeechSkillOutput {
override fun getSpeechOutput(ctx: SkillContext): String =
ctx.getString(R.string.skill_alarm_no_clock_app)
}
}
66 changes: 66 additions & 0 deletions app/src/main/kotlin/org/stypox/dicio/skills/alarm/AlarmSkill.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.stypox.dicio.skills.alarm

import android.content.ActivityNotFoundException
import android.content.Intent
import android.provider.AlarmClock
import org.dicio.skill.context.SkillContext
import org.dicio.skill.skill.SkillInfo
import org.dicio.skill.skill.SkillOutput
import org.dicio.skill.standard.StandardRecognizerData
import org.dicio.skill.standard.StandardRecognizerSkill
import org.stypox.dicio.sentences.Sentences.Alarm
import java.time.LocalDateTime

class AlarmSkill(correspondingSkillInfo: SkillInfo, data: StandardRecognizerData<Alarm>) :
StandardRecognizerSkill<Alarm>(correspondingSkillInfo, data) {

override suspend fun generateOutput(ctx: SkillContext, inputData: Alarm): SkillOutput {
return when (inputData) {
is Alarm.Set -> {
val time = inputData.time?.let {
ctx.parserFormatter?.extractDateTime(it)?.first
}
if (time == null) {
AlarmOutput.SetAskTime { setAlarm(ctx, it) }
} else {
setAlarm(ctx, time)
}
}
is Alarm.Show -> {
showClockApp(ctx)
}
}
}

private fun setAlarm(
ctx: SkillContext,
time: LocalDateTime,
): SkillOutput {
try {
val intent = Intent(AlarmClock.ACTION_SET_ALARM).apply {
putExtra(AlarmClock.EXTRA_HOUR, time.hour)
putExtra(AlarmClock.EXTRA_MINUTES, time.minute)
putExtra(AlarmClock.EXTRA_SKIP_UI, true)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
ctx.android.startActivity(intent)

return AlarmOutput.Set(time.hour, time.minute)
} catch (e: ActivityNotFoundException) {
return AlarmOutput.NoClockApp()
}
}

private fun showClockApp(ctx: SkillContext): SkillOutput {
try {
val intent = Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
ctx.android.startActivity(intent)

return AlarmOutput.OpeningClockApp()
} catch (e: ActivityNotFoundException) {
return AlarmOutput.NoClockApp()
}
}
}
62 changes: 0 additions & 62 deletions app/src/main/kotlin/org/stypox/dicio/skills/timer/SetTimer.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.fragment.app.Fragment
import org.dicio.skill.skill.Skill
import org.dicio.skill.context.SkillContext
import org.dicio.skill.skill.Permission
import org.dicio.skill.skill.SkillInfo
import org.stypox.dicio.R
import org.stypox.dicio.sentences.Sentences
import org.stypox.dicio.util.PERMISSION_SET_ALARM

object TimerInfo : SkillInfo("timer") {
override fun name(context: Context) =
Expand All @@ -24,6 +26,9 @@ object TimerInfo : SkillInfo("timer") {
override fun icon() =
rememberVectorPainter(Icons.Default.Timer)

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

override fun isAvailable(ctx: SkillContext): Boolean {
return Sentences.Timer[ctx.sentencesLanguage] != null
&& Sentences.UtilYesNo[ctx.sentencesLanguage] != null
Expand Down
Loading