Skip to content

Commit

Permalink
update: optimize ui on lyrics view
Browse files Browse the repository at this point in the history
  • Loading branch information
ZTFtrue committed Jan 29, 2024
1 parent 4248ec0 commit e3e395d
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 70 deletions.
38 changes: 27 additions & 11 deletions app/src/main/java/com/ztftrue/music/MusicViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -88,7 +89,7 @@ class MusicViewModel : ViewModel() {
// lyrics
var itemDuration: Long = 1
var hasTime: LyricsType = LyricsType.TEXT
var currentCaptionList = mutableStateListOf<Caption>()
var currentCaptionList = mutableStateListOf<AnnotatedStringCaption>()

// sleep time
var sleepTime = mutableLongStateOf(0L)
Expand Down Expand Up @@ -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<Caption> {
val arrayList = arrayListOf<Caption>()
private fun readLyricsOrText(file: File, context: Context): ArrayList<AnnotatedStringCaption> {
val arrayList = arrayListOf<AnnotatedStringCaption>()
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
Expand All @@ -183,13 +189,23 @@ class MusicViewModel : ViewModel() {
private fun readCaptions(
file: File,
captionType: LyricsType,
): ArrayList<Caption> {
val arrayList = arrayListOf<Caption>()
): ArrayList<AnnotatedStringCaption> {
val captions = arrayListOf<Caption>()
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<AnnotatedStringCaption>()
captions.forEach {
val an = AnnotatedStringCaption(
text = it.text.split(Regex("[\\n\\r\\s]+")),
timeStart = it.timeStart,
timeEnd = it.timeEnd
)
arrayList.add(an)
}

return arrayList
}

Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/com/ztftrue/music/ui/play/CoverView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<Bitmap?>(null) }
var paint by remember { mutableStateOf<Bitmap?>(null) }

val showOtherMessage = remember { mutableStateOf(false) }
LaunchedEffect(musicViewModel.currentPlay.value) {
Expand Down Expand Up @@ -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
Expand Down
157 changes: 110 additions & 47 deletions app/src/main/java/com/ztftrue/music/ui/play/LyricsView.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -182,6 +208,9 @@ fun LyricsView(
word
)
showMenu = false
isSelected = false
selectedTag = ""
word = ""
context.startActivity(intent)
}
// color = MaterialTheme.colorScheme.onBackground,
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -305,7 +361,9 @@ fun LyricsView(
}
}

})
}
}

}
}
}
Expand All @@ -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()
}

Loading

0 comments on commit e3e395d

Please sign in to comment.