@@ -347,36 +347,49 @@ class MainActivity : AppCompatActivity() {
347347 lifecycleScope.launch {
348348 viewModel.activeIndex.collect { newIndex ->
349349 val oldIndex = lyricAdapter?.activeIndex ? : - 1
350-
351- // Update the adapter's internal state
352350 lyricAdapter?.activeIndex = newIndex
353351
354- // OPTIMIZATION: Only refresh the two lines that changed!
355352 if (oldIndex != - 1 ) lyricAdapter?.notifyItemChanged(oldIndex)
353+
356354 if (newIndex != - 1 ) {
357355 lyricAdapter?.notifyItemChanged(newIndex)
358356
359- // Auto-scroll and Dict Box logic
360- findViewById<RecyclerView >(R .id.lyricRecyclerView).smoothScrollToPosition(
361- newIndex
362- )
357+ // --- CUSTOM CENTERING LOGIC ---
358+ val recyclerView = findViewById<RecyclerView >(R .id.lyricRecyclerView)
359+ val layoutManager = recyclerView.layoutManager as androidx.recyclerview.widget.LinearLayoutManager
360+
361+ // Calculate the middle of the RecyclerView
362+ val offset = recyclerView.height / 2 - 100 // Subtracting 100px for the approximate height of one lyric row
363+
364+ // This force-scrolls the index to the top, then applies the 'offset'
365+ // to push it down to the middle.
366+ layoutManager.scrollToPositionWithOffset(newIndex, offset)
367+
363368 displayPreparedLine(newIndex)
364369 }
365370 }
366371 }
367372
368373 // --- 2. SETUP SYNC TOGGLE & ANIMATION ---
374+ val fabReSync = findViewById< com.google.android.material.floatingactionbutton.FloatingActionButton > (R .id.fabReSync)
369375 val syncBtn = findViewById<ToggleButton >(R .id.syncToggleButton)
370376 findViewById<TextView >(R .id.floatingWarningText) // Grab the new text
371377 syncBtn.setOnCheckedChangeListener { _, isChecked ->
372378 if (isChecked) {
373379 viewModel.setSource(LyricSource .SPOTIFY )
380+ fabReSync.visibility = View .GONE // Hide FAB if manually enabled via toggle
374381
375382 // Force a re-fetch of the ACTUAL Spotify song
376383 spotifyAppRemote?.playerApi?.playerState?.setResultCallback { playerState ->
377384 val track = playerState.track
378385 if (track != null ) {
379386 // fetchLyrics(track.name, track.artist.name)
387+ }// Optional: Force a scroll back to active line
388+ val currentActive = viewModel.activeIndex.value
389+ if (currentActive != - 1 ) {
390+ findViewById<RecyclerView >(R .id.lyricRecyclerView).smoothScrollToPosition(
391+ currentActive
392+ )
380393 }
381394 }
382395 } else {
@@ -388,6 +401,45 @@ class MainActivity : AppCompatActivity() {
388401 syncBtn, " Click to pause LyriSync from scrolling on touch"
389402 )
390403
404+ val lyricRecyclerView = findViewById<RecyclerView >(R .id.lyricRecyclerView)
405+
406+ // 1. Detect Manual Scroll
407+ lyricRecyclerView.addOnScrollListener(object : RecyclerView .OnScrollListener () {
408+ override fun onScrollStateChanged (recyclerView : RecyclerView , newState : Int ) {
409+ super .onScrollStateChanged(recyclerView, newState)
410+
411+ // SCROLL_STATE_DRAGGING means the user's finger is moving the list
412+ if (newState == RecyclerView .SCROLL_STATE_DRAGGING && viewModel.source.value == LyricSource .SPOTIFY ) {
413+ // Pause Sync and Show FAB
414+ viewModel.setSource(LyricSource .MANUAL )
415+ syncBtn.isChecked = false
416+
417+ fabReSync.apply {
418+ visibility = View .VISIBLE
419+ alpha = 0f
420+ animate().alpha(1f ).setDuration(200 ).start()
421+ }
422+ }
423+ }
424+ })
425+
426+ // 2. Re-enable Sync on FAB Click
427+ fabReSync.setOnClickListener {
428+ viewModel.setSource(LyricSource .SPOTIFY )
429+ syncBtn.isChecked = true
430+
431+ // Smoothly hide the FAB
432+ fabReSync.animate().alpha(0f ).setDuration(200 ).withEndAction {
433+ fabReSync.visibility = View .GONE
434+ }.start()
435+
436+ // Immediately snap back to the current playing line
437+ val currentActive = viewModel.activeIndex.value
438+ if (currentActive != - 1 ) {
439+ lyricRecyclerView.smoothScrollToPosition(currentActive)
440+ }
441+ }
442+
391443 // --- 3. BOTTOM NAVIGATION ---
392444 // The .post block waits for the UI to measure itself before doing math
393445 val homeScreen = findViewById<View >(R .id.homeScreen)
0 commit comments