Skip to content

Commit

Permalink
feat: add sorting and search functionality for album and artist list
Browse files Browse the repository at this point in the history
  • Loading branch information
CappielloAntonio committed Nov 23, 2024
1 parent 9e6926f commit 48d9022
Show file tree
Hide file tree
Showing 12 changed files with 464 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
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;
Expand All @@ -11,22 +13,60 @@
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
Expand Down Expand Up @@ -55,10 +95,16 @@ public int getItemCount() {
}

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);
}
Expand Down Expand Up @@ -95,4 +141,21 @@ private boolean onLongClick() {
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,68 @@
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;

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
Expand Down Expand Up @@ -59,10 +99,16 @@ public int getItemCount() {
}

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);
}
Expand Down Expand Up @@ -109,4 +155,21 @@ public boolean onLongClick() {
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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,12 +27,14 @@
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;
Expand All @@ -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();
Expand Down Expand Up @@ -86,7 +104,10 @@ private void initAppBar() {
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))) {
Expand All @@ -97,6 +118,7 @@ private void initAppBar() {
});
}

@SuppressLint("ClickableViewAccessibility")
private void initAlbumListView() {
bind.albumListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
bind.albumListRecyclerView.setHasFixedSize(true);
Expand All @@ -107,7 +129,99 @@ private void initAlbumListView() {
);

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
Expand Down
Loading

0 comments on commit 48d9022

Please sign in to comment.