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
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 @@ -31,6 +31,7 @@ import org.stypox.dicio.skills.timer.TimerInfo
import org.stypox.dicio.skills.translation.TranslationInfo
import org.stypox.dicio.skills.weather.WeatherInfo
import org.stypox.dicio.skills.joke.JokeInfo
import org.stypox.dicio.skills.music.MusicInfo
import javax.inject.Inject
import javax.inject.Singleton

Expand All @@ -52,6 +53,7 @@ class SkillHandler @Inject constructor(
TimerInfo,
CurrentTimeInfo,
MediaInfo,
MusicInfo,
JokeInfo,
ListeningInfo(dataStore),
TranslationInfo,
Expand Down
39 changes: 39 additions & 0 deletions app/src/main/kotlin/org/stypox/dicio/skills/music/MusicInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.stypox.dicio.skills.music

import android.content.Context
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Notes
import androidx.compose.material.icons.automirrored.filled.OpenInNew
import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
import androidx.compose.material.icons.automirrored.filled.QueueMusic
import androidx.compose.material.icons.filled.Directions
import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.runtime.Composable
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.SkillInfo
import org.stypox.dicio.R
import org.stypox.dicio.sentences.Sentences
import org.stypox.dicio.skills.open.OpenSkill

object MusicInfo : SkillInfo("music") {
override fun name(context: Context) =
context.getString(R.string.skill_name_music)

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

@Composable
override fun icon() =
rememberVectorPainter(Icons.AutoMirrored.Filled.QueueMusic)

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

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

import android.content.pm.PackageManager
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import org.dicio.skill.context.SkillContext
import org.dicio.skill.skill.SkillOutput
import org.stypox.dicio.R
import org.stypox.dicio.io.graphical.Headline
import org.stypox.dicio.util.getString

private val TAG = MusicOutput::class.simpleName

class MusicOutput(
private val appName: String?,
private val packageName: String?,
) : SkillOutput {
override fun getSpeechOutput(ctx: SkillContext): String = if (packageName == null) {
ctx.getString(R.string.skill_music_no_app_found)
} else {
ctx.getString(R.string.skill_open_opening, appName)
}

@Composable
override fun GraphicalOutput(ctx: SkillContext) {
if (appName == null || packageName == null) {
Headline(text = getSpeechOutput(ctx))

} else {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
val context = LocalContext.current
val icon = remember {
try {
context.packageManager.getApplicationIcon(packageName)
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Could not load icon for $packageName", e)
null
}
}

if (icon != null) {
Image(
painter = rememberDrawablePainter(icon),
contentDescription = appName,
modifier = Modifier
.fillMaxWidth(0.2f)
.aspectRatio(1.0f),
)

Spacer(modifier = Modifier.width(8.dp))
}

Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth(),
) {
Text(
text = getSpeechOutput(ctx),
style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
)

Text(
text = packageName,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
)
}
}
}
}
}
48 changes: 48 additions & 0 deletions app/src/main/kotlin/org/stypox/dicio/skills/music/MusicSkill.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.stypox.dicio.skills.music

import android.app.SearchManager
import android.content.Intent
import android.content.pm.PackageManager
import android.provider.MediaStore
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.Music

class MusicSkill(correspondingSkillInfo: SkillInfo, data: StandardRecognizerData<Music>) :
StandardRecognizerSkill<Music>(correspondingSkillInfo, data) {

override suspend fun generateOutput(ctx: SkillContext, inputData: Music): SkillOutput {
val (song, artist) = when (inputData) {
is Music.Query -> Pair(inputData.song, inputData.artist)
}

val intent = Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH).apply {
putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Media.ENTRY_CONTENT_TYPE)
putExtra(MediaStore.EXTRA_MEDIA_TITLE, song)
putExtra(SearchManager.QUERY, song)
}
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
// also search for the artist if given
if (artist != null)
intent.apply {
putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist)
putExtra(SearchManager.QUERY, "$song $artist")
}

val packageManager: PackageManager = ctx.android.packageManager
val componentName = intent.resolveActivity(packageManager)
if (componentName == null) {
return MusicOutput(appName = null, packageName = null)
}
ctx.android.startActivity(intent)

val applicationInfo = packageManager.getApplicationInfo(componentName.packageName, 0)
return MusicOutput(
appName = applicationInfo.loadLabel(packageManager).toString(),
packageName = applicationInfo.packageName,
)
}
}
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,7 @@
<string name="failed_to_copy">Failed to copy to clipboard</string>
<string name="skill_translation_auto">Auto</string>
<string name="skill_search_duckduckgo_recaptcha">DuckDuckGo did not provide results, asking for a Captcha to be solved</string>
<string name="skill_music_no_app_found">No music player found</string>
<string name="skill_name_music">Play Music</string>
<string name="skill_sentence_example_music">Play We will rock you by Queen</string>
</resources>
2 changes: 2 additions & 0 deletions app/src/main/sentences/en/music.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
query:
- play .song. (by .artist.)?
10 changes: 10 additions & 0 deletions app/src/main/sentences/skill_definitions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ skills:
- id: where
type: string

- id: music
specificity: high
sentences:
- id: query
captures:
- id: song
type: string
- id: artist
type: string

- id: media
specificity: high
sentences:
Expand Down