Skip to content

Commit

Permalink
feat: Add retrieve lyrics functionality
Browse files Browse the repository at this point in the history
This commit introduces the ability to retrieve lyrics from a third-party app in the metadata editor.

- A button was added to the metadata editor page to trigger lyrics retrieval.
- An intent is used to launch the third-party app and request lyrics.
- When the third-party app returns, the lyrics are extracted and populated into the lyrics field.
- A music note icon is now displayed as a placeholder if the original file is not an image.
- Horizontal and Vertical ButtonWithIconAndText composables were created for UI flexibility.
- Support for landscape orientation was improved in the metadata editor.
  • Loading branch information
BobbyESP committed Nov 18, 2024
1 parent 2dc16e4 commit 410b66f
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 137 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.bobbyesp.metadator.presentation.components.image
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.util.Log
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
Expand Down Expand Up @@ -71,11 +72,19 @@ fun AsyncImage(
contentDescription = "Song cover placeholder"
)
},
failure = {
failure = { error ->
//if the error exception if FileNotFoundException, then the icon is a music note, else error outline

val icon = if (error.reason != null && error.reason is java.io.FileNotFoundException) {
Icons.Rounded.MusicNote
} else {
Icons.Rounded.ErrorOutline
}

PlaceholderCreator(
modifier = imageModifier
.fillMaxSize(),
icon = Icons.Rounded.ErrorOutline,
icon = icon,
colorful = false,
contentDescription = "Song cover failed to load"
)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,11 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent

class MetadataEditorVM(
private val context: Context,
private val stateHandle: SavedStateHandle
) : KoinComponent, ViewModel() {
) : ViewModel() {
private val mutableState = MutableStateFlow(PageViewState())
val state = mutableState.onStart {
stateHandle.get<String>("path")?.let {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.bobbyesp.metadator.presentation.pages.utilities.tageditor

import android.content.res.Configuration
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Lyrics
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import com.bobbyesp.utilities.R

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SongSyncNeededDialog(
modifier: Modifier = Modifier,
onDismissRequest: () -> Unit,
) {
val uriLauncher = LocalUriHandler.current
AlertDialog(
icon = {
Icon(
imageVector = Icons.Filled.Lyrics,
contentDescription = "SongSync app needed"
)
}, modifier = modifier, onDismissRequest = onDismissRequest,
title = {
Text(text = stringResource(id = R.string.song_sync_needed))
}, text = {
Text(
text = buildAnnotatedString {
append(stringResource(id = R.string.song_sync_needed_desc))
append(" \n")
append(stringResource(id = R.string.song_sync_not_installed))
append(".")
},
)
}, confirmButton = {
TextButton(
onClick = {
uriLauncher.openUri("https://github.com/Lambada10/SongSync/releases/latest")
}
) {
Text(stringResource(id = R.string.download))
}
}, dismissButton = {
TextButton(
onClick = onDismissRequest
) {
Text(stringResource(id = R.string.dismiss))
}
}
)
}

@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun SongSyncNeededDialogPreview() {
SongSyncNeededDialog(onDismissRequest = { })
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import com.adamratzman.spotify.models.Track
import com.bobbyesp.metadator.R
import com.bobbyesp.metadator.presentation.components.cards.songs.spotify.SpotifyHorizontalSongCard
import com.bobbyesp.metadator.presentation.pages.utilities.tageditor.spotify.MetadataBsVM
import com.bobbyesp.ui.components.button.ButtonWithIconAndText
import com.bobbyesp.ui.components.button.VerticalButtonWithIconAndText
import com.bobbyesp.ui.components.state.LoadingState
import com.bobbyesp.utilities.states.ResourceState
import com.bobbyesp.utilities.ui.pagingStateHandler
Expand Down Expand Up @@ -114,7 +114,7 @@ fun SpMetadataBsSearch(
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically
) {
ButtonWithIconAndText(
VerticalButtonWithIconAndText(
icon = Icons.Rounded.Edit,
text = stringResource(R.string.edit_query),
modifier = Modifier.weight(1f),
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,8 @@
<string name="retrieving_spotify_token">Retrieving Spotify API token…</string>
<string name="by">by</string>
<string name="edit_query">Edit query</string>
<string name="retrieve_lyrics">Retrieve lyrics</string>
<string name="lyrics_retrieve_cancelled">The lyrics retrievement process has been cancelled</string>
<string name="empty_lyrics_received">The lyrics sent from the provider app were null or empty</string>
<string name="something_unexpected_occurred">Something unexpected occurred!</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
Expand All @@ -28,7 +29,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp

@Composable
fun ButtonWithIconAndText(
fun VerticalButtonWithIconAndText(
modifier: Modifier = Modifier,
icon: ImageVector,
text: String,
Expand Down Expand Up @@ -79,4 +80,53 @@ fun ButtonWithIconAndText(
)
}
}
}

@Composable
fun HorizontalButtonWithIconAndText(
modifier: Modifier = Modifier,
icon: ImageVector,
text: String,
enabled: Boolean = true,
border: Boolean = false,
backgroundColor: Color = MaterialTheme.colorScheme.secondaryContainer,
shape: CornerBasedShape = MaterialTheme.shapes.small,
onClick: () -> Unit = {}
) {

val animatedAlpha by animateFloatAsState(
targetValue = if (enabled) 1f else 0.4f
)

Surface(
modifier = modifier
.semantics { role = Role.Button }
.alpha(animatedAlpha),
onClick = onClick,
enabled = enabled,
shape = shape,
border = if (border) BorderStroke(1.dp, MaterialTheme.colorScheme.outline) else null,
color = backgroundColor
) {
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(8.dp)
) {
Icon(
imageVector = icon,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurface
)
Text(
text = text,
style = MaterialTheme.typography.bodySmall.copy(
fontWeight = FontWeight.Medium,
),
color = MaterialTheme.colorScheme.onSurface,
overflow = TextOverflow.Ellipsis,
)
}
}
}
24 changes: 24 additions & 0 deletions app/utilities/src/main/java/com/bobbyesp/utilities/Packages.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.bobbyesp.utilities

import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager

object Packages {
fun isPackageInstalled(context: Context, packageName: String): Boolean {
return try {
context.packageManager.getPackageInfo(packageName, 0)
true
} catch (e: PackageManager.NameNotFoundException) {
false
}
}

fun Intent.launchOrAction(context: Context, action: () -> Unit) {
if (resolveActivity(context.packageManager) != null) {
context.startActivity(this)
} else {
action()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ fun String?.formatForField(separator: String = ","): Array<String> {

fun String.isNumberInRange(start: Int, end: Int): Boolean {
return this.isNotEmpty() && this.isDigitsOnly() && this.length < 10 && this.toInt() >= start && this.toInt() <= end
}

fun String?.isNotNullOrBlank(): Boolean {
return this != null && this.isNotBlank()
}
4 changes: 4 additions & 0 deletions app/utilities/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
<string name="change_wifi_state">Change WiFi state</string>
<string name="read_media_audio">Read audio files</string>
<string name="manage_external_storage">Manage external storage</string>
<string name="song_sync_needed">SongSync needed</string>
<string name="song_sync_needed_desc">SongSync is a simple yet powerful lyrics downloader that lets users fetch lyrics from multiple sources. \nMetadator leverages its integrated bridge to retrieve the lyrics.</string>
<string name="song_sync_not_installed">It seems the app isn\'t installed on your device. Please download it to access this feature</string>
<string name="download">Download</string>


</resources>

0 comments on commit 410b66f

Please sign in to comment.