feat: added the ability to filter and group downloaded songs

This commit is contained in:
antonio 2023-08-11 16:23:53 +02:00
parent 06e2729aca
commit e87eda2757
8 changed files with 449 additions and 78 deletions

View file

@ -52,4 +52,10 @@ class Download(@PrimaryKey override val id: String) : Child(id) {
originalWidth = child.originalWidth
originalHeight = child.originalHeight
}
}
}
@Keep
data class DownloadStack(
var id: String,
var view: String?
)

View file

@ -11,25 +11,35 @@ import androidx.recyclerview.widget.RecyclerView;
import com.cappielloantonio.tempo.R;
import com.cappielloantonio.tempo.databinding.ItemHorizontalDownloadBinding;
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
import com.cappielloantonio.tempo.interfaces.ClickCallback;
import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.MusicUtil;
import com.cappielloantonio.tempo.util.Util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@UnstableApi
public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHorizontalAdapter.ViewHolder> {
private final ClickCallback click;
private String view;
private String filterKey;
private String filterValue;
private List<Child> songs;
private List<Child> grouped;
public DownloadHorizontalAdapter(ClickCallback click) {
this.click = click;
this.view = Constants.DOWNLOAD_TYPE_TRACK;
this.songs = Collections.emptyList();
this.grouped = Collections.emptyList();
}
@NonNull
@ -41,31 +51,43 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Child song = songs.get(position);
holder.item.downloadedSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
holder.item.downloadedSongArtistTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
holder.item.downloadedSongAlbumTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
if (position > 0 && songs.get(position - 1) != null && !Objects.equals(songs.get(position - 1).getAlbum(), songs.get(position).getAlbum())) {
holder.item.divider.setPadding(0, (int) holder.itemView.getContext().getResources().getDimension(R.dimen.downloaded_item_padding), 0, 0);
} else {
if (position > 0) holder.item.divider.setVisibility(View.GONE);
switch (view) {
case Constants.DOWNLOAD_TYPE_TRACK:
initTrackLayout(holder, position);
break;
case Constants.DOWNLOAD_TYPE_ALBUM:
initAlbumLayout(holder, position);
break;
case Constants.DOWNLOAD_TYPE_ARTIST:
initArtistLayout(holder, position);
break;
case Constants.DOWNLOAD_TYPE_GENRE:
initGenreLayout(holder, position);
break;
case Constants.DOWNLOAD_TYPE_YEAR:
initYearLayout(holder, position);
break;
}
}
@Override
public int getItemCount() {
return songs.size();
return grouped.size();
}
public void setItems(List<Child> songs) {
public void setItems(String view, String filterKey, String filterValue, List<Child> songs) {
this.view = filterValue != null ? view : filterKey;
this.filterKey = filterKey;
this.filterValue = filterValue;
this.songs = songs;
this.grouped = groupSong(songs);
notifyDataSetChanged();
}
public Child getItem(int id) {
return songs.get(id);
return grouped.get(id);
}
@Override
@ -78,6 +100,145 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
return position;
}
private List<Child> groupSong(List<Child> songs) {
switch (view) {
case Constants.DOWNLOAD_TYPE_TRACK:
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getId())).filter(Util.distinctByKey(Child::getId)).collect(Collectors.toList()));
case Constants.DOWNLOAD_TYPE_ALBUM:
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getAlbumId())).filter(Util.distinctByKey(Child::getAlbumId)).collect(Collectors.toList()));
case Constants.DOWNLOAD_TYPE_ARTIST:
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getArtistId())).filter(Util.distinctByKey(Child::getArtistId)).collect(Collectors.toList()));
case Constants.DOWNLOAD_TYPE_GENRE:
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getGenre())).filter(Util.distinctByKey(Child::getGenre)).collect(Collectors.toList()));
case Constants.DOWNLOAD_TYPE_YEAR:
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getYear())).filter(Util.distinctByKey(Child::getYear)).collect(Collectors.toList()));
}
return Collections.emptyList();
}
private List<Child> filterSong(String filterKey, String filterValue, List<Child> songs) {
if (filterValue != null) {
switch (filterKey) {
case Constants.DOWNLOAD_TYPE_TRACK:
return songs.stream().filter(child -> child.getId().equals(filterValue)).collect(Collectors.toList());
case Constants.DOWNLOAD_TYPE_ALBUM:
return songs.stream().filter(child -> child.getAlbumId().equals(filterValue)).collect(Collectors.toList());
case Constants.DOWNLOAD_TYPE_GENRE:
return songs.stream().filter(child -> child.getGenre().equals(filterValue)).collect(Collectors.toList());
case Constants.DOWNLOAD_TYPE_YEAR:
return songs.stream().filter(child -> Objects.equals(child.getYear(), Integer.valueOf(filterValue))).collect(Collectors.toList());
case Constants.DOWNLOAD_TYPE_ARTIST:
return songs.stream().filter(child -> child.getArtistId().equals(filterValue)).collect(Collectors.toList());
}
}
return songs;
}
private String countSong(String filterKey, String filterValue, List<Child> songs) {
if (filterValue != null) {
switch (filterKey) {
case Constants.DOWNLOAD_TYPE_TRACK:
return String.valueOf(songs.stream().filter(child -> child.getId().equals(filterValue)).count());
case Constants.DOWNLOAD_TYPE_ALBUM:
return String.valueOf(songs.stream().filter(child -> child.getAlbumId().equals(filterValue)).count());
case Constants.DOWNLOAD_TYPE_GENRE:
return String.valueOf(songs.stream().filter(child -> child.getGenre().equals(filterValue)).count());
case Constants.DOWNLOAD_TYPE_YEAR:
return String.valueOf(songs.stream().filter(child -> Objects.equals(child.getYear(), Integer.valueOf(filterValue))).count());
case Constants.DOWNLOAD_TYPE_ARTIST:
return String.valueOf(songs.stream().filter(child -> child.getArtistId().equals(filterValue)).count());
}
}
return "0";
}
private void initTrackLayout(ViewHolder holder, int position) {
Child song = grouped.get(position);
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
holder.item.downloadedItemPreTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
CustomGlideRequest.Builder
.from(holder.itemView.getContext(), song.getCoverArtId())
.build()
.into(holder.item.itemCoverImageView);
holder.item.itemCoverImageView.setVisibility(View.VISIBLE);
holder.item.downloadedItemMoreButton.setVisibility(View.VISIBLE);
holder.item.divider.setVisibility(View.VISIBLE);
if (position > 0 && grouped.get(position - 1) != null && !Objects.equals(grouped.get(position - 1).getAlbum(), grouped.get(position).getAlbum())) {
holder.item.divider.setPadding(0, (int) holder.itemView.getContext().getResources().getDimension(R.dimen.downloaded_item_padding), 0, 0);
} else {
if (position > 0) holder.item.divider.setVisibility(View.GONE);
}
}
private void initAlbumLayout(ViewHolder holder, int position) {
Child song = grouped.get(position);
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_ALBUM, song.getAlbumId(), songs)));
holder.item.downloadedItemPreTextView.setText(MusicUtil.getReadableString(song.getArtist()));
CustomGlideRequest.Builder
.from(holder.itemView.getContext(), song.getCoverArtId())
.build()
.into(holder.item.itemCoverImageView);
holder.item.itemCoverImageView.setVisibility(View.VISIBLE);
holder.item.downloadedItemMoreButton.setVisibility(View.GONE);
holder.item.divider.setVisibility(View.VISIBLE);
if (position > 0 && grouped.get(position - 1) != null && !Objects.equals(grouped.get(position - 1).getArtist(), grouped.get(position).getArtist())) {
holder.item.divider.setPadding(0, (int) holder.itemView.getContext().getResources().getDimension(R.dimen.downloaded_item_padding), 0, 0);
} else {
if (position > 0) holder.item.divider.setVisibility(View.GONE);
}
}
private void initArtistLayout(ViewHolder holder, int position) {
Child song = grouped.get(position);
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getArtist()));
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_ARTIST, song.getArtistId(), songs)));
CustomGlideRequest.Builder
.from(holder.itemView.getContext(), song.getCoverArtId())
.build()
.into(holder.item.itemCoverImageView);
holder.item.itemCoverImageView.setVisibility(View.VISIBLE);
holder.item.downloadedItemMoreButton.setVisibility(View.GONE);
holder.item.divider.setVisibility(View.GONE);
}
private void initGenreLayout(ViewHolder holder, int position) {
Child song = grouped.get(position);
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getGenre()));
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_GENRE, song.getGenre(), songs)));
holder.item.itemCoverImageView.setVisibility(View.GONE);
holder.item.downloadedItemMoreButton.setVisibility(View.GONE);
holder.item.divider.setVisibility(View.GONE);
}
private void initYearLayout(ViewHolder holder, int position) {
Child song = grouped.get(position);
holder.item.downloadedItemTitleTextView.setText(String.valueOf(song.getYear()));
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_YEAR, song.getYear().toString(), songs)));
holder.item.itemCoverImageView.setVisibility(View.GONE);
holder.item.downloadedItemMoreButton.setVisibility(View.GONE);
holder.item.divider.setVisibility(View.GONE);
}
public class ViewHolder extends RecyclerView.ViewHolder {
ItemHorizontalDownloadBinding item;
@ -86,30 +247,62 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
this.item = item;
item.downloadedSongTitleTextView.setSelected(true);
item.downloadedSongArtistTextView.setSelected(true);
item.downloadedItemTitleTextView.setSelected(true);
item.downloadedItemSubtitleTextView.setSelected(true);
itemView.setOnClickListener(v -> onClick());
itemView.setOnLongClickListener(v -> onLongClick());
item.downloadedSongMoreButton.setOnClickListener(v -> onLongClick());
item.downloadedItemMoreButton.setOnClickListener(v -> onLongClick());
}
public void onClick() {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(songs));
bundle.putInt(Constants.ITEM_POSITION, getBindingAdapterPosition());
click.onMediaClick(bundle);
switch (view) {
case Constants.DOWNLOAD_TYPE_TRACK:
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(songs));
bundle.putInt(Constants.ITEM_POSITION, getBindingAdapterPosition());
click.onMediaClick(bundle);
break;
case Constants.DOWNLOAD_TYPE_ALBUM:
bundle.putString(Constants.DOWNLOAD_TYPE_ALBUM, grouped.get(getBindingAdapterPosition()).getAlbumId());
click.onAlbumClick(bundle);
break;
case Constants.DOWNLOAD_TYPE_ARTIST:
bundle.putString(Constants.DOWNLOAD_TYPE_ARTIST, grouped.get(getBindingAdapterPosition()).getArtistId());
click.onArtistClick(bundle);
break;
case Constants.DOWNLOAD_TYPE_GENRE:
bundle.putString(Constants.DOWNLOAD_TYPE_GENRE, grouped.get(getBindingAdapterPosition()).getGenre());
click.onGenreClick(bundle);
break;
case Constants.DOWNLOAD_TYPE_YEAR:
bundle.putString(Constants.DOWNLOAD_TYPE_YEAR, grouped.get(getBindingAdapterPosition()).getYear().toString());
click.onYearClick(bundle);
break;
}
}
private boolean onLongClick() {
Bundle bundle = new Bundle();
bundle.putParcelable(Constants.TRACK_OBJECT, songs.get(getBindingAdapterPosition()));
click.onMediaLongClick(bundle);
switch (view) {
case Constants.DOWNLOAD_TYPE_TRACK:
bundle.putParcelable(Constants.TRACK_OBJECT, grouped.get(getBindingAdapterPosition()));
click.onMediaLongClick(bundle);
return true;
case Constants.DOWNLOAD_TYPE_ALBUM:
bundle.putString(Constants.DOWNLOAD_TYPE_ALBUM, grouped.get(getBindingAdapterPosition()).getAlbumId());
click.onAlbumLongClick(bundle);
return true;
case Constants.DOWNLOAD_TYPE_ARTIST:
bundle.putString(Constants.DOWNLOAD_TYPE_ARTIST, grouped.get(getBindingAdapterPosition()).getArtistId());
click.onArtistLongClick(bundle);
return true;
}
return true;
return false;
}
}
}

View file

@ -5,6 +5,7 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupMenu;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -19,8 +20,10 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import com.cappielloantonio.tempo.R;
import com.cappielloantonio.tempo.databinding.FragmentDownloadBinding;
import com.cappielloantonio.tempo.interfaces.ClickCallback;
import com.cappielloantonio.tempo.model.DownloadStack;
import com.cappielloantonio.tempo.service.MediaManager;
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.adapter.DownloadHorizontalAdapter;
import com.cappielloantonio.tempo.util.Constants;
@ -28,6 +31,7 @@ import com.cappielloantonio.tempo.viewmodel.DownloadViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
import java.util.Objects;
@UnstableApi
@ -59,7 +63,7 @@ public class DownloadFragment extends Fragment implements ClickCallback {
super.onViewCreated(view, savedInstanceState);
initAppBar();
initDownloadedSongView();
initDownloadedView();
}
@Override
@ -90,11 +94,12 @@ public class DownloadFragment extends Fragment implements ClickCallback {
Objects.requireNonNull(materialToolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
}
private void initDownloadedSongView() {
bind.downloadedTracksRecyclerView.setHasFixedSize(true);
private void initDownloadedView() {
bind.downloadedRecyclerView.setHasFixedSize(true);
downloadHorizontalAdapter = new DownloadHorizontalAdapter(this);
bind.downloadedTracksRecyclerView.setAdapter(downloadHorizontalAdapter);
bind.downloadedRecyclerView.setAdapter(downloadHorizontalAdapter);
downloadViewModel.getDownloadedTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
if (songs != null) {
if (songs.isEmpty()) {
@ -102,26 +107,87 @@ public class DownloadFragment extends Fragment implements ClickCallback {
bind.emptyDownloadLayout.setVisibility(View.VISIBLE);
bind.fragmentDownloadNestedScrollView.setVisibility(View.GONE);
bind.downloadDownloadedTracksPlaceholder.placeholder.setVisibility(View.VISIBLE);
bind.downloadDownloadedTracksSector.setVisibility(View.GONE);
bind.downloadDownloadedPlaceholder.placeholder.setVisibility(View.VISIBLE);
bind.downloadDownloadedSector.setVisibility(View.GONE);
bind.downloadedGroupByImageView.setVisibility(View.GONE);
}
} else {
if (bind != null) {
bind.emptyDownloadLayout.setVisibility(View.GONE);
bind.fragmentDownloadNestedScrollView.setVisibility(View.VISIBLE);
bind.downloadDownloadedTracksPlaceholder.placeholder.setVisibility(View.GONE);
bind.downloadDownloadedTracksSector.setVisibility(View.VISIBLE);
bind.downloadDownloadedPlaceholder.placeholder.setVisibility(View.GONE);
bind.downloadDownloadedSector.setVisibility(View.VISIBLE);
bind.downloadedTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
bind.downloadedGroupByImageView.setVisibility(View.VISIBLE);
downloadHorizontalAdapter.setItems(songs);
finishDownloadView(songs);
}
}
if (bind != null) bind.loadingProgressBar.setVisibility(View.GONE);
}
});
bind.downloadedGroupByImageView.setOnClickListener(view -> showPopupMenu(view, R.menu.download_popup_menu));
bind.downloadedGoBackImageView.setOnClickListener(view -> downloadViewModel.popViewStack());
}
private void finishDownloadView(List<Child> songs) {
downloadViewModel.getViewStack().observe(getViewLifecycleOwner(), stack -> {
bind.downloadedRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
DownloadStack lastLevel = stack.get(stack.size() - 1);
switch (lastLevel.getId()) {
case Constants.DOWNLOAD_TYPE_TRACK:
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_TRACK, lastLevel.getId(), lastLevel.getView(), songs);
break;
case Constants.DOWNLOAD_TYPE_ALBUM:
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_TRACK, lastLevel.getId(), lastLevel.getView(), songs);
break;
case Constants.DOWNLOAD_TYPE_ARTIST:
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_ALBUM, lastLevel.getId(), lastLevel.getView(), songs);
break;
case Constants.DOWNLOAD_TYPE_GENRE:
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_TRACK, lastLevel.getId(), lastLevel.getView(), songs);
break;
case Constants.DOWNLOAD_TYPE_YEAR:
downloadHorizontalAdapter.setItems(Constants.DOWNLOAD_TYPE_TRACK, lastLevel.getId(), lastLevel.getView(), songs);
break;
}
bind.downloadedGoBackImageView.setVisibility(stack.size() > 1 ? View.VISIBLE : View.GONE);
});
}
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_download_group_by_track) {
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_TRACK, null));
return true;
} else if (menuItem.getItemId() == R.id.menu_download_group_by_album) {
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_ALBUM, null));
return true;
} else if (menuItem.getItemId() == R.id.menu_download_group_by_artist) {
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_ARTIST, null));
return true;
} else if (menuItem.getItemId() == R.id.menu_download_group_by_genre) {
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_GENRE, null));
return true;
} else if (menuItem.getItemId() == R.id.menu_download_group_by_year) {
downloadViewModel.initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_YEAR, null));
return true;
}
return false;
});
popup.show();
}
private void initializeMediaBrowser() {
@ -132,6 +198,26 @@ public class DownloadFragment extends Fragment implements ClickCallback {
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
}
@Override
public void onYearClick(Bundle bundle) {
downloadViewModel.pushViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_YEAR, bundle.getString(Constants.DOWNLOAD_TYPE_YEAR)));
}
@Override
public void onGenreClick(Bundle bundle) {
downloadViewModel.pushViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_GENRE, bundle.getString(Constants.DOWNLOAD_TYPE_GENRE)));
}
@Override
public void onArtistClick(Bundle bundle) {
downloadViewModel.pushViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_ARTIST, bundle.getString(Constants.DOWNLOAD_TYPE_ARTIST)));
}
@Override
public void onAlbumClick(Bundle bundle) {
downloadViewModel.pushViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_ALBUM, bundle.getString(Constants.DOWNLOAD_TYPE_ALBUM)));
}
@Override
public void onMediaClick(Bundle bundle) {
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));

View file

@ -0,0 +1,17 @@
package com.cappielloantonio.tempo.util;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
public class Util {
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
try {
Map<Object, Boolean> uniqueMap = new ConcurrentHashMap<>();
return t -> uniqueMap.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
} catch (NullPointerException exception) {
return null;
}
}
}

View file

@ -8,27 +8,55 @@ import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.cappielloantonio.tempo.model.DownloadStack;
import com.cappielloantonio.tempo.repository.DownloadRepository;
import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.util.Constants;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class DownloadViewModel extends AndroidViewModel {
private static final String TAG = "HomeViewModel";
private static final String TAG = "DownloadViewModel";
private final DownloadRepository downloadRepository;
private final MutableLiveData<List<Child>> downloadedTrackSample = new MutableLiveData<>(null);
private final MutableLiveData<ArrayList<DownloadStack>> viewStack = new MutableLiveData<>(null);
public DownloadViewModel(@NonNull Application application) {
super(application);
downloadRepository = new DownloadRepository();
initViewStack(new DownloadStack(Constants.DOWNLOAD_TYPE_TRACK, null));
}
public LiveData<List<Child>> getDownloadedTracks(LifecycleOwner owner) {
downloadRepository.getLiveDownload().observe(owner, downloads -> downloadedTrackSample.postValue(downloads.stream().map(download -> (Child) download).collect(Collectors.toList())));
return downloadedTrackSample;
}
public LiveData<ArrayList<DownloadStack>> getViewStack() {
return viewStack;
}
public void initViewStack(DownloadStack level) {
ArrayList<DownloadStack> stack = new ArrayList<>();
stack.add(level);
viewStack.setValue(stack);
}
public void pushViewStack(DownloadStack level) {
ArrayList<DownloadStack> stack = viewStack.getValue();
stack.add(level);
viewStack.setValue(stack);
}
public void popViewStack() {
ArrayList<DownloadStack> stack = viewStack.getValue();
stack.remove(stack.size() - 1);
viewStack.setValue(stack);
}
}

