mirror of
https://github.com/antebudimir/tempus.git
synced 2026-01-02 02:13:33 +00:00
feat: add sorting and search functionality for song list
This commit is contained in:
parent
780f1c3a2e
commit
9e6926fc97
8 changed files with 198 additions and 9 deletions
|
|
@ -26,6 +26,7 @@ import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -230,4 +231,20 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sort(String order) {
|
||||||
|
switch (order) {
|
||||||
|
case Constants.MEDIA_BY_TITLE:
|
||||||
|
songs.sort(Comparator.comparing(Child::getTitle));
|
||||||
|
break;
|
||||||
|
case Constants.MEDIA_MOST_RECENTLY_STARRED:
|
||||||
|
songs.sort(Comparator.comparing(Child::getStarred, Comparator.nullsLast(Comparator.reverseOrder())));
|
||||||
|
break;
|
||||||
|
case Constants.MEDIA_LEAST_RECENTLY_STARRED:
|
||||||
|
songs.sort(Comparator.comparing(Child::getStarred, Comparator.nullsLast(Comparator.naturalOrder())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,22 @@
|
||||||
package com.cappielloantonio.tempo.ui.fragment;
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
|
import android.widget.SearchView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
@ -22,14 +32,15 @@ import com.cappielloantonio.tempo.helper.recyclerview.PaginationScrollListener;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
import com.cappielloantonio.tempo.service.MediaManager;
|
import com.cappielloantonio.tempo.service.MediaManager;
|
||||||
import com.cappielloantonio.tempo.service.MediaService;
|
import com.cappielloantonio.tempo.service.MediaService;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
|
||||||
import com.cappielloantonio.tempo.viewmodel.SongListPageViewModel;
|
import com.cappielloantonio.tempo.viewmodel.SongListPageViewModel;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class SongListPageFragment extends Fragment implements ClickCallback {
|
public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||||
|
|
@ -45,6 +56,12 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||||
|
|
||||||
private boolean isLoading = true;
|
private boolean isLoading = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
activity = (MainActivity) getActivity();
|
activity = (MainActivity) getActivity();
|
||||||
|
|
@ -138,7 +155,10 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
bind.toolbar.setNavigationOnClickListener(v -> {
|
||||||
|
hideKeyboard(v);
|
||||||
|
activity.navController.navigateUp();
|
||||||
|
});
|
||||||
|
|
||||||
if (bind != null)
|
if (bind != null)
|
||||||
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||||
|
|
@ -153,6 +173,8 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||||
private void initButtons() {
|
private void initButtons() {
|
||||||
songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> {
|
songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (bind != null) {
|
if (bind != null) {
|
||||||
|
setSongListPageSorter();
|
||||||
|
|
||||||
bind.songListShuffleImageView.setOnClickListener(v -> {
|
bind.songListShuffleImageView.setOnClickListener(v -> {
|
||||||
Collections.shuffle(songs);
|
Collections.shuffle(songs);
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs.subList(0, Math.min(25, songs.size())), 0);
|
MediaManager.startQueue(mediaBrowserListenableFuture, songs.subList(0, Math.min(25, songs.size())), 0);
|
||||||
|
|
@ -162,6 +184,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private void initSongListView() {
|
private void initSongListView() {
|
||||||
bind.songListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.songListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.songListRecyclerView.setHasFixedSize(true);
|
bind.songListRecyclerView.setHasFixedSize(true);
|
||||||
|
|
@ -171,6 +194,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||||
songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> {
|
songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
songHorizontalAdapter.setItems(songs);
|
songHorizontalAdapter.setItems(songs);
|
||||||
|
setSongListPageSubtitle(songs);
|
||||||
});
|
});
|
||||||
|
|
||||||
bind.songListRecyclerView.addOnScrollListener(new PaginationScrollListener((LinearLayoutManager) bind.songListRecyclerView.getLayoutManager()) {
|
bind.songListRecyclerView.addOnScrollListener(new PaginationScrollListener((LinearLayoutManager) bind.songListRecyclerView.getLayoutManager()) {
|
||||||
|
|
@ -185,6 +209,101 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||||
return isLoading;
|
return isLoading;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bind.songListRecyclerView.setOnTouchListener((v, event) -> {
|
||||||
|
hideKeyboard(v);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
bind.songListSortImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.sort_song_popup_menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.toolbar_menu, menu);
|
||||||
|
|
||||||
|
MenuItem searchItem = menu.findItem(R.id.action_search);
|
||||||
|
|
||||||
|
SearchView searchView = (SearchView) searchItem.getActionView();
|
||||||
|
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||||
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
searchView.clearFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
songHorizontalAdapter.getFilter().filter(newText);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchView.setPadding(-32, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideKeyboard(View view) {
|
||||||
|
InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPopupMenu(View view, int menuResource) {
|
||||||
|
PopupMenu popup = new PopupMenu(requireContext(), view);
|
||||||
|
popup.getMenuInflater().inflate(menuResource, popup.getMenu());
|
||||||
|
|
||||||
|
popup.setOnMenuItemClickListener(menuItem -> {
|
||||||
|
if (menuItem.getItemId() == R.id.menu_song_sort_name) {
|
||||||
|
songHorizontalAdapter.sort(Constants.MEDIA_BY_TITLE);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_song_sort_most_recently_starred) {
|
||||||
|
songHorizontalAdapter.sort(Constants.MEDIA_MOST_RECENTLY_STARRED);
|
||||||
|
return true;
|
||||||
|
} else if (menuItem.getItemId() == R.id.menu_song_sort_least_recently_starred) {
|
||||||
|
songHorizontalAdapter.sort(Constants.MEDIA_LEAST_RECENTLY_STARRED);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSongListPageSubtitle(List<Child> children) {
|
||||||
|
switch (songListPageViewModel.title) {
|
||||||
|
case Constants.MEDIA_BY_GENRE:
|
||||||
|
bind.pageSubtitleLabel.setText(children.size() < songListPageViewModel.maxNumberByGenre ?
|
||||||
|
getString(R.string.song_list_page_count, children.size()) :
|
||||||
|
getString(R.string.song_list_page_count_unknown, songListPageViewModel.maxNumberByGenre)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case Constants.MEDIA_BY_YEAR:
|
||||||
|
bind.pageSubtitleLabel.setText(children.size() < songListPageViewModel.maxNumberByYear ?
|
||||||
|
getString(R.string.song_list_page_count, children.size()) :
|
||||||
|
getString(R.string.song_list_page_count_unknown, songListPageViewModel.maxNumberByYear)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case Constants.MEDIA_BY_ARTIST:
|
||||||
|
case Constants.MEDIA_BY_GENRES:
|
||||||
|
case Constants.MEDIA_STARRED:
|
||||||
|
bind.pageSubtitleLabel.setText(getString(R.string.song_list_page_count, children.size()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSongListPageSorter() {
|
||||||
|
switch (songListPageViewModel.title) {
|
||||||
|
case Constants.MEDIA_BY_GENRE:
|
||||||
|
case Constants.MEDIA_BY_YEAR:
|
||||||
|
bind.songListSortImageView.setVisibility(View.GONE);
|
||||||
|
break;
|
||||||
|
case Constants.MEDIA_BY_ARTIST:
|
||||||
|
case Constants.MEDIA_BY_GENRES:
|
||||||
|
case Constants.MEDIA_STARRED:
|
||||||
|
bind.songListSortImageView.setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeMediaBrowser() {
|
private void initializeMediaBrowser() {
|
||||||
|
|
@ -197,6 +316,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMediaClick(Bundle bundle) {
|
public void onMediaClick(Bundle bundle) {
|
||||||
|
hideKeyboard(requireView());
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
|
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
|
||||||
activity.setBottomSheetInPeek(true);
|
activity.setBottomSheetInPeek(true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,9 @@ object Constants {
|
||||||
const val MEDIA_MIX = "MEDIA_MIX"
|
const val MEDIA_MIX = "MEDIA_MIX"
|
||||||
const val MEDIA_CHRONOLOGY = "MEDIA_CHRONOLOGY"
|
const val MEDIA_CHRONOLOGY = "MEDIA_CHRONOLOGY"
|
||||||
const val MEDIA_BEST_OF = "MEDIA_BEST_OF"
|
const val MEDIA_BEST_OF = "MEDIA_BEST_OF"
|
||||||
|
const val MEDIA_BY_TITLE = "MEDIA_BY_TITLE"
|
||||||
|
const val MEDIA_MOST_RECENTLY_STARRED = "MEDIA_MOST_RECENTLY_STARRED"
|
||||||
|
const val MEDIA_LEAST_RECENTLY_STARRED = "MEDIA_LEAST_RECENTLY_STARRED"
|
||||||
|
|
||||||
const val DOWNLOAD_URI = "rest/download"
|
const val DOWNLOAD_URI = "rest/download"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ public class MusicUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getReadableAudioQualityString(Child child) {
|
public static String getReadableAudioQualityString(Child child) {
|
||||||
if (!Preferences.showAudioQuality()) return "";
|
if (!Preferences.showAudioQuality() || child.getBitrate() == null) return "";
|
||||||
|
|
||||||
return "•" +
|
return "•" +
|
||||||
" " +
|
" " +
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ public class SongListPageViewModel extends AndroidViewModel {
|
||||||
public ArrayList<String> filterNames = new ArrayList<>();
|
public ArrayList<String> filterNames = new ArrayList<>();
|
||||||
|
|
||||||
public int year = 0;
|
public int year = 0;
|
||||||
|
public int maxNumberByYear = 500;
|
||||||
|
public int maxNumberByGenre = 100;
|
||||||
|
|
||||||
public SongListPageViewModel(@NonNull Application application) {
|
public SongListPageViewModel(@NonNull Application application) {
|
||||||
super(application);
|
super(application);
|
||||||
|
|
@ -58,7 +60,7 @@ public class SongListPageViewModel extends AndroidViewModel {
|
||||||
songList = songRepository.getSongsByGenres(filters);
|
songList = songRepository.getSongsByGenres(filters);
|
||||||
break;
|
break;
|
||||||
case Constants.MEDIA_BY_YEAR:
|
case Constants.MEDIA_BY_YEAR:
|
||||||
songList = songRepository.getRandomSample(500, year, year + 10);
|
songList = songRepository.getRandomSample(maxNumberByYear, year, year + 10);
|
||||||
break;
|
break;
|
||||||
case Constants.MEDIA_STARRED:
|
case Constants.MEDIA_STARRED:
|
||||||
songList = songRepository.getStarredSongs(false, -1);
|
songList = songRepository.getStarredSongs(false, -1);
|
||||||
|
|
@ -73,9 +75,9 @@ public class SongListPageViewModel extends AndroidViewModel {
|
||||||
case Constants.MEDIA_BY_GENRE:
|
case Constants.MEDIA_BY_GENRE:
|
||||||
int songCount = songList.getValue() != null ? songList.getValue().size() : 0;
|
int songCount = songList.getValue() != null ? songList.getValue().size() : 0;
|
||||||
|
|
||||||
if (songCount > 0 && songCount % 100 != 0) return;
|
if (songCount > 0 && songCount % maxNumberByGenre != 0) return;
|
||||||
|
|
||||||
int page = songCount / 100;
|
int page = songCount / maxNumberByGenre;
|
||||||
songRepository.getSongsByGenre(genre.getGenre(), page).observe(owner, children -> {
|
songRepository.getSongsByGenre(genre.getGenre(), page).observe(owner, children -> {
|
||||||
if (children != null && !children.isEmpty()) {
|
if (children != null && !children.isEmpty()) {
|
||||||
List<Child> currentMedia = songList.getValue();
|
List<Child> currentMedia = songList.getValue();
|
||||||
|
|
|
||||||
|
|
@ -33,17 +33,48 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/page_title_label"
|
android:id="@+id/page_title_label"
|
||||||
style="@style/TitleLarge"
|
style="@style/TitleLarge"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingTop="16dp"
|
android:paddingTop="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="4dp"
|
||||||
android:paddingBottom="24dp"
|
android:paddingBottom="24dp"
|
||||||
android:text="@string/label_placeholder"
|
android:text="@string/label_placeholder"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/song_list_shuffle_image_view"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/page_subtitle_label"
|
||||||
|
style="@style/TitleMedium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="4dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="24dp"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/page_title_label"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/page_title_label"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/page_title_label" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/song_list_sort_image_view"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
||||||
|
android:layout_width="52dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:insetLeft="0dp"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetRight="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:cornerRadius="30dp"
|
||||||
|
app:icon="@drawable/ic_sort_list"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/song_list_shuffle_image_view" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/song_list_shuffle_image_view"
|
android:id="@+id/song_list_shuffle_image_view"
|
||||||
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
||||||
|
|
|
||||||
12
app/src/main/res/menu/sort_song_popup_menu.xml
Normal file
12
app/src/main/res/menu/sort_song_popup_menu.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_song_sort_name"
|
||||||
|
android:title="@string/menu_sort_name" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_song_sort_most_recently_starred"
|
||||||
|
android:title="@string/menu_sort_most_recently_starred" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_song_sort_least_recently_starred"
|
||||||
|
android:title="@string/menu_sort_least_recently_starred" />
|
||||||
|
</menu>
|
||||||
|
|
@ -178,6 +178,8 @@
|
||||||
<string name="menu_sort_recently_added">Recently added</string>
|
<string name="menu_sort_recently_added">Recently added</string>
|
||||||
<string name="menu_sort_recently_played">Recently played</string>
|
<string name="menu_sort_recently_played">Recently played</string>
|
||||||
<string name="menu_sort_most_played">Most played</string>
|
<string name="menu_sort_most_played">Most played</string>
|
||||||
|
<string name="menu_sort_most_recently_starred">Most recently starred</string>
|
||||||
|
<string name="menu_sort_least_recently_starred">Least recently starred</string>
|
||||||
<string name="menu_pin_button">Add to home screen</string>
|
<string name="menu_pin_button">Add to home screen</string>
|
||||||
<string name="menu_unpin_button">Remove from home screen</string>
|
<string name="menu_unpin_button">Remove from home screen</string>
|
||||||
<string name="menu_sort_year">Year</string>
|
<string name="menu_sort_year">Year</string>
|
||||||
|
|
@ -373,6 +375,8 @@
|
||||||
<string name="song_list_page_starred">Starred tracks</string>
|
<string name="song_list_page_starred">Starred tracks</string>
|
||||||
<string name="song_list_page_top">%1$s\'s top tracks</string>
|
<string name="song_list_page_top">%1$s\'s top tracks</string>
|
||||||
<string name="song_list_page_year">Year %1$d</string>
|
<string name="song_list_page_year">Year %1$d</string>
|
||||||
|
<string name="song_list_page_count">(%1$d)</string>
|
||||||
|
<string name="song_list_page_count_unknown">(+%1$d)</string>
|
||||||
<string name="song_subtitle_formatter">%1$s • %2$s %3$s</string>
|
<string name="song_subtitle_formatter">%1$s • %2$s %3$s</string>
|
||||||
<string name="starred_sync_dialog_negative_button">Cancel</string>
|
<string name="starred_sync_dialog_negative_button">Cancel</string>
|
||||||
<string name="starred_sync_dialog_neutral_button">Continue</string>
|
<string name="starred_sync_dialog_neutral_button">Continue</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue