From cf752f2dc131ad437a8740883500f1f985267ef1 Mon Sep 17 00:00:00 2001 From: spectreseven1138 Date: Sat, 5 Nov 2022 23:59:51 +0000 Subject: [PATCH] Various fixes (including now playing & lyrics performance) --- .../java/com/spectre7/spmp/MainActivity.kt | 6 +- .../main/java/com/spectre7/spmp/PlayerHost.kt | 49 +-- .../java/com/spectre7/spmp/api/DataApi.kt | 35 +-- .../com/spectre7/spmp/api/TextWithReading.kt | 90 +++--- .../java/com/spectre7/spmp/model/Artist.kt | 3 +- .../main/java/com/spectre7/spmp/model/Song.kt | 5 +- .../ArtistPreview.kt | 3 +- .../MultiSelector.kt | 2 +- .../ui/{components => component}/PillMenu.kt | 2 +- .../{components => component}/SongPreview.kt | 15 +- .../spmp/ui/components/DownloadMenu.kt | 32 -- .../spectre7/spmp/ui/layout/DownloadMenu.kt | 14 + .../{components => layout}/LyricsDisplay.kt | 172 +++-------- .../ui/{components => layout}/NowPlaying.kt | 289 ++++++++---------- .../NowPlayingPaletteSelector.kt | 2 +- .../NowPlayingQueueTab.kt | 41 +-- .../com/spectre7/spmp/ui/layout/PlayerView.kt | 102 +++++-- .../com/spectre7/spmp/ui/layout/PrefsPage.kt | 4 - 18 files changed, 382 insertions(+), 484 deletions(-) rename app/src/main/java/com/spectre7/spmp/ui/{components => component}/ArtistPreview.kt (95%) rename app/src/main/java/com/spectre7/spmp/ui/{components => component}/MultiSelector.kt (96%) rename app/src/main/java/com/spectre7/spmp/ui/{components => component}/PillMenu.kt (99%) rename app/src/main/java/com/spectre7/spmp/ui/{components => component}/SongPreview.kt (87%) delete mode 100644 app/src/main/java/com/spectre7/spmp/ui/components/DownloadMenu.kt create mode 100644 app/src/main/java/com/spectre7/spmp/ui/layout/DownloadMenu.kt rename app/src/main/java/com/spectre7/spmp/ui/{components => layout}/LyricsDisplay.kt (59%) rename app/src/main/java/com/spectre7/spmp/ui/{components => layout}/NowPlaying.kt (77%) rename app/src/main/java/com/spectre7/spmp/ui/{components => layout}/NowPlayingPaletteSelector.kt (95%) rename app/src/main/java/com/spectre7/spmp/ui/{components => layout}/NowPlayingQueueTab.kt (81%) diff --git a/app/src/main/java/com/spectre7/spmp/MainActivity.kt b/app/src/main/java/com/spectre7/spmp/MainActivity.kt index 09ca422d5..edf391eaf 100644 --- a/app/src/main/java/com/spectre7/spmp/MainActivity.kt +++ b/app/src/main/java/com/spectre7/spmp/MainActivity.kt @@ -33,7 +33,7 @@ class MainActivity : ComponentActivity() { Python.start(AndroidPlatform(this)) } - PlayerHost(this) + PlayerHost() window.setFlags( WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, @@ -45,7 +45,9 @@ class MainActivity : ComponentActivity() { theme = Theme.default() Surface(modifier = Modifier.fillMaxSize()) { - PlayerView() + if (PlayerHost.service_connected) { + PlayerView() + } } } } diff --git a/app/src/main/java/com/spectre7/spmp/PlayerHost.kt b/app/src/main/java/com/spectre7/spmp/PlayerHost.kt index 42b85f304..3e39a92ed 100644 --- a/app/src/main/java/com/spectre7/spmp/PlayerHost.kt +++ b/app/src/main/java/com/spectre7/spmp/PlayerHost.kt @@ -10,8 +10,10 @@ import android.os.Binder import android.os.IBinder import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.session.MediaSessionCompat -import android.util.Log import android.view.KeyEvent +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.media.session.MediaButtonReceiver @@ -24,46 +26,42 @@ import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.ui.PlayerNotificationManager import com.spectre7.spmp.api.DataApi import com.spectre7.spmp.model.Song +import com.spectre7.spmp.ui.layout.PlayerStatus import com.spectre7.utils.sendToast import kotlin.concurrent.thread enum class SERVICE_INTENT_ACTIONS { STOP, BUTTON_VOLUME } -class PlayerHost(private var context: Context) { +class PlayerHost { + + private lateinit var service: PlayerService + private var service_connected by mutableStateOf(false) - private var service: PlayerService? = null - private var service_bound: Boolean = false private var service_connection: ServiceConnection? = null private var service_intent: Intent? = null + private val context: Context + get() = MainActivity.context init { - PlayerHost.instance = this + instance = this + getService() } companion object { private lateinit var instance: PlayerHost - private val service: PlayerService? + + lateinit var p_status: PlayerStatus + + val service: PlayerService get() = instance.service + val player: ExoPlayer + get() = service.player + val service_connected: Boolean + get() = instance.service_connected fun release() { instance.release() } - - fun interact(action: (player: ExoPlayer) -> Unit) { - interactService { - action(it.player) - } - } - fun interactService(action: (service: PlayerService) -> Unit) { - if (service == null) { - instance.getService { - action(service!!) - } - } - else { - action(service!!) - } - } } private fun release() { @@ -79,17 +77,20 @@ class PlayerHost(private var context: Context) { context.startForegroundService(service_intent) } + println("binding service") + context.bindService(service_intent, object : ServiceConnection { override fun onServiceConnected(className: ComponentName, binder: IBinder) { + println("service connected") service = (binder as PlayerService.PlayerBinder).getService() - service_bound = true + service_connected = true on_connected?.invoke() } override fun onServiceDisconnected(arg0: ComponentName) { - service_bound = false + service_connected = false } }, 0) diff --git a/app/src/main/java/com/spectre7/spmp/api/DataApi.kt b/app/src/main/java/com/spectre7/spmp/api/DataApi.kt index 27e64d4e2..596eb5ac4 100644 --- a/app/src/main/java/com/spectre7/spmp/api/DataApi.kt +++ b/app/src/main/java/com/spectre7/spmp/api/DataApi.kt @@ -348,7 +348,7 @@ class DataApi { fun getSongLyrics(song: Song, callback: (Song.Lyrics?) -> Unit) { thread { val params = mapOf("title" to song.title, "artist" to song.artist.nativeData.name) - val result = queryServer("/lyrics", parameters = params) + val result = queryServer("/lyrics", parameters = params, max_retries = 15) if (result == null) { callback(null) @@ -500,7 +500,14 @@ class DataApi { var request = getRequest(_tunnel) fun getResult(): Response? { - val ret = OkHttpClient.Builder().readTimeout(timeout, TimeUnit.SECONDS).build().newCall(request).execute() + val ret: Response + try { + ret = OkHttpClient.Builder().readTimeout(timeout, TimeUnit.SECONDS).build().newCall(request).execute() + } + catch (e: java.net.ConnectException) { + println(e) + return null + } if (ret.code == 401) { throw RuntimeException("Server API key is invalid") } @@ -527,7 +534,7 @@ class DataApi { } if (result == null && throw_on_fail) { - throw RuntimeException("Request to server failed\nURL: ${request.url}") + throw RuntimeException("Request to server failed. Request URL: ${request.url}") } } @@ -547,25 +554,9 @@ class DataApi { } } - fun getRecommendedFeed(): List? { - val data = queryServer("/feed", timeout=30) - if (data != null) { - return klaxon.parseArray(data) - } - else { - // TODO (this is temporary, maybe use radio of recent songs?) - // If server fetch fails, use fallback method - val songs = listOf( - Pair("XqKbuEDvaf8", "Quick picks"), - Pair("wPS_x6FiBx0", "Listen again"), - Pair("rgNdeflYdYw", "Listen again"), - Pair("KnPUJwZNV8Y", "Listen again"), - ) - return List(songs.size) { song -> - val radio = getSongRadio(songs[song].first) - RecommendedFeedRow(songs[song].second, "(fallback)", List(radio.size) { RecommendedFeedRow.Item("song", radio[it]) }) - } - } + fun getRecommendedFeed(): List { + val data = queryServer("/feed", timeout=30, throw_on_fail = true)!! + return klaxon.parseArray(data)!! } } } diff --git a/app/src/main/java/com/spectre7/spmp/api/TextWithReading.kt b/app/src/main/java/com/spectre7/spmp/api/TextWithReading.kt index 5d990176c..fb35101d0 100644 --- a/app/src/main/java/com/spectre7/spmp/api/TextWithReading.kt +++ b/app/src/main/java/com/spectre7/spmp/api/TextWithReading.kt @@ -10,8 +10,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.SubcomposeLayout import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.layout.positionInRoot import androidx.compose.ui.platform.LocalDensity @@ -23,12 +25,29 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.* class TermInfo(val text: String, val data: Any?) { - var position: Offset = Offset(0f, 0f) + var rect: Rect = Rect(0f, 0f, 0f, 0f) +} + +@Composable +fun MeasureUnconstrainedView( + viewToMeasure: @Composable () -> Unit, + content: @Composable (width: Int, height: Int) -> Unit, +) { + SubcomposeLayout { constraints -> + val measured = subcompose("viewToMeasure", viewToMeasure)[0] + .measure(Constraints()) + + val contentPlaceable = subcompose("content") { + content(measured.width, measured.height) + }[0].measure(constraints) + + layout(contentPlaceable.width, contentPlaceable.height) { + contentPlaceable.place(0, 0) + } + } } @Composable @@ -61,7 +80,6 @@ fun TextWithReading( return buildAnnotatedString { var child_index = 0 - var children_length = 0 for (elem in textContent) { val text = elem.text @@ -91,43 +109,43 @@ fun TextWithReading( val boxHeight = with(LocalDensity.current) { readingFontSize.toDp() } val index = remember { child_index++ } - // LaunchedEffect(Unit) { - // if (text_positions != null) { - // text_positions[index].index = children_length - // children_length += text.length - // } - // } - - val column_modifier = remember(text_positions == null) { - Modifier.fillMaxHeight().run { - if (text_positions != null) { - onPlaced { coords -> - text_positions[index].position = coords.positionInRoot() + val textElement = @Composable { + Text(text = text, fontSize = _fontSize, color = color) + } + + MeasureUnconstrainedView(textElement) { width: Int, height: Int -> + val column_modifier = remember(text_positions == null) { + Modifier.fillMaxHeight().run { + if (text_positions != null) { + onPlaced { coords -> + text_positions[index].rect = Rect(coords.positionInRoot(), Size(width.toFloat(), height.toFloat())) + } + } + else { + this } - } - else { - this } } - } - Column( - modifier = column_modifier, - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Bottom, - ) { - Box(modifier = Modifier.requiredHeight(boxHeight + 3.dp)) { - if (showReadings && reading != null) { - Text( - modifier = Modifier.wrapContentWidth(unbounded = true), - text = reading, - style = TextStyle.Default.copy(fontSize = readingFontSize), - color = color - ) + Column( + modifier = column_modifier, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Bottom, + ) { + Box(modifier = Modifier.requiredHeight(boxHeight + 3.dp)) { + if (showReadings && reading != null) { + Text( + modifier = Modifier.wrapContentWidth(unbounded = true), + text = reading, + style = TextStyle.Default.copy(fontSize = readingFontSize), + color = color + ) + } } + textElement() } - Text(text = text, fontSize = _fontSize, color = color) } + } ) } diff --git a/app/src/main/java/com/spectre7/spmp/model/Artist.kt b/app/src/main/java/com/spectre7/spmp/model/Artist.kt index eba7f4ae3..da12b13b2 100644 --- a/app/src/main/java/com/spectre7/spmp/model/Artist.kt +++ b/app/src/main/java/com/spectre7/spmp/model/Artist.kt @@ -4,8 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import com.spectre7.spmp.api.DataApi -import com.spectre7.spmp.ui.components.ArtistPreview -import com.spectre7.spmp.MainActivity +import com.spectre7.spmp.ui.component.ArtistPreview import java.util.* data class ArtistData ( diff --git a/app/src/main/java/com/spectre7/spmp/model/Song.kt b/app/src/main/java/com/spectre7/spmp/model/Song.kt index d99383655..186599b5d 100644 --- a/app/src/main/java/com/spectre7/spmp/model/Song.kt +++ b/app/src/main/java/com/spectre7/spmp/model/Song.kt @@ -10,12 +10,9 @@ import androidx.compose.ui.graphics.toArgb import androidx.core.content.edit import com.beust.klaxon.Json import com.beust.klaxon.Klaxon -import com.spectre7.ptl.Ptl import com.spectre7.spmp.MainActivity -import com.spectre7.spmp.R import com.spectre7.spmp.api.DataApi -import com.spectre7.spmp.ui.components.SongPreview -import com.spectre7.utils.getString +import com.spectre7.spmp.ui.component.SongPreview import java.io.FileNotFoundException import java.net.URL import java.time.Duration diff --git a/app/src/main/java/com/spectre7/spmp/ui/components/ArtistPreview.kt b/app/src/main/java/com/spectre7/spmp/ui/component/ArtistPreview.kt similarity index 95% rename from app/src/main/java/com/spectre7/spmp/ui/components/ArtistPreview.kt rename to app/src/main/java/com/spectre7/spmp/ui/component/ArtistPreview.kt index 9d8055831..90159cb6b 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/components/ArtistPreview.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/component/ArtistPreview.kt @@ -1,4 +1,4 @@ -package com.spectre7.spmp.ui.components +package com.spectre7.spmp.ui.component import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* @@ -7,7 +7,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Info import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment diff --git a/app/src/main/java/com/spectre7/spmp/ui/components/MultiSelector.kt b/app/src/main/java/com/spectre7/spmp/ui/component/MultiSelector.kt similarity index 96% rename from app/src/main/java/com/spectre7/spmp/ui/components/MultiSelector.kt rename to app/src/main/java/com/spectre7/spmp/ui/component/MultiSelector.kt index 79b7090bf..7c4f8a3e9 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/components/MultiSelector.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/component/MultiSelector.kt @@ -1,4 +1,4 @@ -package com.spectre7.spmp.ui.components +package com.spectre7.spmp.ui.component import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.FastOutSlowInEasing diff --git a/app/src/main/java/com/spectre7/spmp/ui/components/PillMenu.kt b/app/src/main/java/com/spectre7/spmp/ui/component/PillMenu.kt similarity index 99% rename from app/src/main/java/com/spectre7/spmp/ui/components/PillMenu.kt rename to app/src/main/java/com/spectre7/spmp/ui/component/PillMenu.kt index 1bb542492..739f5f9e1 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/components/PillMenu.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/component/PillMenu.kt @@ -1,4 +1,4 @@ -package com.spectre7.spmp.ui.components +package com.spectre7.spmp.ui.component import androidx.compose.animation.* import androidx.compose.animation.core.tween diff --git a/app/src/main/java/com/spectre7/spmp/ui/components/SongPreview.kt b/app/src/main/java/com/spectre7/spmp/ui/component/SongPreview.kt similarity index 87% rename from app/src/main/java/com/spectre7/spmp/ui/components/SongPreview.kt rename to app/src/main/java/com/spectre7/spmp/ui/component/SongPreview.kt index 8a8bef61a..471694d2a 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/components/SongPreview.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/component/SongPreview.kt @@ -1,4 +1,4 @@ -package com.spectre7.spmp.ui.components +package com.spectre7.spmp.ui.component import androidx.compose.foundation.Image import androidx.compose.foundation.clickable @@ -8,7 +8,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import com.spectre7.spmp.model.Song import androidx.compose.runtime.Composable @@ -20,7 +19,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.* import coil.compose.rememberAsyncImagePainter -import com.spectre7.spmp.MainActivity import com.spectre7.spmp.PlayerHost @Composable @@ -29,10 +27,7 @@ fun SongPreview (song: Song, large: Boolean, colour: Color, modifier: Modifier = if (large) { Column( modifier = modifier.padding(10.dp, 0.dp).clickable { - PlayerHost.interactService { - it.playSong(song) - // it.addToQueue(song) - } + PlayerHost.service.playSong(song) } ) { @@ -88,10 +83,8 @@ fun SongPreview (song: Song, large: Boolean, colour: Color, modifier: Modifier = if (!basic) { IconButton(onClick = { - PlayerHost.interactService { - it.addToQueue(song) { - it.play() - } + PlayerHost.service.addToQueue(song) { + PlayerHost.service.play() } }, modifier = Modifier.fillMaxWidth()) { Icon(Icons.Filled.PlayArrow, null, Modifier, colour) diff --git a/app/src/main/java/com/spectre7/spmp/ui/components/DownloadMenu.kt b/app/src/main/java/com/spectre7/spmp/ui/components/DownloadMenu.kt deleted file mode 100644 index 04049383e..000000000 --- a/app/src/main/java/com/spectre7/spmp/ui/components/DownloadMenu.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.spectre7.spmp.ui.components - -import android.util.Log -import androidx.compose.animation.Crossfade -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.background -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.spectre7.spmp.R -import com.spectre7.spmp.model.Song -import com.spectre7.utils.* -import com.spectre7.utils.getString -import com.spectre7.spmp.ui.layout.PlayerStatus -import com.spectre7.ptl.Ptl - -@Composable -fun DownloadMenu(song: Song, on_close_request: () -> Unit) { - Column(Modifier.fillMaxSize()) { - Text("Download") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/spectre7/spmp/ui/layout/DownloadMenu.kt b/app/src/main/java/com/spectre7/spmp/ui/layout/DownloadMenu.kt new file mode 100644 index 000000000..bd6fcdc93 --- /dev/null +++ b/app/src/main/java/com/spectre7/spmp/ui/layout/DownloadMenu.kt @@ -0,0 +1,14 @@ +package com.spectre7.spmp.ui.layout + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import com.spectre7.spmp.model.Song + +@Composable +fun DownloadMenu(song: Song, on_close_request: () -> Unit) { + Column(Modifier.fillMaxSize()) { + Text("Download") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/spectre7/spmp/ui/components/LyricsDisplay.kt b/app/src/main/java/com/spectre7/spmp/ui/layout/LyricsDisplay.kt similarity index 59% rename from app/src/main/java/com/spectre7/spmp/ui/components/LyricsDisplay.kt rename to app/src/main/java/com/spectre7/spmp/ui/layout/LyricsDisplay.kt index a4a7cb05f..5054e34f0 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/components/LyricsDisplay.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/layout/LyricsDisplay.kt @@ -1,11 +1,10 @@ -package com.spectre7.spmp.ui.components +package com.spectre7.spmp.ui.layout import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.Canvas -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* @@ -14,12 +13,14 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Info import androidx.compose.material.ripple.rememberRipple -import androidx.compose.material3.* +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -27,39 +28,27 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex -import com.chaquo.python.PyObject -import com.chaquo.python.Python -import com.spectre7.ptl.Ptl import com.spectre7.spmp.MainActivity +import com.spectre7.spmp.PlayerHost import com.spectre7.spmp.R import com.spectre7.spmp.model.Song -import com.spectre7.spmp.ui.layout.PlayerStatus +import com.spectre7.spmp.ui.component.PillMenu import com.spectre7.utils.getString -import com.spectre7.utils.hasKanjiAndHiragana -import com.spectre7.utils.isKanji import com.spectre7.utils.sendToast import net.zerotask.libraries.android.compose.furigana.TermInfo import net.zerotask.libraries.android.compose.furigana.TextData import net.zerotask.libraries.android.compose.furigana.TextWithReading -import kotlin.concurrent.thread @Composable -fun LyricsDisplay(song: Song, on_close_request: () -> Unit, p_status: PlayerStatus, size: Dp, open_shutter_menu: (@Composable () -> Unit) -> Unit) { +fun LyricsDisplay(song: Song, on_close_request: () -> Unit, size: Dp, open_shutter_menu: (@Composable () -> Unit) -> Unit) { var lyrics: Song.Lyrics? by remember { mutableStateOf(null) } var show_furigana: Boolean by remember { mutableStateOf(false) } - var t_first_word: Ptl.TimedLyrics.Word? by remember { mutableStateOf(null) } - - var t_word_start by remember { mutableStateOf(-1) } - var t_word_end by remember { mutableStateOf(-1) } val text_positions = remember { mutableStateListOf() } - val overlay_terms: MutableList = remember { mutableStateListOf() } LaunchedEffect(song.getId()) { lyrics = null - t_first_word = null - overlay_terms.clear() song.getLyrics { if (it == null) { sendToast(getString(R.string.lyrics_unavailable)) @@ -67,70 +56,6 @@ fun LyricsDisplay(song: Song, on_close_request: () -> Unit, p_status: PlayerStat } else { lyrics = it -// if (lyrics is Song.PTLyrics && (lyrics as Song.PTLyrics).getTimed() != null) { -// t_first_word = (lyrics as Song.PTLyrics).getTimed()!!.first_word -// } - } - } - } - - LaunchedEffect(p_status.position) { - return@LaunchedEffect - if (t_first_word != null) { - - thread { - val pos = (p_status.duration * p_status.position) + 0.15 - - var word = t_first_word - - var start = -1 - var end = -1 - - while(true) { - if (pos >= word!!.start_time && pos < word.end_time) { - if (start == -1) { - if (t_word_start == word.index) { - break - } - start = word.index - } - } - else if (start != -1) { - end = word.index - 1 - break - } - - if (word.next_word == null) { - if (start != -1) { - end = word.index - } - break - } - - word = word.next_word - } - - if ((start != t_word_start || end != t_word_end) && start != -1 && end != -1) { - t_word_start = start - t_word_end = end - - overlay_terms.clear() - for (term in text_positions) { - val word = term.data as Ptl.TimedLyrics.Word? - if (word == null) { - break - } - - if (word.index >= t_word_start && word.index <= t_word_end) { - if (term.text.trim().length > 0) { - overlay_terms.add(term) - } - } - else if (word.index > t_word_end) { - break - } - } - } } } } @@ -169,10 +94,10 @@ fun LyricsDisplay(song: Song, on_close_request: () -> Unit, p_status: PlayerStat } } }, - null, + remember { mutableStateOf(false) }, MainActivity.theme.getAccent(), MainActivity.theme.getOnAccent(), - vertical = true, top = true + vertical = true ) } @@ -191,12 +116,7 @@ fun LyricsDisplay(song: Song, on_close_request: () -> Unit, p_status: PlayerStat } } else { - val offset = Offset(-75f, -150f) - Canvas(modifier = Modifier.fillMaxSize()) { - for (term in overlay_terms) { - drawRect(Color.Red, term.position + offset, Rect(0f, 0f, 50f, 50f).size) - } - } + TimingOverlay(it, text_positions, song.getId()) Column { val terms = mutableListOf() for (line in it.lyrics) { @@ -225,41 +145,6 @@ fun LyricsDisplay(song: Song, on_close_request: () -> Unit, p_status: PlayerStat } } -fun trimOkurigana(original: String, furigana: String): List> { - if (original.hasKanjiAndHiragana()) { - var trim_amount: Int = 0 - for (i in 1 until furigana.length + 1) { - if (original[original.length - i].isKanji() || original[original.length - i] != furigana[furigana.length - i]) { - trim_amount = i - 1 - break - } - } - - if (trim_amount != 0) { - return listOf( - Pair(original.slice(0 until original.length - trim_amount), furigana.slice(0 until furigana.length - trim_amount)), - Pair(original.slice(original.length - trim_amount until original.length), null) - ) - } - } - - return listOf( - Pair(original, furigana) - ) -} - -val kakasi = Python.getInstance().getModule("pykakasi").callAttr("Kakasi") -fun getFuriganaTerms(text: String): List> { - val ret = mutableListOf>() - fun getKey(term: PyObject, key: String): String { - return term.callAttr("get", key).toString().replace("\\n", "\n").replace("\\r", "\r") - } - for (term in kakasi.callAttr("convert", text.replace("\n", "\\n").replace("\r", "\\r")).asList()) { - ret.add(Triple(getKey(term, "orig"), getKey(term, "hira"), getKey(term, "kana"))) - } - return ret -} - @Composable fun FuriganaText(terms: List, show_furigana: Boolean, text_positions: MutableList? = null) { Crossfade(targetState = show_furigana) { @@ -275,3 +160,40 @@ fun FuriganaText(terms: List, show_furigana: Boolean, text_positions: ) } } + +@Composable +fun TimingOverlay(lyrics: Song.Lyrics, text_positions: List, reset_state: Any) { + val overlay_terms: MutableList = remember { mutableStateListOf() } + + Canvas(modifier = Modifier.fillMaxSize()) { + for (term in overlay_terms) { + drawRect(Color.Red, term.rect.topLeft + Offset(-80f, -160f), term.rect.size) + } + } + + LaunchedEffect(reset_state) { + overlay_terms.clear() + } + + val p_status = PlayerHost.p_status + LaunchedEffect(p_status.position) { + val pos = (p_status.duration * p_status.position) + + val lines = lyrics.lyrics + var term_index = 0 + + overlay_terms.clear() + for (i in lines.indices) { + val line = lines[i] + for (j in line.indices) { + val term = line[j] + for (subterm in term.subterms) { + if (pos >= term.start && pos < term.end) { + overlay_terms.add(text_positions[term_index]) + } + term_index++ + } + } + } + } +} diff --git a/app/src/main/java/com/spectre7/spmp/ui/components/NowPlaying.kt b/app/src/main/java/com/spectre7/spmp/ui/layout/NowPlaying.kt similarity index 77% rename from app/src/main/java/com/spectre7/spmp/ui/components/NowPlaying.kt rename to app/src/main/java/com/spectre7/spmp/ui/layout/NowPlaying.kt index b0bbfb3ea..a72c2321a 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/components/NowPlaying.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/layout/NowPlaying.kt @@ -1,4 +1,4 @@ -package com.spectre7.spmp.ui.components +package com.spectre7.spmp.ui.layout import android.content.SharedPreferences.OnSharedPreferenceChangeListener import androidx.activity.compose.BackHandler @@ -20,7 +20,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.* import androidx.compose.ui.graphics.painter.Painter @@ -40,27 +39,27 @@ import com.google.android.exoplayer2.Player import com.spectre7.spmp.MainActivity import com.spectre7.spmp.PlayerHost import com.spectre7.spmp.R -import com.spectre7.spmp.ui.layout.MINIMISED_NOW_PLAYING_HEIGHT -import com.spectre7.spmp.ui.layout.PlayerStatus +import com.spectre7.spmp.ui.component.* import com.spectre7.utils.* import kotlin.concurrent.thread import kotlin.math.max - enum class AccentColourSource { THUMBNAIL, SYSTEM } enum class ThemeMode { BACKGROUND, ELEMENTS, NONE } enum class NowPlayingOverlayMenu { NONE, MAIN, PALETTE, LYRICS, DOWNLOAD } enum class NowPlayingTab { RELATED, PLAYER, QUEUE } -const val SEEK_CANCEL_THRESHOLD = 0.05f +const val SEEK_CANCEL_THRESHOLD = 0.03f @Composable -fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, close: () -> Unit) { +fun NowPlaying(_expansion: Float, max_height: Float, close: () -> Unit) { val expansion = if (_expansion < 0.08f) 0.0f else _expansion val inv_expansion = -expansion + 1.0f + val p_status = remember { PlayerHost.p_status } + fun getSongTitle(): String { if (p_status.song == null) { return "-----" @@ -245,14 +244,12 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo if (tab == NowPlayingTab.PLAYER) { Spacer(Modifier.requiredHeight(50.dp * expansion)) - val min_height_fraction = (MINIMISED_NOW_PLAYING_HEIGHT + 20f) / max_height - Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier .fillMaxWidth() - .fillMaxHeight(0.5f * max(expansion, min_height_fraction)) + .fillMaxHeight(0.5f * max(expansion, (MINIMISED_NOW_PLAYING_HEIGHT + 20f) / max_height)) ) { var overlay_menu by remember { mutableStateOf(NowPlayingOverlayMenu.NONE) } @@ -352,7 +349,7 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo } NowPlayingOverlayMenu.LYRICS -> if (p_status.song != null) { - LyricsDisplay(p_status.song!!, { overlay_menu = NowPlayingOverlayMenu.NONE }, p_status, (screen_width_dp - (main_padding * 2) - (15.dp * expansion * 2)).value * 0.9.dp) { + LyricsDisplay(p_status.song!!, { overlay_menu = NowPlayingOverlayMenu.NONE }, (screen_width_dp - (main_padding * 2) - (15.dp * expansion * 2)).value * 0.9.dp) { get_shutter_menu = it shutter_menu_open = true } @@ -419,9 +416,7 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo AnimatedVisibility(p_status.has_previous, enter = expandHorizontally(), exit = shrinkHorizontally()) { IconButton( onClick = { - PlayerHost.interact { - it.seekToPreviousMediaItem() - } + PlayerHost.player.seekToPreviousMediaItem() } ) { Image( @@ -435,9 +430,7 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo AnimatedVisibility(p_status.song != null, enter = fadeIn(), exit = fadeOut()) { IconButton( onClick = { - PlayerHost.interactService { - it.playPause() - } + PlayerHost.service.playPause() } ) { Image( @@ -451,9 +444,7 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo AnimatedVisibility(p_status.has_next, enter = expandHorizontally(), exit = shrinkHorizontally()) { IconButton( onClick = { - PlayerHost.interact { - it.seekToNextMediaItem() - } + PlayerHost.player.seekToNextMediaItem() } ) { Image( @@ -510,7 +501,6 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo } Column(verticalArrangement = Arrangement.spacedBy(35.dp)) { - Column(verticalArrangement = Arrangement.spacedBy(5.dp)) { // Title text @@ -537,123 +527,10 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo } - var slider_moving by remember { mutableStateOf(false) } - var slider_value by remember { mutableStateOf(0.0f) } - var old_p_position by remember { mutableStateOf(null) } - var slider_start_position by remember { mutableStateOf(null) } - - LaunchedEffect(p_status.position) { - if (!slider_moving && p_status.position != old_p_position) { - slider_value = p_status.position - old_p_position = null - } + SeekBar { + PlayerHost.player.seekTo((PlayerHost.player.duration * it).toLong()) } - @Composable - fun SeekTrack( - modifier: Modifier, - progress: Float, - enabled: Boolean, - track_colour: Color = Color(0xffD3B4F7), - progress_colour: Color = Color(0xff7000F8), - height: Dp = 4.dp, - highlight: Pair? = null, - highlight_colour: Color = setColourAlpha(Color.Red, 0.2) - ) { - Canvas( - Modifier - .then(modifier) - .height(height) - ) { - - val isRtl = layoutDirection == LayoutDirection.Rtl - val slider_left = Offset(0f, center.y) - val slider_right = Offset(size.width, center.y) - val slider_start = if (isRtl) slider_right else slider_left - val slider_end = if (isRtl) slider_left else slider_right - drawLine( - track_colour, - slider_start, - slider_end, - size.height, - StrokeCap.Round, - alpha = if (enabled) 1f else 0.6f - ) - - val slider_value_end = Offset( - slider_start.x + (slider_end.x - slider_start.x) * progress, - center.y - ) - val slider_value_start = Offset( - slider_start.x, - center.y - ) - drawLine( - progress_colour, - slider_value_start, - slider_value_end, - size.height, - StrokeCap.Round, - alpha = if (enabled) 1f else 0.6f - ) - - if (highlight != null) { - drawLine( - highlight_colour, - Offset(size.width * highlight.first, center.y), - Offset(size.width * highlight.second, center.y), - size.height, - StrokeCap.Square, - alpha = if (enabled) 1f else 0.6f - ) - } - } - } - - // Song position seek bar - var in_cancel_area by remember { mutableStateOf(false) } - val highlight = if (slider_start_position != null) Pair( - slider_start_position!! - SEEK_CANCEL_THRESHOLD / 2.0f, slider_start_position!! + SEEK_CANCEL_THRESHOLD / 2.0f - ) else null - - SliderValueHorizontal( - value = slider_value, - onValueChange = { - if (slider_start_position == null) { - slider_start_position = slider_value - } - - slider_moving = true - slider_value = it - - if (in_cancel_area != (slider_value >= slider_start_position!! - SEEK_CANCEL_THRESHOLD / 2.0 && slider_value <= slider_start_position!! + SEEK_CANCEL_THRESHOLD / 2.0)) { - in_cancel_area = !in_cancel_area - if (in_cancel_area) { - vibrate(0.01) - } - } - - }, - onValueChangeFinished = { - slider_moving = false - old_p_position = p_status.position - slider_start_position = null - - if (!in_cancel_area) { - PlayerHost.interact { - it.seekTo((it.duration * slider_value).toLong()) - } - } - else { - vibrate(0.01) - in_cancel_area = false - } - }, - thumbSizeInDp = DpSize(12.dp, 12.dp), - track = { a, b, _, _, c -> SeekTrack(a, b, c, setColourAlpha(MainActivity.theme.getOnBackground(true), 0.5), MainActivity.theme.getOnBackground(true), highlight = highlight) }, - thumb = { a, b, c, d, e -> DefaultThumb(a, b, c, d, e, MainActivity.theme.getOnBackground(true), 1f) } - ) - Row( horizontalArrangement = Arrangement.Center, modifier = Modifier @@ -665,32 +542,24 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo // Toggle shuffle PlayerButton(R.drawable.ic_shuffle, button_size * 0.65f, if (p_status.shuffle) 1f else 0.25f) { - PlayerHost.interact { - it.shuffleModeEnabled = !it.shuffleModeEnabled - } + PlayerHost.player.shuffleModeEnabled = !PlayerHost.player.shuffleModeEnabled } Spacer(Modifier.requiredWidth(utility_separation)) // Previous PlayerButton(R.drawable.ic_skip_previous, enabled = p_status.has_previous) { - PlayerHost.interact { - it.seekToPreviousMediaItem() - } + PlayerHost.player.seekToPreviousMediaItem() } // Play / pause PlayerButton(if (p_status.playing) R.drawable.ic_pause else R.drawable.ic_play_arrow, enabled = p_status.song != null) { - PlayerHost.interactService { - it.playPause() - } + PlayerHost.service.playPause() } // Next PlayerButton(R.drawable.ic_skip_next, enabled = p_status.has_next) { - PlayerHost.interact { - it.seekToNextMediaItem() - } + PlayerHost.player.seekToNextMediaItem() } Spacer(Modifier.requiredWidth(utility_separation)) @@ -701,12 +570,10 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo button_size * 0.65f, if (p_status.repeat_mode != Player.REPEAT_MODE_OFF) 1f else 0.25f ) { - PlayerHost.interact { - it.repeatMode = when (it.repeatMode) { - Player.REPEAT_MODE_ALL -> Player.REPEAT_MODE_ONE - Player.REPEAT_MODE_ONE -> Player.REPEAT_MODE_OFF - else -> Player.REPEAT_MODE_ALL - } + PlayerHost.player.repeatMode = when (PlayerHost.player.repeatMode) { + Player.REPEAT_MODE_ALL -> Player.REPEAT_MODE_ONE + Player.REPEAT_MODE_ONE -> Player.REPEAT_MODE_OFF + else -> Player.REPEAT_MODE_ALL } } } @@ -815,3 +682,117 @@ fun NowPlaying(_expansion: Float, max_height: Float, p_status: PlayerStatus, clo // } // } // } + +@Composable +fun SeekBar(seek: (Float) -> Unit) { + val p_status = remember { PlayerHost.p_status } + var position_override by remember { mutableStateOf(null) } + var old_position by remember { mutableStateOf(null) } + var grab_start_position by remember { mutableStateOf(null) } + + @Composable + fun SeekTrack( + modifier: Modifier, + progress: Float, + enabled: Boolean, + track_colour: Color = Color(0xffD3B4F7), + progress_colour: Color = Color(0xff7000F8), + height: Dp = 4.dp, + highlight_colour: Color = setColourAlpha(Color.Red, 0.2) + ) { + Canvas( + Modifier + .then(modifier) + .height(height) + ) { + + val left = Offset(0f, center.y) + val right = Offset(size.width, center.y) + val start = if (layoutDirection == LayoutDirection.Rtl) right else left + val end = if (layoutDirection == LayoutDirection.Rtl) left else right + + drawLine( + track_colour, + start, + end, + size.height, + StrokeCap.Round, + alpha = if (enabled) 1f else 0.6f + ) + + drawLine( + progress_colour, + Offset( + start.x, + center.y + ), + Offset( + start.x + (end.x - start.x) * progress, + center.y + ), + size.height, + StrokeCap.Round, + alpha = if (enabled) 1f else 0.6f + ) + + if (grab_start_position != null) { + drawLine( + highlight_colour, + Offset(size.width * (grab_start_position!! - SEEK_CANCEL_THRESHOLD / 2.0f), center.y), + Offset(size.width * (grab_start_position!! + SEEK_CANCEL_THRESHOLD / 2.0f), center.y), + size.height, + StrokeCap.Square, + alpha = if (enabled) 1f else 0.6f + ) + } + } + } + + var cancel_area_side: Int? by remember { mutableStateOf(null) } + + fun getSliderValue(): Float { + if (position_override != null && old_position != null) { + if (p_status.position != old_position) { + old_position = null + position_override = null + } + else { + return position_override!! + } + } + return position_override ?: p_status.position + } + + SliderValueHorizontal( + value = getSliderValue(), + onValueChange = { + if (grab_start_position == null) { + grab_start_position = p_status.position + } + + position_override = it + + val side = if (it <= grab_start_position!! - SEEK_CANCEL_THRESHOLD / 2.0) -1 else if (it >= grab_start_position!! + SEEK_CANCEL_THRESHOLD / 2.0) 1 else 0 + if (side != cancel_area_side) { + if (side == 0 || side + (cancel_area_side ?: 0) == 0) { + vibrate(0.01) + } + cancel_area_side = side + } + }, + onValueChangeFinished = { + if (cancel_area_side == 0) { + vibrate(0.01) + } + else { + seek(position_override!!) + } + old_position = p_status.position + grab_start_position = null + cancel_area_side = null + }, + thumbSizeInDp = DpSize(12.dp, 12.dp), + track = { a, b, _, _, c -> SeekTrack(a, b, c, setColourAlpha(MainActivity.theme.getOnBackground(true), 0.5), MainActivity.theme.getOnBackground(true)) }, + thumb = { a, b, c, d, e -> DefaultThumb(a, b, c, d, e, MainActivity.theme.getOnBackground(true), 1f) } + ) +} diff --git a/app/src/main/java/com/spectre7/spmp/ui/components/NowPlayingPaletteSelector.kt b/app/src/main/java/com/spectre7/spmp/ui/layout/NowPlayingPaletteSelector.kt similarity index 95% rename from app/src/main/java/com/spectre7/spmp/ui/components/NowPlayingPaletteSelector.kt rename to app/src/main/java/com/spectre7/spmp/ui/layout/NowPlayingPaletteSelector.kt index 0d0fd0a5c..b4c96f48b 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/components/NowPlayingPaletteSelector.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/layout/NowPlayingPaletteSelector.kt @@ -1,4 +1,4 @@ -package com.spectre7.spmp.ui.components +package com.spectre7.spmp.ui.layout import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/com/spectre7/spmp/ui/components/NowPlayingQueueTab.kt b/app/src/main/java/com/spectre7/spmp/ui/layout/NowPlayingQueueTab.kt similarity index 81% rename from app/src/main/java/com/spectre7/spmp/ui/components/NowPlayingQueueTab.kt rename to app/src/main/java/com/spectre7/spmp/ui/layout/NowPlayingQueueTab.kt index 6af7d169a..58487160c 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/components/NowPlayingQueueTab.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/layout/NowPlayingQueueTab.kt @@ -1,14 +1,9 @@ -package com.spectre7.spmp.ui.components +package com.spectre7.spmp.ui.layout -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.calculateTargetValue -import androidx.compose.animation.splineBasedDecay import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.awaitFirstDown -import androidx.compose.foundation.gestures.horizontalDrag import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape @@ -27,10 +22,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.vector.rememberVectorPainter -import androidx.compose.ui.input.pointer.consumePositionChange -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.input.pointer.positionChange -import androidx.compose.ui.input.pointer.util.VelocityTracker import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.IntOffset @@ -38,7 +29,6 @@ import androidx.compose.ui.unit.dp import com.google.android.exoplayer2.C import com.spectre7.spmp.PlayerHost import com.spectre7.spmp.model.Song -import com.spectre7.spmp.ui.layout.PlayerStatus import com.spectre7.utils.getContrasted import com.spectre7.utils.vibrate import org.burnoutcrew.reorderable.ReorderableItem @@ -83,9 +73,7 @@ fun QueueTab(p_status: PlayerStatus, on_background_colour: Color) { Modifier .weight(1f) .clickable { - PlayerHost.interact { - it.seekTo(index, C.TIME_UNSET) - } + PlayerHost.player.seekTo(index, C.TIME_UNSET) } .swipeable( swipe_state, @@ -146,13 +134,9 @@ fun QueueTab(p_status: PlayerStatus, on_background_colour: Color) { // } DisposableEffect(Unit) { - PlayerHost.interactService { - it.addQueueListener(queue_listener) - } + PlayerHost.service.addQueueListener(queue_listener) onDispose { - PlayerHost.interactService { - it.removeQueueListener(queue_listener) - } + PlayerHost.service.removeQueueListener(queue_listener) } } @@ -164,9 +148,7 @@ fun QueueTab(p_status: PlayerStatus, on_background_colour: Color) { }, onDragEnd = { from, to -> if (from != to) { - PlayerHost.interact { - it.moveMediaItem(from, to) - } + PlayerHost.player.moveMediaItem(from, to) playing_key = null } } @@ -193,9 +175,7 @@ fun QueueTab(p_status: PlayerStatus, on_background_colour: Color) { song_items = song_items.toMutableList().apply { removeAt(index) } - PlayerHost.interactService { - it.removeFromQueue(index) - } + PlayerHost.service.removeFromQueue(index) } } } @@ -204,9 +184,7 @@ fun QueueTab(p_status: PlayerStatus, on_background_colour: Color) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically) { Button( onClick = { - PlayerHost.interactService { - it.clearQueue() - } + PlayerHost.service.clearQueue() }, colors = ButtonDefaults.buttonColors( containerColor = Color.Transparent @@ -220,9 +198,8 @@ fun QueueTab(p_status: PlayerStatus, on_background_colour: Color) { Button( onClick = { - PlayerHost.interactService { - // TODO - } + // TODO +// PlayerHost.service. }, colors = ButtonDefaults.buttonColors( containerColor = Color.Transparent diff --git a/app/src/main/java/com/spectre7/spmp/ui/layout/PlayerView.kt b/app/src/main/java/com/spectre7/spmp/ui/layout/PlayerView.kt index f5d2da668..9ec40f564 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/layout/PlayerView.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/layout/PlayerView.kt @@ -20,6 +20,7 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.platform.LocalConfiguration @@ -27,6 +28,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.Player import com.spectre7.spmp.MainActivity @@ -35,15 +37,15 @@ import com.spectre7.spmp.R import com.spectre7.spmp.api.DataApi import com.spectre7.spmp.model.Previewable import com.spectre7.spmp.model.Song -import com.spectre7.spmp.ui.components.NowPlaying -import com.spectre7.spmp.ui.components.PillMenu +import com.spectre7.spmp.ui.component.PillMenu import com.spectre7.utils.getContrasted import com.spectre7.utils.getString import com.spectre7.utils.setAlpha import kotlinx.coroutines.delay -import java.net.UnknownHostException import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.thread +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine fun convertPixelsToDp(px: Int): Float { return px.toFloat() / (MainActivity.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT) @@ -66,8 +68,8 @@ class PlayerStatus { var index: Int by mutableStateOf(0) var queue: MutableList by mutableStateOf(mutableListOf()) var playing: Boolean by mutableStateOf(false) - var position: Float by mutableStateOf(0.0f) - var duration: Float by mutableStateOf(0.0f) + var position: Float by mutableStateOf(0f) + var duration: Float by mutableStateOf(0f) var shuffle: Boolean by mutableStateOf(false) var repeat_mode: Int by mutableStateOf(Player.REPEAT_MODE_OFF) var has_next: Boolean by mutableStateOf(false) @@ -139,9 +141,7 @@ fun PlayerView() { val refresh_mutex = remember { ReentrantLock() } fun refreshFeed() { - println("REFRESH") if (refresh_mutex.isLocked) { - println("REFRESH LOCKED") return } @@ -217,22 +217,37 @@ fun PlayerView() { AnimatedVisibility(expand) { var msg = "Error: ${error.javaClass.simpleName}" + - "\nMessage: ${error.message}" + - "\nCause: ${error.cause}" + - "\nStack trace: ${error.stackTrace}" - Text( - msg, - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth(0.6f), - fontSize = 15.sp - ) + "\n\nMessage: ${error.message}" + + "\n\nCause: ${error.cause}" + + "\n\nStack trace: ${error.stackTrace.asList()}" + LazyColumn(Modifier.fillMaxHeight(0.4f)) { + item { + Text( + msg, + textAlign = TextAlign.Left, + modifier = Modifier.fillMaxWidth(0.9f), + fontSize = 12.sp + ) + } + } } } } else { - LazyColumn(Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(20.dp)) { - items(rows.size) { index -> - SongList(rows[index]) + Crossfade(rows.isNotEmpty()) { loaded -> + if (loaded) { + LazyColumn(Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(20.dp)) { + items(rows.size) { index -> + SongList(rows[index]) + } + } + } + else { + Column(Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) { + Text("Loading feed", Modifier.alpha(0.4f), fontSize = 12.sp, color = MainActivity.theme.getOnBackground(false)) + Spacer(Modifier.height(5.dp)) + LinearProgressIndicator(Modifier.alpha(0.4f).fillMaxWidth(0.35f), color = MainActivity.theme.getOnBackground(false)) + } } } } @@ -259,7 +274,7 @@ fun PlayerView() { } val p_status = remember { PlayerStatus() }.also { status -> - PlayerHost.interactService { + PlayerHost.service.also { status.playing = it.player.isPlaying status.position = it.player.currentPosition.toFloat() / it.player.duration.toFloat() status.duration = it.player.duration / 1000f @@ -273,6 +288,7 @@ fun PlayerView() { status.queue.add(song) } } + PlayerHost.p_status = status } val listener = remember { @@ -322,24 +338,48 @@ fun PlayerView() { } DisposableEffect(Unit) { - PlayerHost.interactService { - it.player.addListener(listener) - it.addQueueListener(queue_listener) + PlayerHost.service.apply { + player.addListener(listener) + addQueueListener(queue_listener) } + onDispose { - PlayerHost.interactService { - it.player.removeListener(listener) - it.removeQueueListener(queue_listener) + PlayerHost.service.apply { + player.removeListener(listener) + removeQueueListener(queue_listener) } } } + var player by remember { mutableStateOf(null) } LaunchedEffect(Unit) { - while (true) { - PlayerHost.interact { - p_status.position = it.currentPosition.toFloat() / it.duration.toFloat() + player = PlayerHost.service.player + } +// +// LaunchedEffect(player) { +// if (player != null) { +// while (true) { +// p_status.position = player!!.currentPosition.toFloat() / player!!.duration.toFloat() +// delay(250) +// } +// } +// } + + LaunchedEffect(Unit) { + val poll_job = launch { + while (true) { + delay(100) + if (player == null) { + continue + } + p_status.position = player!!.currentPosition.toFloat() / player!!.duration.toFloat() } - delay(100) + } + try { + suspendCancellableCoroutine { } + } + finally { + poll_job.cancel() } } @@ -372,7 +412,7 @@ fun PlayerView() { ) { switch = !switch }, shape = RectangleShape) { Column(Modifier.fillMaxSize()) { - NowPlaying(swipe_state.offset.value / screen_height, screen_height, p_status) { switch = false } + NowPlaying(swipe_state.offset.value / screen_height, screen_height) { switch = false } } } } diff --git a/app/src/main/java/com/spectre7/spmp/ui/layout/PrefsPage.kt b/app/src/main/java/com/spectre7/spmp/ui/layout/PrefsPage.kt index d80273c30..c7a6c03df 100644 --- a/app/src/main/java/com/spectre7/spmp/ui/layout/PrefsPage.kt +++ b/app/src/main/java/com/spectre7/spmp/ui/layout/PrefsPage.kt @@ -94,8 +94,4 @@ fun PrefsPage(setOverlayPage: (page: OverlayPage) -> Unit) { } } } - - PlayerHost.interact { - it.volume = it.volume * 0.95f - } }