diff --git a/app/src/main/java/com/ztftrue/music/MusicViewModel.kt b/app/src/main/java/com/ztftrue/music/MusicViewModel.kt
index ff87dd4..6559cf9 100644
--- a/app/src/main/java/com/ztftrue/music/MusicViewModel.kt
+++ b/app/src/main/java/com/ztftrue/music/MusicViewModel.kt
@@ -20,6 +20,7 @@ import androidx.navigation.NavHostController
import com.ztftrue.music.sqlData.model.MainTab
import com.ztftrue.music.sqlData.model.MusicItem
import com.ztftrue.music.ui.play.Lyrics
+import com.ztftrue.music.utils.AnnotatedStringCaption
import com.ztftrue.music.utils.AnyListBase
import com.ztftrue.music.utils.Caption
import com.ztftrue.music.utils.EqualizerBand
@@ -88,7 +89,7 @@ class MusicViewModel : ViewModel() {
// lyrics
var itemDuration: Long = 1
var hasTime: LyricsType = LyricsType.TEXT
- var currentCaptionList = mutableStateListOf
()
+ var currentCaptionList = mutableStateListOf()
// sleep time
var sleepTime = mutableLongStateOf(0L)
@@ -157,24 +158,29 @@ class MusicViewModel : ViewModel() {
hasTime = LyricsType.VTT
currentCaptionList.addAll(readCaptions(File("$path.vtt"), LyricsType.VTT))
} else {
-
+ return
}
-
}
val duration = currentPlay.duration
// every lyrics line duration
- itemDuration = duration / if(currentCaptionList.size == 0) 1 else currentCaptionList.size
+ itemDuration = duration / if (currentCaptionList.size == 0) 1 else currentCaptionList.size
}
- private fun readLyricsOrText(file: File, context: Context): ArrayList {
- val arrayList = arrayListOf()
+ private fun readLyricsOrText(file: File, context: Context): ArrayList {
+ val arrayList = arrayListOf()
val inputStream: InputStream = file.inputStream()
val inputString = inputStream.bufferedReader().use { it.readText() }
inputString.split("\n").forEach {
if (it.startsWith("offset:")) {
// TODO
} else {
- arrayList.add(Utils.parseLyricLine(it, context))
+ val captions = Utils.parseLyricLine(it, context)
+ val an = AnnotatedStringCaption(
+ text = captions.text.split(Regex("[\\n\\r\\s]+")),
+ timeStart = captions.timeStart,
+ timeEnd = captions.timeEnd
+ )
+ arrayList.add(an)
}
}
return arrayList
@@ -183,13 +189,23 @@ class MusicViewModel : ViewModel() {
private fun readCaptions(
file: File,
captionType: LyricsType,
- ): ArrayList {
- val arrayList = arrayListOf()
+ ): ArrayList {
+ val captions = arrayListOf()
if (captionType == LyricsType.SRT) {
- arrayList.addAll(Utils.parseSrtFile(file))
+ captions.addAll(Utils.parseSrtFile(file))
} else if (captionType == LyricsType.VTT) {
- arrayList.addAll(Utils.parseVttFile(file))
+ captions.addAll(Utils.parseVttFile(file))
}
+ val arrayList = arrayListOf()
+ captions.forEach {
+ val an = AnnotatedStringCaption(
+ text = it.text.split(Regex("[\\n\\r\\s]+")),
+ timeStart = it.timeStart,
+ timeEnd = it.timeEnd
+ )
+ arrayList.add(an)
+ }
+
return arrayList
}
diff --git a/app/src/main/java/com/ztftrue/music/ui/play/CoverView.kt b/app/src/main/java/com/ztftrue/music/ui/play/CoverView.kt
index 949e5be..e563aee 100644
--- a/app/src/main/java/com/ztftrue/music/ui/play/CoverView.kt
+++ b/app/src/main/java/com/ztftrue/music/ui/play/CoverView.kt
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -35,7 +36,7 @@ import com.ztftrue.music.R
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
fun CoverView(musicViewModel: MusicViewModel) {
val listState = rememberLazyListState()
- var paint by remember { mutableStateOf(null) }
+ var paint by remember { mutableStateOf(null) }
val showOtherMessage = remember { mutableStateOf(false) }
LaunchedEffect(musicViewModel.currentPlay.value) {
@@ -77,6 +78,16 @@ fun CoverView(musicViewModel: MusicViewModel) {
color = MaterialTheme.colorScheme.onBackground,// Set the text color here
fontSize = MaterialTheme.typography.titleSmall.fontSize
)
+ Text(
+ text = "artist: ${it1.artist}", modifier =
+ Modifier
+ .padding(0.dp)
+ .height(30.dp)
+ .horizontalScroll(rememberScrollState(0))
+ .fillMaxWidth(),
+ color = MaterialTheme.colorScheme.onBackground,// Set the text color here
+ fontSize = MaterialTheme.typography.titleSmall.fontSize
+ )
Text(
text = "album: ${it1.album}", modifier =
Modifier
diff --git a/app/src/main/java/com/ztftrue/music/ui/play/LyricsView.kt b/app/src/main/java/com/ztftrue/music/ui/play/LyricsView.kt
index d09e61f..f365e01 100644
--- a/app/src/main/java/com/ztftrue/music/ui/play/LyricsView.kt
+++ b/app/src/main/java/com/ztftrue/music/ui/play/LyricsView.kt
@@ -1,7 +1,9 @@
package com.ztftrue.music.ui.play
+import android.content.Context
import android.content.Intent
-import androidx.compose.foundation.ExperimentalFoundationApi
+import android.util.TypedValue
+import android.view.MotionEvent
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
@@ -24,13 +26,17 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.motionEventSpy
+import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
@@ -40,7 +46,10 @@ import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -49,7 +58,7 @@ import androidx.compose.ui.window.PopupProperties
import androidx.media3.common.util.UnstableApi
import com.ztftrue.music.MainActivity
import com.ztftrue.music.MusicViewModel
-import com.ztftrue.music.utils.Caption
+import com.ztftrue.music.utils.AnnotatedStringCaption
import com.ztftrue.music.utils.CustomTextToolbar
import com.ztftrue.music.utils.LyricsType
import com.ztftrue.music.utils.Utils
@@ -63,7 +72,7 @@ const val Lyrics = "lyrics"
var size = mutableStateOf(IntSize.Zero)
@UnstableApi
-@OptIn(ExperimentalFoundationApi::class)
+@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun LyricsView(
musicViewModel: MusicViewModel,
@@ -115,8 +124,8 @@ fun LyricsView(
} else {
currentI = (timeState / musicViewModel.itemDuration).toInt()
if (musicViewModel.currentCaptionList.getOrElse(currentI) {
- Caption("", 0)
- }.text.isNotBlank()) {
+ AnnotatedStringCaption(arrayListOf(), 0)
+ }.text.isNotEmpty()) {
if (musicViewModel.autoScroll.value && isSelected && !showMenu) {
launch(Dispatchers.Main) {
listState.scrollToItem(if ((currentI - 1) < 0) 0 else (currentI - 1), 0)
@@ -126,27 +135,44 @@ fun LyricsView(
}
}
-
+ var fontSize by remember {
+ mutableIntStateOf(18)
+ }
var word by remember {
mutableStateOf("")
}
+ var selectedTag by remember {
+ mutableStateOf("")
+ }
+
+ var popupOffset by remember {
+ mutableStateOf(IntOffset(0, 0))
+ }
+
if (showMenu) {
val list = getAllCitivity(context)
- if(list.isEmpty()) {
- showMenu=false
- }else{
+ if (list.isEmpty()) {
+ showMenu = false
+ } else {
Popup(
// on below line we are adding
// alignment and properties.
alignment = Alignment.TopCenter,
- properties = PopupProperties()
+ properties = PopupProperties(),
+ offset = popupOffset,
+ onDismissRequest = {
+ showMenu = false
+ isSelected = false
+ selectedTag = ""
+ word = ""
+ }
) {
val rowListSate = rememberLazyListState()
val configuration = LocalConfiguration.current
// on the below line we are creating a box.
Column(
Modifier
- .size((configuration.screenWidthDp - 40).dp, 50.dp)
+ .size((configuration.screenWidthDp - 40).dp, 60.dp)
.padding(top = 5.dp)
// on below line we are adding background color
.background(
@@ -182,6 +208,9 @@ fun LyricsView(
word
)
showMenu = false
+ isSelected = false
+ selectedTag = ""
+ word = ""
context.startActivity(intent)
}
// color = MaterialTheme.colorScheme.onBackground,
@@ -207,10 +236,7 @@ fun LyricsView(
modifier = Modifier
.fillMaxWidth()
.padding(2.dp)
- .onSizeChanged { sizeIt ->
- size.value = sizeIt
- }
- .clickable() {
+ .clickable {
if (musicViewModel.currentPlay.value != null) {
val regexPattern = Regex("[<>\"/~'{}?,+=)(^&*%!@#\$]")
val artistsFolder = musicViewModel.currentPlay.value?.artist
@@ -247,56 +273,86 @@ fun LyricsView(
CompositionLocalProvider(
LocalTextToolbar provides CustomTextToolbar(LocalView.current)
) {
-
SelectionContainer {
LazyColumn(
state = listState,
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
+ .pointerInteropFilter {
+ when (it.action) {
+ MotionEvent.ACTION_DOWN -> {
+ if (it.action == MotionEvent.ACTION_DOWN) {
+ val a = if (it.y > size.value.height / 2) {
+ it.y - fontSize * 2.5 - 60.dp.toPx(context)
+ } else {
+ it.y + fontSize * 2.5
+ }
+ popupOffset = IntOffset(0, a.toInt())
+ }
+ }
+ }
+ false
+ }
+ .motionEventSpy {
+
+ }
.onSizeChanged { sizeIt ->
size.value = sizeIt
}
.padding(start = 20.dp, end = 20.dp)
) {
- items(musicViewModel.currentCaptionList.size) {
- val tex = musicViewModel.currentCaptionList[it].text
- val annotatedString = buildAnnotatedString {
- for (text in tex.split(Regex("[\\n\\r\\s]+"))) {
- val pattern = Regex("[,:;.\"]")
- pushStringAnnotation("word", text.replace(pattern, ""))
- withStyle(style = SpanStyle()) {
- append(text)
+ items(musicViewModel.currentCaptionList.size) { listIndex ->
+ key(Unit) {
+ val tex = musicViewModel.currentCaptionList[listIndex].text
+ val annotatedString = buildAnnotatedString {
+ for ((index, text) in tex.withIndex()) {
+ val pattern = Regex("[,:;.\"]")
+ val tItem = text.replace(pattern, "")
+ pushStringAnnotation("word$tItem$index", tItem)
+ withStyle(
+ style = SpanStyle(
+ textDecoration = if (selectedTag == "$listIndex word$tItem$index") {
+ TextDecoration.Underline
+ } else {
+ TextDecoration.None
+ }
+ )
+ ) {
+ append(text)
+ }
+ pop()
+ pushStringAnnotation("space", "")
+ append(" ")
+ pop()
}
- pop()
- pushStringAnnotation("space", "")
- append(" ")
- pop()
}
- }
- ClickableText(
- text = annotatedString,
- style = TextStyle(
- color = MaterialTheme.colorScheme.onBackground,
- fontSize = if (currentI == it && musicViewModel.autoHighLight.value) 24.sp else
- 18.sp,
- textAlign = TextAlign.Center,
- lineHeight = MaterialTheme.typography.titleLarge.fontSize
- ),
- modifier = Modifier
- .fillMaxWidth()
- .padding(2.dp)
- .onSizeChanged { sizeIt ->
- size.value = sizeIt
- },
- onClick = { offset ->
+ ClickableText(
+ text = annotatedString,
+ style = TextStyle(
+ color = if (currentI == listIndex && musicViewModel.autoHighLight.value) {
+// MaterialTheme.colorScheme.onSecondary
+ Color.Blue
+ } else {
+ MaterialTheme.colorScheme.onBackground
+ },
+ fontSize = fontSize.sp,
+ textAlign = TextAlign.Center,
+ lineHeight = (fontSize * 1.5).sp
+ ),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(2.dp)
+ ) { offset ->
+
if (showMenu) {
showMenu = false
} else {
val annotations =
annotatedString.getStringAnnotations(offset, offset)
annotations.firstOrNull()?.let { itemAnnotations ->
- if (itemAnnotations.tag == "word") {
+ if (itemAnnotations.tag.startsWith("word")) {
+ selectedTag = "$listIndex ${itemAnnotations.tag}"
word = itemAnnotations.item
showMenu = true
} else {
@@ -305,7 +361,9 @@ fun LyricsView(
}
}
- })
+ }
+ }
+
}
}
}
@@ -315,3 +373,8 @@ fun LyricsView(
}
+private fun Dp.toPx(context: Context): Int {
+ val displayMetrics = context.resources.displayMetrics
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.value, displayMetrics).toInt()
+}
+
diff --git a/app/src/main/java/com/ztftrue/music/ui/play/PlayingPage.kt b/app/src/main/java/com/ztftrue/music/ui/play/PlayingPage.kt
index 30e1406..3bb0ce3 100644
--- a/app/src/main/java/com/ztftrue/music/ui/play/PlayingPage.kt
+++ b/app/src/main/java/com/ztftrue/music/ui/play/PlayingPage.kt
@@ -384,20 +384,9 @@ fun PlayingPage(
text = it1.name,
modifier = Modifier
.padding(0.dp)
- .height(40.dp)
.horizontalScroll(rememberScrollState(0))
.fillMaxWidth(),
color = MaterialTheme.colorScheme.onBackground,
- fontSize = MaterialTheme.typography.titleMedium.fontSize
- )
- Text(
- text = it1.artist, modifier =
- Modifier
- .padding(0.dp)
- .height(30.dp)
- .horizontalScroll(rememberScrollState(0))
- .fillMaxWidth(),
- color = MaterialTheme.colorScheme.onBackground,// Set the text color here
fontSize = MaterialTheme.typography.titleSmall.fontSize
)
}
diff --git a/app/src/main/java/com/ztftrue/music/utils/Model.kt b/app/src/main/java/com/ztftrue/music/utils/Model.kt
index f09ac59..37637ac 100644
--- a/app/src/main/java/com/ztftrue/music/utils/Model.kt
+++ b/app/src/main/java/com/ztftrue/music/utils/Model.kt
@@ -1,6 +1,7 @@
package com.ztftrue.music.utils
import android.os.Parcelable
+import androidx.compose.ui.text.AnnotatedString
import kotlinx.parcelize.Parcelize
import java.io.Serializable
@@ -82,4 +83,10 @@ data class Caption(
val text: String,
val timeStart: Long,
val timeEnd: Long=0
+)
+
+data class AnnotatedStringCaption(
+ val text: List,
+ val timeStart: Long,
+ val timeEnd: Long=0
)
\ No newline at end of file