优化: 启动自动随机播放本地歌曲 + 数据设置界面卡顿修复
- 启动无恢复播放/空队列时, 曲库就绪后自动随机播放本地已下载歌曲 (playRandomLocalSong 增加 startup 文案, 与播放完毕随机续播同逻辑, 点歌即取消) - 数据设置: 曲库/下载数统计(逐文件校验)从主线程移到 IO 线程, 界面秒开先显示占位, 统计完成后原位回填副标题(updateSubtitle), 不丢焦点
This commit is contained in:
@@ -300,6 +300,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private val browseDisplayRequests = HashMap<BrowseCacheKey, Int>()
|
private val browseDisplayRequests = HashMap<BrowseCacheKey, Int>()
|
||||||
private var searchScope = "song"
|
private var searchScope = "song"
|
||||||
private var activeSettingsEntries: List<SettingsEntry> = emptyList()
|
private var activeSettingsEntries: List<SettingsEntry> = emptyList()
|
||||||
|
private var activeSettingsAdapter: SettingsListAdapter? = null
|
||||||
private var activeSearchQuery = ""
|
private var activeSearchQuery = ""
|
||||||
private var searchLanguage = "全部"
|
private var searchLanguage = "全部"
|
||||||
private var searchContextId = ""
|
private var searchContextId = ""
|
||||||
@@ -459,6 +460,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
startupSong?.let { restoredSong ->
|
startupSong?.let { restoredSong ->
|
||||||
play(restoredSong)
|
play(restoredSong)
|
||||||
updateBottomBar(restoredSong, true)
|
updateBottomBar(restoredSong, true)
|
||||||
|
} ?: run {
|
||||||
|
// 启动时没有恢复的播放与已点歌曲: 曲库就绪后自动随机播放本地已下载歌曲
|
||||||
|
// (与"点歌列表播放完毕自动随机本地歌曲"同一逻辑), 避免开机后空白等待。
|
||||||
|
startupRandomPending = true
|
||||||
}
|
}
|
||||||
// Keep initialization on the loading state until the external catalog is ready.
|
// Keep initialization on the loading state until the external catalog is ready.
|
||||||
SongApiClient.init(this) // 主线程初始化 JS Bridge
|
SongApiClient.init(this) // 主线程初始化 JS Bridge
|
||||||
@@ -507,6 +512,13 @@ class MainActivity : AppCompatActivity() {
|
|||||||
showHomePage()
|
showHomePage()
|
||||||
if (usedOldDatabase && bootstrapError != null) toast("曲库更新失败,已使用本地曲库")
|
if (usedOldDatabase && bootstrapError != null) toast("曲库更新失败,已使用本地曲库")
|
||||||
if (catalogRefreshPending) refreshCurrentCatalogPage()
|
if (catalogRefreshPending) refreshCurrentCatalogPage()
|
||||||
|
// 启动自动随机播放: 仅当启动时无恢复播放/空队列且允许顺序播放时触发一次。
|
||||||
|
if (startupRandomPending) {
|
||||||
|
startupRandomPending = false
|
||||||
|
if (autoNext && orderQueue!!.isEmpty() && currentSong == null) {
|
||||||
|
main.postDelayed({ playRandomLocalSong(startup = true) }, 600L)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val reason = bootstrapError?.message.orEmpty().ifBlank { "未找到可用曲库" }
|
val reason = bootstrapError?.message.orEmpty().ifBlank { "未找到可用曲库" }
|
||||||
@@ -5140,10 +5152,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已点队列自然播放完毕后,从本机所有有效歌曲中随机续播。
|
* 从本机所有有效歌曲中随机续播(启动自动播放 / 点歌列表播放完毕均走此逻辑)。
|
||||||
* 随机歌曲不写入已点列表;用户此时点歌会立即取消本次随机请求并优先播放已点歌曲。
|
* 随机歌曲不写入已点列表;用户此时点歌会立即取消本次随机请求并优先播放已点歌曲。
|
||||||
*/
|
*/
|
||||||
private fun playRandomLocalSong() {
|
private fun playRandomLocalSong(startup: Boolean = false) {
|
||||||
val previousSongId = currentSong?.let(::stableId) ?: lastRandomLocalSongId
|
val previousSongId = currentSong?.let(::stableId) ?: lastRandomLocalSongId
|
||||||
val requestSerial = ++randomLocalRequestSerial
|
val requestSerial = ++randomLocalRequestSerial
|
||||||
playbackPreparing = true
|
playbackPreparing = true
|
||||||
@@ -5152,7 +5164,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
currentSong = null
|
currentSong = null
|
||||||
clearLyrics()
|
clearLyrics()
|
||||||
updateBottomBar(null, false)
|
updateBottomBar(null, false)
|
||||||
status?.text = "已点播放完成,正在随机本地歌曲..."
|
status?.text = if (startup) "正在随机播放本地歌曲..." else "已点播放完成,正在随机本地歌曲..."
|
||||||
persistRuntimeState()
|
persistRuntimeState()
|
||||||
|
|
||||||
io.execute {
|
io.execute {
|
||||||
@@ -6242,6 +6254,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private var fullScreenPreviousVolume = 70
|
private var fullScreenPreviousVolume = 70
|
||||||
private var randomLocalRequestSerial = 0L
|
private var randomLocalRequestSerial = 0L
|
||||||
private var lastRandomLocalSongId: String? = null
|
private var lastRandomLocalSongId: String? = null
|
||||||
|
/** 启动时无恢复播放/空队列: 曲库就绪后自动随机播放本地已下载歌曲(仅触发一次)。 */
|
||||||
|
private var startupRandomPending = false
|
||||||
private var advancingPlayback = false
|
private var advancingPlayback = false
|
||||||
private var lastAutoAdvanceAtMs = 0L
|
private var lastAutoAdvanceAtMs = 0L
|
||||||
private var fullScreenChromeVisible = false
|
private var fullScreenChromeVisible = false
|
||||||
@@ -8135,7 +8149,12 @@ class MainActivity : AppCompatActivity() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
1 -> listOf(
|
1 -> listOf(
|
||||||
SettingsEntry(R.drawable.ott_ic_data_upgrade, "曲库", "数据库 ${library.muse.songCount()} 首 本地已下载 ${countDownloadedFiles()} 首", "更新") {
|
SettingsEntry(
|
||||||
|
R.drawable.ott_ic_data_upgrade,
|
||||||
|
"曲库",
|
||||||
|
"统计中…",
|
||||||
|
"更新",
|
||||||
|
) {
|
||||||
io.execute { library.muse.close(); library.muse.open(); main.post { showSettingsSection(1, false) } }
|
io.execute { library.muse.close(); library.muse.open(); main.post { showSettingsSection(1, false) } }
|
||||||
},
|
},
|
||||||
SettingsEntry(R.drawable.ott_ic_data_setting, "U盘加歌", "扫描U盘内按规定命名的歌曲文件,并添加到曲库中", "立即加歌") { toast("未检测到U盘") },
|
SettingsEntry(R.drawable.ott_ic_data_setting, "U盘加歌", "扫描U盘内按规定命名的歌曲文件,并添加到曲库中", "立即加歌") { toast("未检测到U盘") },
|
||||||
@@ -8164,9 +8183,25 @@ class MainActivity : AppCompatActivity() {
|
|||||||
songList!!.dividerHeight = dp(12)
|
songList!!.dividerHeight = dp(12)
|
||||||
songList!!.setBackgroundColor(Color.TRANSPARENT)
|
songList!!.setBackgroundColor(Color.TRANSPARENT)
|
||||||
val settingsAdapter = SettingsListAdapter(this, entries, R.id.btn_back)
|
val settingsAdapter = SettingsListAdapter(this, entries, R.id.btn_back)
|
||||||
|
activeSettingsAdapter = settingsAdapter
|
||||||
songList!!.adapter = settingsAdapter
|
songList!!.adapter = settingsAdapter
|
||||||
btnBack?.nextFocusDownId = settingsAdapter.firstActionId
|
btnBack?.nextFocusDownId = settingsAdapter.firstActionId
|
||||||
if (requestInitialFocus) settingsAdapter.requestInitialFocus(songList!!)
|
if (requestInitialFocus) settingsAdapter.requestInitialFocus(songList!!)
|
||||||
|
// 数据设置: 曲库/下载数统计较耗时(逐文件校验), 移到 IO 线程, 避免进入界面卡顿。
|
||||||
|
if (section == 1) refreshLibraryStatsAsync(settingsAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** IO 线程统计曲库歌曲数与本地已下载数, 完成后原位回填"曲库"条副标题。 */
|
||||||
|
private fun refreshLibraryStatsAsync(adapter: SettingsListAdapter) {
|
||||||
|
io.execute {
|
||||||
|
val count = runCatching { library.muse.songCount() }.getOrDefault(0)
|
||||||
|
val downloaded = runCatching { countDownloadedFiles() }.getOrDefault(0)
|
||||||
|
main.post {
|
||||||
|
if (activeSettingsAdapter === adapter) {
|
||||||
|
adapter.updateSubtitle("曲库", "数据库 $count 首 本地已下载 $downloaded 首")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isNetworkConnected(): Boolean {
|
private fun isNetworkConnected(): Boolean {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import android.widget.Switch
|
|||||||
data class SettingsEntry(
|
data class SettingsEntry(
|
||||||
val icon: Int,
|
val icon: Int,
|
||||||
val title: String,
|
val title: String,
|
||||||
val subtitle: String = "",
|
var subtitle: String = "",
|
||||||
val actionText: String = "设置",
|
val actionText: String = "设置",
|
||||||
val checked: Boolean? = null,
|
val checked: Boolean? = null,
|
||||||
val action: () -> Unit,
|
val action: () -> Unit,
|
||||||
@@ -112,6 +112,16 @@ class SettingsListAdapter(
|
|||||||
requestFocusAt(list, 0, onTargetReady)
|
requestFocusAt(list, 0, onTargetReady)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 原位更新某条目的副标题(如异步统计完成后回填), 不重建列表、不丢焦点。 */
|
||||||
|
fun updateSubtitle(title: String, newSubtitle: String) {
|
||||||
|
val index = entries.indexOfFirst { it.title == title }
|
||||||
|
if (index < 0) return
|
||||||
|
val entry = entries[index]
|
||||||
|
if (entry.subtitle == newSubtitle) return
|
||||||
|
entry.subtitle = newSubtitle
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
fun positionForMarker(marker: String): Int? {
|
fun positionForMarker(marker: String): Int? {
|
||||||
val prefix = "focus:settings:"
|
val prefix = "focus:settings:"
|
||||||
if (!marker.startsWith(prefix)) return null
|
if (!marker.startsWith(prefix)) return null
|
||||||
|
|||||||
Reference in New Issue
Block a user