feat: add sorting and search functionality for album and artist list

This commit is contained in:
CappielloAntonio 2024-11-23 16:00:01 +01:00
parent 9e6926fc97
commit 48d9022f9a
12 changed files with 464 additions and 27 deletions

View file

@ -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

View file

@ -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

View file

@ -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;
}
}