View file

@ -62,45 +62,67 @@
android:visibility="gone"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/download_linear_layout_container"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/download_downloaded_sector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingTop="16dp"
android:paddingBottom="@dimen/global_padding_bottom">
<!-- Downloaded tracks -->
<LinearLayout
android:id="@+id/download_downloaded_tracks_sector"
<TextView
android:id="@+id/downloaded_text_view_refreshable"
style="@style/TitleLarge"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/download_title_section"
android:layout_marginBottom="16dp"
app:layout_constraintEnd_toStartOf="@+id/downloaded_go_back_image_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/downloaded_go_back_image_view"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginHorizontal="12dp"
android:layout_gravity="center"
android:background="@drawable/ic_arrow_back"
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
app:layout_constraintEnd_toStartOf="@id/downloaded_group_by_image_view"
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
<ImageView
android:id="@+id/downloaded_group_by_image_view"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:background="@drawable/ic_filter_list"
app:layout_constraintBottom_toBottomOf="@+id/downloaded_text_view_refreshable"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/downloaded_text_view_refreshable" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/downloaded_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/downloaded_tracks_text_view_refreshable"
style="@style/TitleLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:text="@string/download_title_section" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/downloaded_tracks_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:paddingBottom="8dp" />
</LinearLayout>
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:paddingBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/downloaded_text_view_refreshable" />
<include
android:id="@+id/download_downloaded_tracks_placeholder"
android:id="@+id/download_downloaded_placeholder"
layout="@layout/item_placeholder_horizontal"
android:visibility="gone" />
</LinearLayout>
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/downloaded_text_view_refreshable" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -3,8 +3,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:orientation="horizontal"
android:paddingStart="16dp">
android:orientation="horizontal">
<LinearLayout
android:id="@+id/divider"
@ -17,7 +16,7 @@
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/downloaded_song_album_text_view"
android:id="@+id/downloaded_item_pre_text_view"
style="@style/LabelExtraSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -28,8 +27,20 @@
android:layout_gravity="center_vertical" />
</LinearLayout>
<ImageView
android:id="@+id/item_cover_image_view"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
app:layout_constraintEnd_toStartOf="@id/downloaded_item_title_text_view"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider" />
<TextView
android:id="@+id/downloaded_song_title_text_view"
android:id="@+id/downloaded_item_title_text_view"
style="@style/LabelMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
@ -37,12 +48,14 @@
android:paddingEnd="12dp"
android:singleLine="true"
android:text="@string/label_placeholder"
app:layout_constraintEnd_toStartOf="@+id/downloaded_song_more_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider" />
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintEnd_toStartOf="@+id/downloaded_item_more_button"
app:layout_constraintStart_toEndOf="@+id/item_cover_image_view"
app:layout_constraintTop_toTopOf="@+id/item_cover_image_view"
app:layout_constraintBottom_toTopOf="@id/downloaded_item_subtitle_text_view"/>
<TextView
android:id="@+id/downloaded_song_artist_text_view"
android:id="@+id/downloaded_item_subtitle_text_view"
style="@style/LabelSmall"
android:layout_width="0dp"
android:layout_height="wrap_content"
@ -51,16 +64,15 @@
android:paddingBottom="6dp"
android:singleLine="true"
android:text="@string/label_placeholder"
app:layout_constraintEnd_toStartOf="@+id/downloaded_song_more_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/downloaded_song_title_text_view" />
app:layout_constraintTop_toBottomOf="@id/downloaded_item_title_text_view"
app:layout_constraintEnd_toStartOf="@+id/downloaded_item_more_button"
app:layout_constraintStart_toStartOf="@+id/downloaded_item_title_text_view"
app:layout_constraintBottom_toBottomOf="@+id/item_cover_image_view" />
<ImageView
android:id="@+id/downloaded_song_more_button"
android:id="@+id/downloaded_item_more_button"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="10dp"
android:background="@drawable/ic_more_vert"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -55,6 +55,8 @@
<string name="delete_download_storage_dialog_title">Delete saved items</string>
<string name="download_info_empty_subtitle">Once you download a song, you\'ll find it here</string>
<string name="download_info_empty_title">No downloads yet!</string>
<string name="download_item_multiple_subtitle_formatter">%1$s • %2$s items</string>
<string name="download_item_single_subtitle_formatter">%1$s items</string>
<string name="download_title_section">Downloads</string>
<string name="download_storage_internal_dialog_negative_button">Internal</string>
<string name="download_storage_external_dialog_positive_button">External</string>
@ -280,4 +282,9 @@
<string name="description_empty_title">No description available</string>
<string name="menu_filter_download">Downloaded</string>
<string name="menu_filter_all">All</string>
<string name="menu_group_by_track">Track</string>
<string name="menu_group_by_album">Album</string>
<string name="menu_group_by_artist">Artist</string>
<string name="menu_group_by_genre">Genre</string>
<string name="menu_group_by_year">Year</string>
</resources>