Skip to content

Commit e112139

Browse files
committed
add music skill for playing songs
1 parent 3fa05a3 commit e112139

File tree

7 files changed

+196
-0
lines changed

7 files changed

+196
-0
lines changed

app/src/main/kotlin/org/stypox/dicio/eval/SkillHandler.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import org.stypox.dicio.skills.translation.TranslationInfo
3333
import org.stypox.dicio.skills.weather.WeatherInfo
3434
import org.stypox.dicio.skills.joke.JokeInfo
3535
import org.stypox.dicio.skills.flashlight.FlashlightInfo
36+
import org.stypox.dicio.skills.music.MusicInfo
3637
import javax.inject.Inject
3738
import javax.inject.Singleton
3839

@@ -54,6 +55,7 @@ class SkillHandler @Inject constructor(
5455
TimerInfo,
5556
CurrentTimeInfo,
5657
MediaInfo,
58+
MusicInfo,
5759
JokeInfo,
5860
ListeningInfo(dataStore),
5961
TranslationInfo,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.stypox.dicio.skills.music
2+
3+
import android.content.Context
4+
import androidx.compose.material.icons.Icons
5+
import androidx.compose.material.icons.automirrored.filled.Notes
6+
import androidx.compose.material.icons.automirrored.filled.OpenInNew
7+
import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
8+
import androidx.compose.material.icons.automirrored.filled.QueueMusic
9+
import androidx.compose.material.icons.filled.Directions
10+
import androidx.compose.material.icons.filled.OpenInNew
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.ui.graphics.vector.rememberVectorPainter
13+
import androidx.fragment.app.Fragment
14+
import org.dicio.skill.skill.Skill
15+
import org.dicio.skill.context.SkillContext
16+
import org.dicio.skill.skill.SkillInfo
17+
import org.stypox.dicio.R
18+
import org.stypox.dicio.sentences.Sentences
19+
import org.stypox.dicio.skills.open.OpenSkill
20+
21+
object MusicInfo : SkillInfo("music") {
22+
override fun name(context: Context) =
23+
context.getString(R.string.skill_name_music)
24+
25+
override fun sentenceExample(context: Context) =
26+
context.getString(R.string.skill_sentence_example_music)
27+
28+
@Composable
29+
override fun icon() =
30+
rememberVectorPainter(Icons.AutoMirrored.Filled.QueueMusic)
31+
32+
override fun isAvailable(ctx: SkillContext): Boolean {
33+
return Sentences.Music[ctx.sentencesLanguage] != null
34+
}
35+
36+
override fun build(ctx: SkillContext): Skill<*> {
37+
return MusicSkill(MusicInfo, Sentences.Music[ctx.sentencesLanguage]!!)
38+
}
39+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package org.stypox.dicio.skills.music
2+
3+
import android.content.pm.PackageManager
4+
import android.util.Log
5+
import androidx.compose.foundation.Image
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.Row
8+
import androidx.compose.foundation.layout.Spacer
9+
import androidx.compose.foundation.layout.aspectRatio
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.width
12+
import androidx.compose.material3.MaterialTheme
13+
import androidx.compose.material3.Text
14+
import androidx.compose.runtime.Composable
15+
import androidx.compose.runtime.remember
16+
import androidx.compose.ui.Alignment
17+
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.platform.LocalContext
19+
import androidx.compose.ui.text.style.TextAlign
20+
import androidx.compose.ui.unit.dp
21+
import com.google.accompanist.drawablepainter.rememberDrawablePainter
22+
import org.dicio.skill.context.SkillContext
23+
import org.dicio.skill.skill.SkillOutput
24+
import org.stypox.dicio.R
25+
import org.stypox.dicio.io.graphical.Headline
26+
import org.stypox.dicio.util.getString
27+
28+
private val TAG = MusicOutput::class.simpleName
29+
30+
class MusicOutput(
31+
private val appName: String?,
32+
private val packageName: String?,
33+
) : SkillOutput {
34+
override fun getSpeechOutput(ctx: SkillContext): String = if (packageName == null) {
35+
ctx.getString(R.string.skill_music_no_app_found)
36+
} else {
37+
ctx.getString(R.string.skill_open_opening, appName)
38+
}
39+
40+
@Composable
41+
override fun GraphicalOutput(ctx: SkillContext) {
42+
if (appName == null || packageName == null) {
43+
Headline(text = getSpeechOutput(ctx))
44+
45+
} else {
46+
Row(
47+
modifier = Modifier.fillMaxWidth(),
48+
verticalAlignment = Alignment.CenterVertically,
49+
) {
50+
val context = LocalContext.current
51+
val icon = remember {
52+
try {
53+
context.packageManager.getApplicationIcon(packageName)
54+
} catch (e: PackageManager.NameNotFoundException) {
55+
Log.e(TAG, "Could not load icon for $packageName", e)
56+
null
57+
}
58+
}
59+
60+
if (icon != null) {
61+
Image(
62+
painter = rememberDrawablePainter(icon),
63+
contentDescription = appName,
64+
modifier = Modifier
65+
.fillMaxWidth(0.2f)
66+
.aspectRatio(1.0f),
67+
)
68+
69+
Spacer(modifier = Modifier.width(8.dp))
70+
}
71+
72+
Column(
73+
horizontalAlignment = Alignment.CenterHorizontally,
74+
modifier = Modifier.fillMaxWidth(),
75+
) {
76+
Text(
77+
text = getSpeechOutput(ctx),
78+
style = MaterialTheme.typography.headlineMedium,
79+
textAlign = TextAlign.Center,
80+
modifier = Modifier.fillMaxWidth(),
81+
)
82+
83+
Text(
84+
text = packageName,
85+
textAlign = TextAlign.Center,
86+
modifier = Modifier.fillMaxWidth(),
87+
)
88+
}
89+
}
90+
}
91+
}
92+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.stypox.dicio.skills.music
2+
3+
import android.app.SearchManager
4+
import android.content.Intent
5+
import android.content.pm.PackageManager
6+
import android.provider.MediaStore
7+
import org.dicio.skill.context.SkillContext
8+
import org.dicio.skill.skill.SkillInfo
9+
import org.dicio.skill.skill.SkillOutput
10+
import org.dicio.skill.standard.StandardRecognizerData
11+
import org.dicio.skill.standard.StandardRecognizerSkill
12+
import org.stypox.dicio.sentences.Sentences.Music
13+
14+
class MusicSkill(correspondingSkillInfo: SkillInfo, data: StandardRecognizerData<Music>) :
15+
StandardRecognizerSkill<Music>(correspondingSkillInfo, data) {
16+
17+
override suspend fun generateOutput(ctx: SkillContext, inputData: Music): SkillOutput {
18+
val (song, artist) = when (inputData) {
19+
is Music.Query -> Pair(inputData.song, inputData.artist)
20+
}
21+
22+
val intent = Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH).apply {
23+
putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Media.ENTRY_CONTENT_TYPE)
24+
putExtra(MediaStore.EXTRA_MEDIA_TITLE, song)
25+
putExtra(SearchManager.QUERY, song)
26+
}
27+
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
28+
// also search for the artist if given
29+
if (artist != null)
30+
intent.apply {
31+
putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist)
32+
putExtra(SearchManager.QUERY, "$song $artist")
33+
}
34+
35+
val packageManager: PackageManager = ctx.android.packageManager
36+
val componentName = intent.resolveActivity(packageManager)
37+
if (componentName == null) {
38+
return MusicOutput(appName = null, packageName = null)
39+
}
40+
ctx.android.startActivity(intent)
41+
42+
val applicationInfo = packageManager.getApplicationInfo(componentName.packageName, 0)
43+
return MusicOutput(
44+
appName = applicationInfo.loadLabel(packageManager).toString(),
45+
packageName = applicationInfo.packageName,
46+
)
47+
}
48+
}

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,4 +278,7 @@
278278
<string name="about_privacy_title">Privacy policy</string>
279279
<string name="about_privacy_description">Dicio connects to external services only when a skill is expected to do so, or to download machine learning models during setup. All speech processing is performed locally on-device, and most skills can be used offline.</string>
280280
<string name="about_privacy_link" translatable="false">https://stypox.org/dicio-privacy-policy.html</string>
281+
<string name="skill_music_no_app_found">No music player found</string>
282+
<string name="skill_name_music">Play Music</string>
283+
<string name="skill_sentence_example_music">Play We will rock you by Queen</string>
281284
</resources>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
query:
2+
- play .song. (by .artist.)?

app/src/main/sentences/skill_definitions.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ skills:
3737
- id: where
3838
type: string
3939

40+
- id: music
41+
specificity: high
42+
sentences:
43+
- id: query
44+
captures:
45+
- id: song
46+
type: string
47+
- id: artist
48+
type: string
49+
4050
- id: media
4151
specificity: high
4252
sentences:

0 commit comments

Comments
 (0)