mirror of
https://github.com/antebudimir/tempus.git
synced 2026-01-01 18:03:33 +00:00
feat: implemented karaoke mode for synchronized lyrics
This commit is contained in:
parent
28fef53590
commit
733102a8a4
4 changed files with 88 additions and 0 deletions
|
|
@ -50,8 +50,11 @@ public class PlayerLyricsFragment extends Fragment {
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
bind = InnerFragmentPlayerLyricsBinding.inflate(inflater, container, false);
|
bind = InnerFragmentPlayerLyricsBinding.inflate(inflater, container, false);
|
||||||
View view = bind.getRoot();
|
View view = bind.getRoot();
|
||||||
|
|
||||||
playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class);
|
playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class);
|
||||||
|
|
||||||
|
initOverlay();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,6 +96,12 @@ public class PlayerLyricsFragment extends Fragment {
|
||||||
bind = null;
|
bind = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initOverlay() {
|
||||||
|
bind.syncLyricsTapButton.setOnClickListener(view -> {
|
||||||
|
playerBottomSheetViewModel.changeSyncLyricsState();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeBrowser() {
|
private void initializeBrowser() {
|
||||||
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
||||||
}
|
}
|
||||||
|
|
@ -134,25 +143,31 @@ public class PlayerLyricsFragment extends Fragment {
|
||||||
private void setPanelContent(String lyrics, LyricsList lyricsList) {
|
private void setPanelContent(String lyrics, LyricsList lyricsList) {
|
||||||
playerBottomSheetViewModel.getLiveDescription().observe(getViewLifecycleOwner(), description -> {
|
playerBottomSheetViewModel.getLiveDescription().observe(getViewLifecycleOwner(), description -> {
|
||||||
if (bind != null) {
|
if (bind != null) {
|
||||||
|
bind.nowPlayingSongLyricsSrollView.smoothScrollTo(0, 0);
|
||||||
|
|
||||||
if (lyrics != null && !lyrics.trim().equals("")) {
|
if (lyrics != null && !lyrics.trim().equals("")) {
|
||||||
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(lyrics));
|
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(lyrics));
|
||||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
||||||
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
||||||
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
||||||
|
bind.syncLyricsTapButton.setVisibility(View.GONE);
|
||||||
} else if (lyricsList != null && lyricsList.getStructuredLyrics() != null) {
|
} else if (lyricsList != null && lyricsList.getStructuredLyrics() != null) {
|
||||||
setSyncLirics(lyricsList);
|
setSyncLirics(lyricsList);
|
||||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
||||||
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
||||||
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
||||||
|
bind.syncLyricsTapButton.setVisibility(View.VISIBLE);
|
||||||
} else if (description != null && !description.trim().equals("")) {
|
} else if (description != null && !description.trim().equals("")) {
|
||||||
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(description));
|
bind.nowPlayingSongLyricsTextView.setText(MusicUtil.getReadableLyrics(description));
|
||||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
bind.nowPlayingSongLyricsTextView.setVisibility(View.VISIBLE);
|
||||||
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
bind.emptyDescriptionImageView.setVisibility(View.GONE);
|
||||||
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
bind.titleEmptyDescriptionLabel.setVisibility(View.GONE);
|
||||||
|
bind.syncLyricsTapButton.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
bind.nowPlayingSongLyricsTextView.setVisibility(View.GONE);
|
bind.nowPlayingSongLyricsTextView.setVisibility(View.GONE);
|
||||||
bind.emptyDescriptionImageView.setVisibility(View.VISIBLE);
|
bind.emptyDescriptionImageView.setVisibility(View.VISIBLE);
|
||||||
bind.titleEmptyDescriptionLabel.setVisibility(View.VISIBLE);
|
bind.titleEmptyDescriptionLabel.setVisibility(View.VISIBLE);
|
||||||
|
bind.syncLyricsTapButton.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -228,6 +243,10 @@ public class PlayerLyricsFragment extends Fragment {
|
||||||
spannableString.setSpan(new ForegroundColorSpan(requireContext().getResources().getColor(R.color.lyricsTextColor, null)), startingPosition, endingPosition, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
spannableString.setSpan(new ForegroundColorSpan(requireContext().getResources().getColor(R.color.lyricsTextColor, null)), startingPosition, endingPosition, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
bind.nowPlayingSongLyricsTextView.setText(spannableString);
|
bind.nowPlayingSongLyricsTextView.setText(spannableString);
|
||||||
|
|
||||||
|
if (playerBottomSheetViewModel.getSyncLyricsState()) {
|
||||||
|
bind.nowPlayingSongLyricsSrollView.smoothScrollTo(0, getScroll(lines, toHighlight));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -245,4 +264,27 @@ public class PlayerLyricsFragment extends Fragment {
|
||||||
|
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getLineCount(List<Line> lines, Line toHighlight) {
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
for (Line line : lines) {
|
||||||
|
if (line != toHighlight) {
|
||||||
|
bind.tempLyricsLineTextView.setText(line.getValue());
|
||||||
|
start = start + bind.tempLyricsLineTextView.getLineCount();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getScroll(List<Line> lines, Line toHighlight) {
|
||||||
|
int lineHeight = bind.nowPlayingSongLyricsTextView.getLineHeight();
|
||||||
|
int lineCount = getLineCount(lines, toHighlight);
|
||||||
|
int scrollViewHeight = bind.nowPlayingSongLyricsSrollView.getHeight();
|
||||||
|
|
||||||
|
return lineHeight * lineCount < scrollViewHeight / 2 ? 0 : lineHeight * lineCount - scrollViewHeight / 2 + lineHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +50,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
||||||
private final MutableLiveData<Child> liveMedia = new MutableLiveData<>(null);
|
private final MutableLiveData<Child> liveMedia = new MutableLiveData<>(null);
|
||||||
private final MutableLiveData<ArtistID3> liveArtist = new MutableLiveData<>(null);
|
private final MutableLiveData<ArtistID3> liveArtist = new MutableLiveData<>(null);
|
||||||
private final MutableLiveData<List<Child>> instantMix = new MutableLiveData<>(null);
|
private final MutableLiveData<List<Child>> instantMix = new MutableLiveData<>(null);
|
||||||
|
private boolean lyricsSyncState = true;
|
||||||
|
|
||||||
|
|
||||||
public PlayerBottomSheetViewModel(@NonNull Application application) {
|
public PlayerBottomSheetViewModel(@NonNull Application application) {
|
||||||
|
|
@ -210,4 +211,12 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void changeSyncLyricsState() {
|
||||||
|
lyricsSyncState = !lyricsSyncState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getSyncLyricsState() {
|
||||||
|
return lyricsSyncState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
app/src/main/res/drawable/ic_lyrics_sync_lock.xml
Normal file
9
app/src/main/res/drawable/ic_lyrics_sync_lock.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/titleTextColor"
|
||||||
|
android:pathData="M160,800L160,720L269,720Q218,676 189,614Q160,552 160,480Q160,368 228,282.5Q296,197 400,170L400,254Q330,279 285,340.5Q240,402 240,480Q240,534 261.5,579.5Q283,625 320,658L320,560L400,560L400,800L160,800ZM720,480Q720,429 699.5,384.5Q679,340 640,302L640,400L560,400L560,160L800,160L800,240L691,240Q750,293 774.5,353.5Q799,414 800,480L720,480ZM640,880Q623,880 611.5,868.5Q600,857 600,840L600,720Q600,703 611.5,691.5Q623,680 640,680L640,680L640,640Q640,607 663.5,583.5Q687,560 720,560Q753,560 776.5,583.5Q800,607 800,640L800,680L800,680Q817,680 828.5,691.5Q840,703 840,720L840,840Q840,857 828.5,868.5Q817,880 800,880L640,880ZM680,680L760,680L760,640Q760,623 748.5,611.5Q737,600 720,600Q703,600 691.5,611.5Q680,623 680,640L680,680Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -50,4 +50,32 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/sync_lyrics_tap_button"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:insetLeft="0dp"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetRight="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
app:cornerRadius="64dp"
|
||||||
|
android:alpha="0.7"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:icon="@drawable/ic_lyrics_sync_lock"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/now_playing_song_lyrics_sroll_view"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/now_playing_song_lyrics_sroll_view" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/temp_lyrics_line_text_view"
|
||||||
|
style="@style/BodyLarge"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:visibility="invisible"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue