Skip to content

Commit 0a1db14

Browse files
committed
feat: catchup button
1 parent 90f1168 commit 0a1db14

3 files changed

Lines changed: 76 additions & 7 deletions

File tree

app/src/main/java/com/mixtapeo/lyrisync/MainActivity.kt

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
3+
4+
</selector>

app/src/main/res/layout/activity_main.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@
8585
android:paddingTop="24dp"
8686
android:paddingBottom="80dp" />
8787

88+
<com.google.android.material.floatingactionbutton.FloatingActionButton
89+
android:id="@+id/fabReSync"
90+
android:layout_width="wrap_content"
91+
android:layout_height="wrap_content"
92+
android:layout_gravity="bottom|center_horizontal"
93+
android:layout_marginBottom="16dp"
94+
android:visibility="gone"
95+
app:backgroundTint="@color/brand_tertiary"
96+
app:fabSize="mini"
97+
app:srcCompat="@android:drawable/stat_notify_sync"
98+
app:tint="@color/purple_300"
99+
tools:visibility="visible" />
100+
88101
<TextView
89102
android:id="@+id/NoLyricsText"
90103
android:layout_width="wrap_content"

0 commit comments

Comments
 (0)