mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2025-12-24 00:18:02 -05:00
feat: add sorting and search functionality for album and artist list
This commit is contained in:
@@ -3,6 +3,8 @@ package com.cappielloantonio.tempo.ui.adapter;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -11,22 +13,60 @@ import com.cappielloantonio.tempo.databinding.ItemHorizontalAlbumBinding;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontalAdapter.ViewHolder> {
|
||||
public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontalAdapter.ViewHolder> implements Filterable {
|
||||
private final ClickCallback click;
|
||||
private final boolean isOffline;
|
||||
|
||||
private List<AlbumID3> albumsFull;
|
||||
private List<AlbumID3> albums;
|
||||
private String currentFilter;
|
||||
|
||||
private final Filter filtering = new Filter() {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
List<AlbumID3> filteredList = new ArrayList<>();
|
||||
|
||||
if (constraint == null || constraint.length() == 0) {
|
||||
filteredList.addAll(albumsFull);
|
||||
} else {
|
||||
String filterPattern = constraint.toString().toLowerCase().trim();
|
||||
currentFilter = filterPattern;
|
||||
|
||||
for (AlbumID3 item : albumsFull) {
|
||||
if (item.getName().toLowerCase().contains(filterPattern)) {
|
||||
filteredList.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterResults results = new FilterResults();
|
||||
results.values = filteredList;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
albums = (List<AlbumID3>) results.values;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public AlbumHorizontalAdapter(ClickCallback click, boolean isOffline) {
|
||||
this.click = click;
|
||||
this.isOffline = isOffline;
|
||||
this.albums = Collections.emptyList();
|
||||
this.albumsFull = Collections.emptyList();
|
||||
this.currentFilter = "";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -55,10 +95,16 @@ public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontal
|
||||
}
|
||||
|
||||
public void setItems(List<AlbumID3> albums) {
|
||||
this.albums = albums;
|
||||
this.albumsFull = albums != null ? albums : Collections.emptyList();
|
||||
filtering.filter(currentFilter);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return filtering;
|
||||
}
|
||||
|
||||
public AlbumID3 getItem(int id) {
|
||||
return albums.get(id);
|
||||
}
|
||||
@@ -95,4 +141,21 @@ public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontal
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void sort(String order) {
|
||||
switch (order) {
|
||||
case Constants.ALBUM_ORDER_BY_NAME:
|
||||
albums.sort(Comparator.comparing(AlbumID3::getName));
|
||||
break;
|
||||
case Constants.ALBUM_ORDER_BY_MOST_RECENTLY_STARRED:
|
||||
albums.sort(Comparator.comparing(AlbumID3::getStarred, Comparator.nullsLast(Comparator.reverseOrder())));
|
||||
break;
|
||||
case Constants.ALBUM_ORDER_BY_LEAST_RECENTLY_STARRED:
|
||||
albums.sort(Comparator.comparing(AlbumID3::getStarred, Comparator.nullsLast(Comparator.naturalOrder())));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -11,21 +13,59 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.cappielloantonio.tempo.databinding.ItemHorizontalArtistBinding;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizontalAdapter.ViewHolder> {
|
||||
public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizontalAdapter.ViewHolder> implements Filterable {
|
||||
private final ClickCallback click;
|
||||
|
||||
private List<ArtistID3> artistsFull;
|
||||
private List<ArtistID3> artists;
|
||||
private String currentFilter;
|
||||
|
||||
private final Filter filtering = new Filter() {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
List<ArtistID3> filteredList = new ArrayList<>();
|
||||
|
||||
if (constraint == null || constraint.length() == 0) {
|
||||
filteredList.addAll(artistsFull);
|
||||
} else {
|
||||
String filterPattern = constraint.toString().toLowerCase().trim();
|
||||
currentFilter = filterPattern;
|
||||
|
||||
for (ArtistID3 item : artistsFull) {
|
||||
if (item.getName().toLowerCase().contains(filterPattern)) {
|
||||
filteredList.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterResults results = new FilterResults();
|
||||
results.values = filteredList;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
artists = (List<ArtistID3>) results.values;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public ArtistHorizontalAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.artists = Collections.emptyList();
|
||||
this.artistsFull = Collections.emptyList();
|
||||
this.currentFilter = "";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -59,10 +99,16 @@ public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizont
|
||||
}
|
||||
|
||||
public void setItems(List<ArtistID3> artists) {
|
||||
this.artists = artists;
|
||||
this.artistsFull = artists != null ? artists : Collections.emptyList();
|
||||
filtering.filter(currentFilter);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return filtering;
|
||||
}
|
||||
|
||||
public ArtistID3 getItem(int id) {
|
||||
return artists.get(id);
|
||||
}
|
||||
@@ -109,4 +155,21 @@ public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizont
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void sort(String order) {
|
||||
switch (order) {
|
||||
case Constants.ARTIST_ORDER_BY_NAME:
|
||||
artists.sort(Comparator.comparing(ArtistID3::getName));
|
||||
break;
|
||||
case Constants.ARTIST_ORDER_BY_MOST_RECENTLY_STARRED:
|
||||
artists.sort(Comparator.comparing(ArtistID3::getStarred, Comparator.nullsLast(Comparator.reverseOrder())));
|
||||
break;
|
||||
case Constants.ARTIST_ORDER_BY_LEAST_RECENTLY_STARRED:
|
||||
artists.sort(Comparator.comparing(ArtistID3::getStarred, Comparator.nullsLast(Comparator.naturalOrder())));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
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.Nullable;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -17,12 +27,14 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.FragmentAlbumListPageBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.AlbumHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.viewmodel.AlbumListPageViewModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
||||
private FragmentAlbumListPageBinding bind;
|
||||
@@ -31,6 +43,12 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
||||
private AlbumListPageViewModel albumListPageViewModel;
|
||||
private AlbumHorizontalAdapter albumHorizontalAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
activity = (MainActivity) getActivity();
|
||||
@@ -86,7 +104,10 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
||||
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
}
|
||||
|
||||
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
||||
bind.toolbar.setNavigationOnClickListener(v -> {
|
||||
hideKeyboard(v);
|
||||
activity.navController.navigateUp();
|
||||
});
|
||||
|
||||
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||
if ((bind.albumInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
||||
@@ -97,6 +118,7 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void initAlbumListView() {
|
||||
bind.albumListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
bind.albumListRecyclerView.setHasFixedSize(true);
|
||||
@@ -107,7 +129,99 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback {
|
||||
);
|
||||
|
||||
bind.albumListRecyclerView.setAdapter(albumHorizontalAdapter);
|
||||
albumListPageViewModel.getAlbumList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> albumHorizontalAdapter.setItems(albums));
|
||||
albumListPageViewModel.getAlbumList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), albums -> {
|
||||
albumHorizontalAdapter.setItems(albums);
|
||||
setAlbumListPageSubtitle(albums);
|
||||
setAlbumListPageSorter();
|
||||
});
|
||||
|
||||
bind.albumListRecyclerView.setOnTouchListener((v, event) -> {
|
||||
hideKeyboard(v);
|
||||
return false;
|
||||
});
|
||||
|
||||
bind.albumListSortImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.sort_horizontal_album_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) {
|
||||
albumHorizontalAdapter.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_horizontal_album_sort_name) {
|
||||
albumHorizontalAdapter.sort(Constants.ALBUM_ORDER_BY_NAME);
|
||||
return true;
|
||||
} else if (menuItem.getItemId() == R.id.menu_horizontal_album_sort_most_recently_starred) {
|
||||
albumHorizontalAdapter.sort(Constants.ALBUM_ORDER_BY_MOST_RECENTLY_STARRED);
|
||||
return true;
|
||||
} else if (menuItem.getItemId() == R.id.menu_horizontal_album_sort_least_recently_starred) {
|
||||
albumHorizontalAdapter.sort(Constants.ALBUM_ORDER_BY_LEAST_RECENTLY_STARRED);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
popup.show();
|
||||
}
|
||||
|
||||
private void setAlbumListPageSubtitle(List<AlbumID3> albums) {
|
||||
switch (albumListPageViewModel.title) {
|
||||
case Constants.ALBUM_RECENTLY_PLAYED:
|
||||
case Constants.ALBUM_MOST_PLAYED:
|
||||
case Constants.ALBUM_RECENTLY_ADDED:
|
||||
bind.pageSubtitleLabel.setText(albums.size() < albumListPageViewModel.maxNumber ?
|
||||
getString(R.string.generic_list_page_count, albums.size()) :
|
||||
getString(R.string.generic_list_page_count_unknown, albumListPageViewModel.maxNumber)
|
||||
);
|
||||
break;
|
||||
case Constants.ALBUM_STARRED:
|
||||
bind.pageSubtitleLabel.setText(getString(R.string.generic_list_page_count, albums.size()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void setAlbumListPageSorter() {
|
||||
switch (albumListPageViewModel.title) {
|
||||
case Constants.ALBUM_RECENTLY_PLAYED:
|
||||
case Constants.ALBUM_MOST_PLAYED:
|
||||
case Constants.ALBUM_RECENTLY_ADDED:
|
||||
bind.albumListSortImageView.setVisibility(View.GONE);
|
||||
break;
|
||||
case Constants.ALBUM_STARRED:
|
||||
bind.albumListSortImageView.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
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.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
@@ -16,11 +26,15 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.FragmentArtistListPageBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.ArtistHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.viewmodel.ArtistListPageViewModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@UnstableApi
|
||||
public class ArtistListPageFragment extends Fragment implements ClickCallback {
|
||||
private FragmentArtistListPageBinding bind;
|
||||
@@ -30,6 +44,12 @@ public class ArtistListPageFragment extends Fragment implements ClickCallback {
|
||||
|
||||
private ArtistHorizontalAdapter artistHorizontalAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
activity = (MainActivity) getActivity();
|
||||
@@ -69,7 +89,10 @@ public class ArtistListPageFragment extends Fragment implements ClickCallback {
|
||||
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
}
|
||||
|
||||
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
||||
bind.toolbar.setNavigationOnClickListener(v -> {
|
||||
hideKeyboard(v);
|
||||
activity.navController.navigateUp();
|
||||
});
|
||||
|
||||
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||
if ((bind.artistInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
||||
@@ -80,18 +103,100 @@ public class ArtistListPageFragment extends Fragment implements ClickCallback {
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void initArtistListView() {
|
||||
bind.artistListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
bind.artistListRecyclerView.setHasFixedSize(true);
|
||||
|
||||
artistHorizontalAdapter = new ArtistHorizontalAdapter(this);
|
||||
bind.artistListRecyclerView.setAdapter(artistHorizontalAdapter);
|
||||
artistListPageViewModel.getArtistList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> artistHorizontalAdapter.setItems(artists));
|
||||
artistListPageViewModel.getArtistList(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), artists -> {
|
||||
artistHorizontalAdapter.setItems(artists);
|
||||
setArtistListPageSubtitle(artists);
|
||||
setArtistListPageSorter();
|
||||
});
|
||||
|
||||
bind.artistListRecyclerView.setOnTouchListener((v, event) -> {
|
||||
hideKeyboard(v);
|
||||
return false;
|
||||
});
|
||||
|
||||
bind.artistListSortImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.sort_horizontal_artist_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) {
|
||||
artistHorizontalAdapter.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_horizontal_artist_sort_name) {
|
||||
artistHorizontalAdapter.sort(Constants.ARTIST_ORDER_BY_NAME);
|
||||
return true;
|
||||
} else if (menuItem.getItemId() == R.id.menu_horizontal_artist_sort_most_recently_starred) {
|
||||
artistHorizontalAdapter.sort(Constants.ARTIST_ORDER_BY_MOST_RECENTLY_STARRED);
|
||||
return true;
|
||||
} else if (menuItem.getItemId() == R.id.menu_horizontal_artist_sort_least_recently_starred) {
|
||||
artistHorizontalAdapter.sort(Constants.ARTIST_ORDER_BY_LEAST_RECENTLY_STARRED);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
popup.show();
|
||||
}
|
||||
|
||||
private void setArtistListPageSubtitle(List<ArtistID3> artists) {
|
||||
switch (artistListPageViewModel.title) {
|
||||
case Constants.ARTIST_STARRED:
|
||||
case Constants.ARTIST_DOWNLOADED:
|
||||
bind.pageSubtitleLabel.setText(getString(R.string.generic_list_page_count, artists.size()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void setArtistListPageSorter() {
|
||||
switch (artistListPageViewModel.title) {
|
||||
case Constants.ARTIST_STARRED:
|
||||
case Constants.ARTIST_DOWNLOADED:
|
||||
bind.artistListSortImageView.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onArtistClick(Bundle bundle) {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.albumListPageFragment, bundle);
|
||||
Navigation.findNavController(requireView()).navigate(R.id.artistPageFragment, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -274,20 +274,20 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||
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)
|
||||
getString(R.string.generic_list_page_count, children.size()) :
|
||||
getString(R.string.generic_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)
|
||||
getString(R.string.generic_list_page_count, children.size()) :
|
||||
getString(R.string.generic_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()));
|
||||
bind.pageSubtitleLabel.setText(getString(R.string.generic_list_page_count, children.size()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,15 @@ object Constants {
|
||||
const val ALBUM_ORDER_BY_RECENTLY_ADDED = "ALBUM_ORDER_BY_RECENTLY_ADDED"
|
||||
const val ALBUM_ORDER_BY_RECENTLY_PLAYED = "ALBUM_ORDER_BY_RECENTLY_PLAYED"
|
||||
const val ALBUM_ORDER_BY_MOST_PLAYED = "ALBUM_ORDER_BY_MOST_PLAYED"
|
||||
const val ALBUM_ORDER_BY_MOST_RECENTLY_STARRED = "ALBUM_ORDER_BY_MOST_RECENTLY_STARRED"
|
||||
const val ALBUM_ORDER_BY_LEAST_RECENTLY_STARRED = "ALBUM_ORDER_BY_LEAST_RECENTLY_STARRED"
|
||||
|
||||
const val ARTIST_DOWNLOADED = "ARTIST_DOWNLOADED"
|
||||
const val ARTIST_STARRED = "ARTIST_STARRED"
|
||||
const val ARTIST_ORDER_BY_NAME = "ARTIST_ORDER_BY_NAME"
|
||||
const val ARTIST_ORDER_BY_RANDOM = "ARTIST_ORDER_BY_RANDOM"
|
||||
const val ARTIST_ORDER_BY_MOST_RECENTLY_STARRED = "ARTIST_ORDER_BY_MOST_RECENTLY_STARRED"
|
||||
const val ARTIST_ORDER_BY_LEAST_RECENTLY_STARRED = "ARTIST_ORDER_BY_LEAST_RECENTLY_STARRED"
|
||||
|
||||
const val GENRE_ORDER_BY_NAME = "GENRE_ORDER_BY_NAME"
|
||||
const val GENRE_ORDER_BY_RANDOM = "GENRE_ORDER_BY_RANDOM"
|
||||
|
||||
@@ -28,6 +28,8 @@ public class AlbumListPageViewModel extends AndroidViewModel {
|
||||
|
||||
private MutableLiveData<List<AlbumID3>> albumList;
|
||||
|
||||
public int maxNumber = 500;
|
||||
|
||||
public AlbumListPageViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
|
||||
@@ -40,20 +42,20 @@ public class AlbumListPageViewModel extends AndroidViewModel {
|
||||
|
||||
switch (title) {
|
||||
case Constants.ALBUM_RECENTLY_PLAYED:
|
||||
albumRepository.getAlbums("recent", 500, null, null).observe(owner, albums -> albumList.setValue(albums));
|
||||
albumRepository.getAlbums("recent", maxNumber, null, null).observe(owner, albums -> albumList.setValue(albums));
|
||||
break;
|
||||
case Constants.ALBUM_MOST_PLAYED:
|
||||
albumRepository.getAlbums("frequent", 500, null, null).observe(owner, albums -> albumList.setValue(albums));
|
||||
albumRepository.getAlbums("frequent", maxNumber, null, null).observe(owner, albums -> albumList.setValue(albums));
|
||||
break;
|
||||
case Constants.ALBUM_RECENTLY_ADDED:
|
||||
albumRepository.getAlbums("newest", 500, null, null).observe(owner, albums -> albumList.setValue(albums));
|
||||
albumRepository.getAlbums("newest", maxNumber, null, null).observe(owner, albums -> albumList.setValue(albums));
|
||||
break;
|
||||
case Constants.ALBUM_STARRED:
|
||||
albumList = albumRepository.getStarredAlbums(false, -1);
|
||||
break;
|
||||
case Constants.ALBUM_NEW_RELEASES:
|
||||
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
|
||||
albumRepository.getAlbums("byYear", 500, currentYear, currentYear).observe(owner, albums -> {
|
||||
albumRepository.getAlbums("byYear", maxNumber, currentYear, currentYear).observe(owner, albums -> {
|
||||
albums.sort(Comparator.comparing(AlbumID3::getCreated).reversed());
|
||||
albumList.postValue(albums.subList(0, Math.min(20, albums.size())));
|
||||
});
|
||||
|
||||
@@ -33,17 +33,48 @@
|
||||
<TextView
|
||||
android:id="@+id/page_title_label"
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingBottom="24dp"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="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/album_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="16dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
app:cornerRadius="30dp"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_sort_list"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
@@ -33,17 +33,48 @@
|
||||
<TextView
|
||||
android:id="@+id/page_title_label"
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingBottom="24dp"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="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/artist_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="16dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
app:cornerRadius="30dp"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_sort_list"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
12
app/src/main/res/menu/sort_horizontal_album_popup_menu.xml
Normal file
12
app/src/main/res/menu/sort_horizontal_album_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_horizontal_album_sort_name"
|
||||
android:title="@string/menu_sort_name" />
|
||||
<item
|
||||
android:id="@+id/menu_horizontal_album_sort_most_recently_starred"
|
||||
android:title="@string/menu_sort_most_recently_starred" />
|
||||
<item
|
||||
android:id="@+id/menu_horizontal_album_sort_least_recently_starred"
|
||||
android:title="@string/menu_sort_least_recently_starred" />
|
||||
</menu>
|
||||
12
app/src/main/res/menu/sort_horizontal_artist_popup_menu.xml
Normal file
12
app/src/main/res/menu/sort_horizontal_artist_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_horizontal_artist_sort_name"
|
||||
android:title="@string/menu_sort_name" />
|
||||
<item
|
||||
android:id="@+id/menu_horizontal_artist_sort_most_recently_starred"
|
||||
android:title="@string/menu_sort_most_recently_starred" />
|
||||
<item
|
||||
android:id="@+id/menu_horizontal_artist_sort_least_recently_starred"
|
||||
android:title="@string/menu_sort_least_recently_starred" />
|
||||
</menu>
|
||||
@@ -90,6 +90,8 @@
|
||||
<string name="filter_info_selection">Select two or more filters</string>
|
||||
<string name="filter_title">Filter</string>
|
||||
<string name="filter_title_expanded">Filter Genres</string>
|
||||
<string name="generic_list_page_count">(%1$d)</string>
|
||||
<string name="generic_list_page_count_unknown">(+%1$d)</string>
|
||||
<string name="genre_catalogue_title">Genre Catalogue</string>
|
||||
<string name="genre_catalogue_title_expanded">Browse Genres</string>
|
||||
<string name="github_update_dialog_negative_button">Remind me later</string>
|
||||
@@ -375,8 +377,6 @@
|
||||
<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_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="starred_sync_dialog_negative_button">Cancel</string>
|
||||
<string name="starred_sync_dialog_neutral_button">Continue</string>
|
||||
|
||||
Reference in New Issue
Block a user