mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 17:43:32 +00:00
feat: folder navigation
This commit is contained in:
parent
e85d7f9198
commit
24d2d201ad
29 changed files with 1238 additions and 9 deletions
|
|
@ -39,4 +39,16 @@ public interface ClickCallback {
|
||||||
default void onInternetRadioStationClick(Bundle bundle) {}
|
default void onInternetRadioStationClick(Bundle bundle) {}
|
||||||
|
|
||||||
default void onInternetRadioStationLongClick(Bundle bundle) {}
|
default void onInternetRadioStationLongClick(Bundle bundle) {}
|
||||||
|
|
||||||
|
default void onMusicFolderClick(Bundle bundle) {}
|
||||||
|
|
||||||
|
default void onMusicFolderLongClick(Bundle bundle) {}
|
||||||
|
|
||||||
|
default void onMusicDirectoryClick(Bundle bundle) {}
|
||||||
|
|
||||||
|
default void onMusicDirectoryLongClick(Bundle bundle) {}
|
||||||
|
|
||||||
|
default void onMusicIndexClick(Bundle bundle) {}
|
||||||
|
|
||||||
|
default void onMusicIndexLongClick(Bundle bundle) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.cappielloantonio.play.repository;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.App;
|
||||||
|
import com.cappielloantonio.play.subsonic.base.ApiResponse;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Directory;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Indexes;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.MusicFolder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class DirectoryRepository {
|
||||||
|
private static final String TAG = "DirectoryRepository";
|
||||||
|
|
||||||
|
public MutableLiveData<List<MusicFolder>> getMusicFolders() {
|
||||||
|
MutableLiveData<List<MusicFolder>> liveMusicFolders = new MutableLiveData<>();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getBrowsingClient()
|
||||||
|
.getMusicFolders()
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getMusicFolders() != null) {
|
||||||
|
liveMusicFolders.setValue(response.body().getSubsonicResponse().getMusicFolders().getMusicFolders());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return liveMusicFolders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MutableLiveData<Indexes> getIndexes(String musicFolderId, Long ifModifiedSince) {
|
||||||
|
MutableLiveData<Indexes> liveIndexes = new MutableLiveData<>();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getBrowsingClient()
|
||||||
|
.getIndexes(musicFolderId, ifModifiedSince)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getIndexes() != null) {
|
||||||
|
liveIndexes.setValue(response.body().getSubsonicResponse().getIndexes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return liveIndexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MutableLiveData<Directory> getMusicDirectory(String id) {
|
||||||
|
MutableLiveData<Directory> liveMusicDirectory = new MutableLiveData<>();
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getBrowsingClient()
|
||||||
|
.getMusicDirectory(id)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getDirectory() != null) {
|
||||||
|
liveMusicDirectory.setValue(response.body().getSubsonicResponse().getDirectory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return liveMusicDirectory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,9 +24,9 @@ public class BrowsingClient {
|
||||||
return browsingService.getMusicFolders(subsonic.getParams());
|
return browsingService.getMusicFolders(subsonic.getParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Call<ApiResponse> getIndexes() {
|
public Call<ApiResponse> getIndexes(String musicFolderId, Long ifModifiedSince) {
|
||||||
Log.d(TAG, "getIndexes()");
|
Log.d(TAG, "getIndexes()");
|
||||||
return browsingService.getIndexes(subsonic.getParams());
|
return browsingService.getIndexes(subsonic.getParams(), musicFolderId, ifModifiedSince);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Call<ApiResponse> getMusicDirectory(String id) {
|
public Call<ApiResponse> getMusicDirectory(String id) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ public interface BrowsingService {
|
||||||
Call<ApiResponse> getMusicFolders(@QueryMap Map<String, String> params);
|
Call<ApiResponse> getMusicFolders(@QueryMap Map<String, String> params);
|
||||||
|
|
||||||
@GET("getIndexes")
|
@GET("getIndexes")
|
||||||
Call<ApiResponse> getIndexes(@QueryMap Map<String, String> params);
|
Call<ApiResponse> getIndexes(@QueryMap Map<String, String> params, @Query("musicFolderId") String musicFolderId, @Query("ifModifiedSince") Long ifModifiedSince);
|
||||||
|
|
||||||
@GET("getMusicDirectory")
|
@GET("getMusicDirectory")
|
||||||
Call<ApiResponse> getMusicDirectory(@QueryMap Map<String, String> params, @Query("id") String id);
|
Call<ApiResponse> getMusicDirectory(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
package com.cappielloantonio.play.subsonic.models
|
package com.cappielloantonio.play.subsonic.models
|
||||||
|
|
||||||
import java.util.*
|
import android.os.Parcelable
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
class Artist {
|
@Parcelize
|
||||||
|
class Artist : Parcelable {
|
||||||
var id: String? = null
|
var id: String? = null
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
var starred: Date? = null
|
var starred: Date? = null
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
package com.cappielloantonio.play.subsonic.models
|
package com.cappielloantonio.play.subsonic.models
|
||||||
|
|
||||||
import java.util.*
|
import android.os.Parcelable
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
class Directory {
|
@Parcelize
|
||||||
|
class Directory : Parcelable {
|
||||||
|
@SerializedName("child")
|
||||||
var children: List<Child>? = null
|
var children: List<Child>? = null
|
||||||
var id: String? = null
|
var id: String? = null
|
||||||
|
@SerializedName("parent")
|
||||||
var parentId: String? = null
|
var parentId: String? = null
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
var starred: Date? = null
|
var starred: Date? = null
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package com.cappielloantonio.play.subsonic.models
|
package com.cappielloantonio.play.subsonic.models
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
class Index {
|
class Index {
|
||||||
|
@SerializedName("artist")
|
||||||
var artists: List<Artist>? = null
|
var artists: List<Artist>? = null
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
package com.cappielloantonio.play.subsonic.models
|
package com.cappielloantonio.play.subsonic.models
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
class Indexes {
|
class Indexes {
|
||||||
var shortcuts: List<Artist>? = null
|
var shortcuts: List<Artist>? = null
|
||||||
|
@SerializedName("index")
|
||||||
var indices: List<Index>? = null
|
var indices: List<Index>? = null
|
||||||
var children: List<Child>? = null
|
var children: List<Child>? = null
|
||||||
var lastModified: Long = 0
|
var lastModified: Long = 0
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package com.cappielloantonio.play.subsonic.models
|
package com.cappielloantonio.play.subsonic.models
|
||||||
|
|
||||||
class MusicFolder {
|
import android.os.Parcelable
|
||||||
var id = 0
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
class MusicFolder : Parcelable {
|
||||||
|
var id: String? = null
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
package com.cappielloantonio.play.subsonic.models
|
package com.cappielloantonio.play.subsonic.models
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
class MusicFolders {
|
class MusicFolders {
|
||||||
|
@SerializedName("musicFolder")
|
||||||
var musicFolders: List<MusicFolder>? = null
|
var musicFolders: List<MusicFolder>? = null
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.cappielloantonio.play.ui.adapter;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.databinding.ItemLibraryMusicDirectoryBinding;
|
||||||
|
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
||||||
|
import com.cappielloantonio.play.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Child;
|
||||||
|
import com.cappielloantonio.play.util.Constants;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAdapter.ViewHolder> {
|
||||||
|
private final ClickCallback click;
|
||||||
|
|
||||||
|
private List<Child> children;
|
||||||
|
|
||||||
|
public MusicDirectoryAdapter(ClickCallback click) {
|
||||||
|
this.click = click;
|
||||||
|
this.children = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
ItemLibraryMusicDirectoryBinding view = ItemLibraryMusicDirectoryBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
|
Child child = children.get(position);
|
||||||
|
|
||||||
|
holder.item.musicDirectoryTitleTextView.setText(child.getTitle());
|
||||||
|
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(holder.itemView.getContext(), child.getCoverArtId())
|
||||||
|
.build()
|
||||||
|
.into(holder.item.musicDirectoryCoverImageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return children.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItems(List<Child> children) {
|
||||||
|
this.children = children != null ? children : Collections.emptyList();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
ItemLibraryMusicDirectoryBinding item;
|
||||||
|
|
||||||
|
ViewHolder(ItemLibraryMusicDirectoryBinding item) {
|
||||||
|
super(item.getRoot());
|
||||||
|
|
||||||
|
this.item = item;
|
||||||
|
|
||||||
|
item.musicDirectoryTitleTextView.setSelected(true);
|
||||||
|
|
||||||
|
itemView.setOnClickListener(v -> onClick());
|
||||||
|
itemView.setOnLongClickListener(v -> onLongClick());
|
||||||
|
|
||||||
|
item.musicDirectoryMoreButton.setOnClickListener(v -> onLongClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
|
||||||
|
if (children.get(getBindingAdapterPosition()).isDir()) {
|
||||||
|
bundle.putParcelable(Constants.MUSIC_DIRECTORY_OBJECT, children.get(getBindingAdapterPosition()));
|
||||||
|
click.onMusicDirectoryClick(bundle);
|
||||||
|
} else {
|
||||||
|
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(children));
|
||||||
|
bundle.putInt(Constants.ITEM_POSITION, getBindingAdapterPosition());
|
||||||
|
click.onMediaClick(bundle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onLongClick() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(Constants.MUSIC_DIRECTORY_OBJECT, children.get(getBindingAdapterPosition()));
|
||||||
|
|
||||||
|
click.onMusicDirectoryLongClick(bundle);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
package com.cappielloantonio.play.ui.adapter;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.databinding.ItemLibraryMusicFolderBinding;
|
||||||
|
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
||||||
|
import com.cappielloantonio.play.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.MusicFolder;
|
||||||
|
import com.cappielloantonio.play.util.Constants;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
public class MusicFolderAdapter extends RecyclerView.Adapter<MusicFolderAdapter.ViewHolder> {
|
||||||
|
private final ClickCallback click;
|
||||||
|
|
||||||
|
private List<MusicFolder> musicFolders;
|
||||||
|
|
||||||
|
public MusicFolderAdapter(ClickCallback click) {
|
||||||
|
this.click = click;
|
||||||
|
this.musicFolders = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
ItemLibraryMusicFolderBinding view = ItemLibraryMusicFolderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
|
MusicFolder musicFolder = musicFolders.get(position);
|
||||||
|
|
||||||
|
holder.item.musicFolderTitleTextView.setText(musicFolder.getName());
|
||||||
|
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(holder.itemView.getContext(), musicFolder.getName())
|
||||||
|
.build()
|
||||||
|
.into(holder.item.musicFolderCoverImageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return musicFolders.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItems(List<MusicFolder> musicFolders) {
|
||||||
|
this.musicFolders = musicFolders;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MusicFolder getItem(int position) {
|
||||||
|
return musicFolders.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
ItemLibraryMusicFolderBinding item;
|
||||||
|
|
||||||
|
ViewHolder(ItemLibraryMusicFolderBinding item) {
|
||||||
|
super(item.getRoot());
|
||||||
|
|
||||||
|
this.item = item;
|
||||||
|
|
||||||
|
item.musicFolderTitleTextView.setSelected(true);
|
||||||
|
|
||||||
|
itemView.setOnClickListener(v -> onClick());
|
||||||
|
itemView.setOnLongClickListener(v -> onLongClick());
|
||||||
|
|
||||||
|
item.musicFolderMoreButton.setOnClickListener(v -> onLongClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(Constants.MUSIC_FOLDER_OBJECT, musicFolders.get(getBindingAdapterPosition()));
|
||||||
|
click.onMusicFolderClick(bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onLongClick() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(Constants.MUSIC_FOLDER_OBJECT, musicFolders.get(getBindingAdapterPosition()));
|
||||||
|
|
||||||
|
click.onMusicFolderLongClick(bundle);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.cappielloantonio.play.ui.adapter;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.databinding.ItemLibraryMusicIndexBinding;
|
||||||
|
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
||||||
|
import com.cappielloantonio.play.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Artist;
|
||||||
|
import com.cappielloantonio.play.util.Constants;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.ViewHolder> {
|
||||||
|
private final ClickCallback click;
|
||||||
|
|
||||||
|
private List<Artist> artists;
|
||||||
|
|
||||||
|
public MusicIndexAdapter(ClickCallback click) {
|
||||||
|
this.click = click;
|
||||||
|
this.artists = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
ItemLibraryMusicIndexBinding view = ItemLibraryMusicIndexBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
|
Artist artist = artists.get(position);
|
||||||
|
|
||||||
|
holder.item.musicIndexTitleTextView.setText(artist.getName());
|
||||||
|
|
||||||
|
CustomGlideRequest.Builder
|
||||||
|
.from(holder.itemView.getContext(), artist.getName())
|
||||||
|
.build()
|
||||||
|
.into(holder.item.musicIndexCoverImageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return artists.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItems(List<Artist> artists) {
|
||||||
|
this.artists = artists;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
ItemLibraryMusicIndexBinding item;
|
||||||
|
|
||||||
|
ViewHolder(ItemLibraryMusicIndexBinding item) {
|
||||||
|
super(item.getRoot());
|
||||||
|
|
||||||
|
this.item = item;
|
||||||
|
|
||||||
|
item.musicIndexTitleTextView.setSelected(true);
|
||||||
|
|
||||||
|
itemView.setOnClickListener(v -> onClick());
|
||||||
|
itemView.setOnLongClickListener(v -> onLongClick());
|
||||||
|
|
||||||
|
item.musicIndexMoreButton.setOnClickListener(v -> onLongClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(Constants.MUSIC_INDEX_OBJECT, artists.get(getBindingAdapterPosition()));
|
||||||
|
click.onMusicIndexClick(bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onLongClick() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(Constants.MUSIC_INDEX_OBJECT, artists.get(getBindingAdapterPosition()));
|
||||||
|
|
||||||
|
click.onMusicIndexLongClick(bundle);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
package com.cappielloantonio.play.ui.fragment;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.media3.session.MediaBrowser;
|
||||||
|
import androidx.media3.session.SessionToken;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.R;
|
||||||
|
import com.cappielloantonio.play.databinding.FragmentDirectoryBinding;
|
||||||
|
import com.cappielloantonio.play.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.play.service.MediaManager;
|
||||||
|
import com.cappielloantonio.play.service.MediaService;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Artist;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Child;
|
||||||
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.play.ui.adapter.MusicDirectoryAdapter;
|
||||||
|
import com.cappielloantonio.play.util.Constants;
|
||||||
|
import com.cappielloantonio.play.viewmodel.DirectoryViewModel;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
public class DirectoryFragment extends Fragment implements ClickCallback {
|
||||||
|
private static final String TAG = "DirectoryFragment";
|
||||||
|
|
||||||
|
private FragmentDirectoryBinding bind;
|
||||||
|
private MainActivity activity;
|
||||||
|
private DirectoryViewModel directoryViewModel;
|
||||||
|
|
||||||
|
private MusicDirectoryAdapter musicDirectoryAdapter;
|
||||||
|
|
||||||
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
activity = (MainActivity) getActivity();
|
||||||
|
|
||||||
|
bind = FragmentDirectoryBinding.inflate(inflater, container, false);
|
||||||
|
View view = bind.getRoot();
|
||||||
|
directoryViewModel = new ViewModelProvider(requireActivity()).get(DirectoryViewModel.class);
|
||||||
|
|
||||||
|
initAppBar();
|
||||||
|
initButtons();
|
||||||
|
initDirectoryListView();
|
||||||
|
init();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
initializeMediaBrowser();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
releaseMediaBrowser();
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
bind = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
Artist artist = getArguments().getParcelable(Constants.MUSIC_INDEX_OBJECT);
|
||||||
|
|
||||||
|
if (artist != null) {
|
||||||
|
directoryViewModel.setMusicDirectoryId(artist.getId());
|
||||||
|
directoryViewModel.setMusicDirectoryName(artist.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
directoryViewModel.loadMusicDirectory(getViewLifecycleOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAppBar() {
|
||||||
|
activity.setSupportActionBar(bind.toolbar);
|
||||||
|
|
||||||
|
if (activity.getSupportActionBar() != null) {
|
||||||
|
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind != null)
|
||||||
|
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
||||||
|
|
||||||
|
if (bind != null)
|
||||||
|
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||||
|
if ((bind.directoryInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
||||||
|
directoryViewModel.getDirectory().observe(getViewLifecycleOwner(), directory -> {
|
||||||
|
if (directory != null) {
|
||||||
|
bind.toolbar.setTitle(directory.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
bind.toolbar.setTitle(R.string.empty_string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
directoryViewModel.getDirectory().observe(getViewLifecycleOwner(), directory -> {
|
||||||
|
if (directory != null) {
|
||||||
|
bind.directoryTitleLabel.setText(directory.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initButtons() {
|
||||||
|
directoryViewModel.getDirectory().observe(getViewLifecycleOwner(), directory -> {
|
||||||
|
if (directory != null && directory.getParentId() != null && !Objects.equals(directory.getParentId(), "-1")) {
|
||||||
|
bind.directoryBackImageView.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
bind.directoryBackImageView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
bind.directoryBackImageView.setOnClickListener(v -> directoryViewModel.goBack());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDirectoryListView() {
|
||||||
|
bind.directoryRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
bind.directoryRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
|
musicDirectoryAdapter = new MusicDirectoryAdapter(this);
|
||||||
|
bind.directoryRecyclerView.setAdapter(musicDirectoryAdapter);
|
||||||
|
|
||||||
|
directoryViewModel.getDirectory().observe(getViewLifecycleOwner(), directory -> {
|
||||||
|
if (directory != null) {
|
||||||
|
musicDirectoryAdapter.setItems(directory.getChildren());
|
||||||
|
} else {
|
||||||
|
musicDirectoryAdapter.setItems(Collections.emptyList());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeMediaBrowser() {
|
||||||
|
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseMediaBrowser() {
|
||||||
|
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMediaClick(Bundle bundle) {
|
||||||
|
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMusicDirectoryClick(Bundle bundle) {
|
||||||
|
Child child = bundle.getParcelable(Constants.MUSIC_DIRECTORY_OBJECT);
|
||||||
|
|
||||||
|
if (child != null) {
|
||||||
|
directoryViewModel.setMusicDirectoryId(child.getId());
|
||||||
|
directoryViewModel.setMusicDirectoryName(child.getTitle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMusicDirectoryLongClick(Bundle bundle) {
|
||||||
|
Toast.makeText(requireContext(), "Long click!", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
package com.cappielloantonio.play.ui.fragment;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.navigation.Navigation;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.R;
|
||||||
|
import com.cappielloantonio.play.databinding.FragmentIndexBinding;
|
||||||
|
import com.cappielloantonio.play.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.MusicFolder;
|
||||||
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.play.ui.adapter.MusicIndexAdapter;
|
||||||
|
import com.cappielloantonio.play.util.Constants;
|
||||||
|
import com.cappielloantonio.play.util.IndexUtil;
|
||||||
|
import com.cappielloantonio.play.viewmodel.IndexViewModel;
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
public class IndexFragment extends Fragment implements ClickCallback {
|
||||||
|
private static final String TAG = "IndexFragment";
|
||||||
|
|
||||||
|
private FragmentIndexBinding bind;
|
||||||
|
private MainActivity activity;
|
||||||
|
private IndexViewModel indexViewModel;
|
||||||
|
|
||||||
|
private MusicIndexAdapter musicIndexAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
activity = (MainActivity) getActivity();
|
||||||
|
|
||||||
|
bind = FragmentIndexBinding.inflate(inflater, container, false);
|
||||||
|
View view = bind.getRoot();
|
||||||
|
indexViewModel = new ViewModelProvider(requireActivity()).get(IndexViewModel.class);
|
||||||
|
|
||||||
|
initAppBar();
|
||||||
|
initDirectoryListView();
|
||||||
|
init();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
bind = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
MusicFolder musicFolder = getArguments().getParcelable(Constants.MUSIC_FOLDER_OBJECT);
|
||||||
|
|
||||||
|
if (musicFolder != null) {
|
||||||
|
indexViewModel.setMusicFolder(musicFolder);
|
||||||
|
bind.indexTitleLabel.setText(musicFolder.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAppBar() {
|
||||||
|
activity.setSupportActionBar(bind.toolbar);
|
||||||
|
|
||||||
|
if (activity.getSupportActionBar() != null) {
|
||||||
|
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
activity.getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind != null)
|
||||||
|
bind.toolbar.setNavigationOnClickListener(v -> activity.navController.navigateUp());
|
||||||
|
|
||||||
|
if (bind != null)
|
||||||
|
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||||
|
if ((bind.indexInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
||||||
|
bind.toolbar.setTitle(indexViewModel.getMusicFolderName());
|
||||||
|
} else {
|
||||||
|
bind.toolbar.setTitle(R.string.empty_string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDirectoryListView() {
|
||||||
|
bind.indexRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
bind.indexRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
|
musicIndexAdapter = new MusicIndexAdapter(this);
|
||||||
|
bind.indexRecyclerView.setAdapter(musicIndexAdapter);
|
||||||
|
|
||||||
|
indexViewModel.getIndexes().observe(getViewLifecycleOwner(), indexes -> {
|
||||||
|
if (indexes != null) {
|
||||||
|
musicIndexAdapter.setItems(IndexUtil.getArtist(indexes));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMusicIndexClick(Bundle bundle) {
|
||||||
|
Navigation.findNavController(requireView()).navigate(R.id.directoryFragment, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMusicIndexLongClick(Bundle bundle) {
|
||||||
|
Toast.makeText(requireContext(), "Long click!", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
package com.cappielloantonio.play.ui.fragment;
|
package com.cappielloantonio.play.ui.fragment;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
@ -25,6 +27,7 @@ import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.play.ui.adapter.AlbumAdapter;
|
import com.cappielloantonio.play.ui.adapter.AlbumAdapter;
|
||||||
import com.cappielloantonio.play.ui.adapter.ArtistAdapter;
|
import com.cappielloantonio.play.ui.adapter.ArtistAdapter;
|
||||||
import com.cappielloantonio.play.ui.adapter.GenreAdapter;
|
import com.cappielloantonio.play.ui.adapter.GenreAdapter;
|
||||||
|
import com.cappielloantonio.play.ui.adapter.MusicFolderAdapter;
|
||||||
import com.cappielloantonio.play.ui.adapter.PlaylistHorizontalAdapter;
|
import com.cappielloantonio.play.ui.adapter.PlaylistHorizontalAdapter;
|
||||||
import com.cappielloantonio.play.ui.dialog.PlaylistEditorDialog;
|
import com.cappielloantonio.play.ui.dialog.PlaylistEditorDialog;
|
||||||
import com.cappielloantonio.play.util.Constants;
|
import com.cappielloantonio.play.util.Constants;
|
||||||
|
|
@ -35,10 +38,13 @@ import java.util.Objects;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class LibraryFragment extends Fragment implements ClickCallback {
|
public class LibraryFragment extends Fragment implements ClickCallback {
|
||||||
|
private static final String TAG = "LibraryFragment";
|
||||||
|
|
||||||
private FragmentLibraryBinding bind;
|
private FragmentLibraryBinding bind;
|
||||||
private MainActivity activity;
|
private MainActivity activity;
|
||||||
private LibraryViewModel libraryViewModel;
|
private LibraryViewModel libraryViewModel;
|
||||||
|
|
||||||
|
private MusicFolderAdapter musicFolderAdapter;
|
||||||
private AlbumAdapter albumAdapter;
|
private AlbumAdapter albumAdapter;
|
||||||
private ArtistAdapter artistAdapter;
|
private ArtistAdapter artistAdapter;
|
||||||
private GenreAdapter genreAdapter;
|
private GenreAdapter genreAdapter;
|
||||||
|
|
@ -77,6 +83,7 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
initAppBar();
|
initAppBar();
|
||||||
|
initMusicFolderView();
|
||||||
initAlbumView();
|
initAlbumView();
|
||||||
initArtistView();
|
initArtistView();
|
||||||
initGenreView();
|
initGenreView();
|
||||||
|
|
@ -141,6 +148,28 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||||
Objects.requireNonNull(bind.toolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
Objects.requireNonNull(bind.toolbar.getOverflowIcon()).setTint(requireContext().getResources().getColor(R.color.titleTextColor, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initMusicFolderView() {
|
||||||
|
bind.musicFolderRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
bind.musicFolderRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
|
musicFolderAdapter = new MusicFolderAdapter(this);
|
||||||
|
bind.musicFolderRecyclerView.setAdapter(musicFolderAdapter);
|
||||||
|
libraryViewModel.getMusicFolders(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), musicFolders -> {
|
||||||
|
if (musicFolders == null) {
|
||||||
|
if (bind != null)
|
||||||
|
bind.libraryMusicFolderPlaceholder.placeholder.setVisibility(View.VISIBLE);
|
||||||
|
if (bind != null) bind.libraryMusicFolderSector.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
if (bind != null)
|
||||||
|
bind.libraryMusicFolderPlaceholder.placeholder.setVisibility(View.GONE);
|
||||||
|
if (bind != null)
|
||||||
|
bind.libraryMusicFolderSector.setVisibility(!musicFolders.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
musicFolderAdapter.setItems(musicFolders);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void initAlbumView() {
|
private void initAlbumView() {
|
||||||
bind.albumRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
bind.albumRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
bind.albumRecyclerView.setHasFixedSize(true);
|
bind.albumRecyclerView.setHasFixedSize(true);
|
||||||
|
|
@ -273,4 +302,14 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||||
dialog.setArguments(bundle);
|
dialog.setArguments(bundle);
|
||||||
dialog.show(activity.getSupportFragmentManager(), null);
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMusicFolderClick(Bundle bundle) {
|
||||||
|
Navigation.findNavController(requireView()).navigate(R.id.indexFragment, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMusicFolderLongClick(Bundle bundle) {
|
||||||
|
Toast.makeText(requireContext(), "Long click!", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ object Constants {
|
||||||
const val PODCAST_OBJECT = "PODCAST_OBJECT"
|
const val PODCAST_OBJECT = "PODCAST_OBJECT"
|
||||||
const val PODCAST_CHANNEL_OBJECT = "PODCAST_CHANNEL_OBJECT"
|
const val PODCAST_CHANNEL_OBJECT = "PODCAST_CHANNEL_OBJECT"
|
||||||
const val INTERNET_RADIO_STATION_OBJECT = "INTERNET_RADIO_STATION_OBJECT"
|
const val INTERNET_RADIO_STATION_OBJECT = "INTERNET_RADIO_STATION_OBJECT"
|
||||||
|
const val MUSIC_FOLDER_OBJECT = "MUSIC_FOLDER_OBJECT"
|
||||||
|
const val MUSIC_DIRECTORY_OBJECT = "MUSIC_DIRECTORY_OBJECT"
|
||||||
|
const val MUSIC_INDEX_OBJECT = "MUSIC_DIRECTORY_OBJECT"
|
||||||
|
|
||||||
const val ALBUM_RECENTLY_PLAYED = "ALBUM_RECENTLY_PLAYED"
|
const val ALBUM_RECENTLY_PLAYED = "ALBUM_RECENTLY_PLAYED"
|
||||||
const val ALBUM_MOST_PLAYED = "ALBUM_MOST_PLAYED"
|
const val ALBUM_MOST_PLAYED = "ALBUM_MOST_PLAYED"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.cappielloantonio.play.util;
|
||||||
|
|
||||||
|
import androidx.annotation.OptIn;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Artist;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Index;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Indexes;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
|
public class IndexUtil {
|
||||||
|
public static List<Artist> getArtist(Indexes indexes) {
|
||||||
|
if (indexes.getIndices() == null) return Collections.emptyList();
|
||||||
|
|
||||||
|
ArrayList<Artist> toReturn = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Index index : indexes.getIndices()) {
|
||||||
|
if (index.getArtists() != null) {
|
||||||
|
toReturn.addAll(index.getArtists());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.cappielloantonio.play.viewmodel;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.repository.DirectoryRepository;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Directory;
|
||||||
|
|
||||||
|
public class DirectoryViewModel extends AndroidViewModel {
|
||||||
|
private final DirectoryRepository directoryRepository;
|
||||||
|
|
||||||
|
private MutableLiveData<String> id = new MutableLiveData<>(null);
|
||||||
|
private MutableLiveData<String> name = new MutableLiveData<>(null);
|
||||||
|
|
||||||
|
private MutableLiveData<Directory> directory = new MutableLiveData<>(null);
|
||||||
|
|
||||||
|
public DirectoryViewModel(@NonNull Application application) {
|
||||||
|
super(application);
|
||||||
|
|
||||||
|
directoryRepository = new DirectoryRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<Directory> getDirectory() {
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMusicDirectoryId(String id) {
|
||||||
|
this.id.setValue(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMusicDirectoryName(String name) {
|
||||||
|
this.name.setValue(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadMusicDirectory(LifecycleOwner owner) {
|
||||||
|
this.id.observe(owner, id -> directoryRepository.getMusicDirectory(id).observe(owner, directory -> this.directory.setValue(directory)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void goBack() {
|
||||||
|
this.id.setValue(this.directory.getValue().getParentId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.cappielloantonio.play.viewmodel;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.repository.DirectoryRepository;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Indexes;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.MusicFolder;
|
||||||
|
|
||||||
|
public class IndexViewModel extends AndroidViewModel {
|
||||||
|
private final DirectoryRepository directoryRepository;
|
||||||
|
|
||||||
|
private MusicFolder musicFolder;
|
||||||
|
|
||||||
|
private MutableLiveData<Indexes> indexes = new MutableLiveData<>(null);
|
||||||
|
|
||||||
|
public IndexViewModel(@NonNull Application application) {
|
||||||
|
super(application);
|
||||||
|
|
||||||
|
directoryRepository = new DirectoryRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MutableLiveData<Indexes> getIndexes() {
|
||||||
|
return directoryRepository.getIndexes(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMusicFolderName() {
|
||||||
|
return musicFolder != null ? musicFolder.getName() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMusicFolder(MusicFolder musicFolder) {
|
||||||
|
this.musicFolder = musicFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,11 +10,14 @@ import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import com.cappielloantonio.play.repository.AlbumRepository;
|
import com.cappielloantonio.play.repository.AlbumRepository;
|
||||||
import com.cappielloantonio.play.repository.ArtistRepository;
|
import com.cappielloantonio.play.repository.ArtistRepository;
|
||||||
|
import com.cappielloantonio.play.repository.DirectoryRepository;
|
||||||
import com.cappielloantonio.play.repository.GenreRepository;
|
import com.cappielloantonio.play.repository.GenreRepository;
|
||||||
import com.cappielloantonio.play.repository.PlaylistRepository;
|
import com.cappielloantonio.play.repository.PlaylistRepository;
|
||||||
import com.cappielloantonio.play.subsonic.models.AlbumID3;
|
import com.cappielloantonio.play.subsonic.models.AlbumID3;
|
||||||
import com.cappielloantonio.play.subsonic.models.ArtistID3;
|
import com.cappielloantonio.play.subsonic.models.ArtistID3;
|
||||||
import com.cappielloantonio.play.subsonic.models.Genre;
|
import com.cappielloantonio.play.subsonic.models.Genre;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.Indexes;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.MusicFolder;
|
||||||
import com.cappielloantonio.play.subsonic.models.Playlist;
|
import com.cappielloantonio.play.subsonic.models.Playlist;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -22,11 +25,14 @@ import java.util.List;
|
||||||
public class LibraryViewModel extends AndroidViewModel {
|
public class LibraryViewModel extends AndroidViewModel {
|
||||||
private static final String TAG = "LibraryViewModel";
|
private static final String TAG = "LibraryViewModel";
|
||||||
|
|
||||||
|
private final DirectoryRepository directoryRepository;
|
||||||
private final AlbumRepository albumRepository;
|
private final AlbumRepository albumRepository;
|
||||||
private final ArtistRepository artistRepository;
|
private final ArtistRepository artistRepository;
|
||||||
private final GenreRepository genreRepository;
|
private final GenreRepository genreRepository;
|
||||||
private final PlaylistRepository playlistRepository;
|
private final PlaylistRepository playlistRepository;
|
||||||
|
|
||||||
|
private final MutableLiveData<List<MusicFolder>> musicFolders = new MutableLiveData<>(null);
|
||||||
|
private final MutableLiveData<Indexes> indexes = new MutableLiveData<>(null);
|
||||||
private final MutableLiveData<List<Playlist>> playlistSample = new MutableLiveData<>(null);
|
private final MutableLiveData<List<Playlist>> playlistSample = new MutableLiveData<>(null);
|
||||||
private final MutableLiveData<List<AlbumID3>> sampleAlbum = new MutableLiveData<>(null);
|
private final MutableLiveData<List<AlbumID3>> sampleAlbum = new MutableLiveData<>(null);
|
||||||
private final MutableLiveData<List<ArtistID3>> sampleArtist = new MutableLiveData<>(null);
|
private final MutableLiveData<List<ArtistID3>> sampleArtist = new MutableLiveData<>(null);
|
||||||
|
|
@ -35,12 +41,29 @@ public class LibraryViewModel extends AndroidViewModel {
|
||||||
public LibraryViewModel(@NonNull Application application) {
|
public LibraryViewModel(@NonNull Application application) {
|
||||||
super(application);
|
super(application);
|
||||||
|
|
||||||
|
directoryRepository = new DirectoryRepository();
|
||||||
albumRepository = new AlbumRepository();
|
albumRepository = new AlbumRepository();
|
||||||
artistRepository = new ArtistRepository();
|
artistRepository = new ArtistRepository();
|
||||||
genreRepository = new GenreRepository();
|
genreRepository = new GenreRepository();
|
||||||
playlistRepository = new PlaylistRepository();
|
playlistRepository = new PlaylistRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<List<MusicFolder>> getMusicFolders(LifecycleOwner owner) {
|
||||||
|
if (musicFolders.getValue() == null) {
|
||||||
|
directoryRepository.getMusicFolders().observe(owner, musicFolders::postValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return musicFolders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<Indexes> getIndexes(LifecycleOwner owner) {
|
||||||
|
if (indexes.getValue() == null) {
|
||||||
|
directoryRepository.getIndexes("0", null).observe(owner, indexes::postValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return indexes;
|
||||||
|
}
|
||||||
|
|
||||||
public LiveData<List<AlbumID3>> getAlbumSample(LifecycleOwner owner) {
|
public LiveData<List<AlbumID3>> getAlbumSample(LifecycleOwner owner) {
|
||||||
if (sampleAlbum.getValue() == null) {
|
if (sampleAlbum.getValue() == null) {
|
||||||
albumRepository.getAlbums("random", 10, null, null).observe(owner, sampleAlbum::postValue);
|
albumRepository.getAlbums("random", 10, null, null).observe(owner, sampleAlbum::postValue);
|
||||||
|
|
|
||||||
76
app/src/main/res/layout/fragment_directory.xml
Normal file
76
app/src/main/res/layout/fragment_directory.xml
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_collapseMode="pin"
|
||||||
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="8dp">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/app_bar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/directory_info_sector"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="172dp"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
android:clipChildren="false"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/directory_title_label"
|
||||||
|
style="@style/TitleLarge"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="24dp"
|
||||||
|
android:text="@string/label_placeholder"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/directory_back_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"
|
||||||
|
app:icon="@drawable/ic_arrow_back"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/directory_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
59
app/src/main/res/layout/fragment_index.xml
Normal file
59
app/src/main/res/layout/fragment_index.xml
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_collapseMode="pin"
|
||||||
|
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="8dp">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/app_bar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/index_info_sector"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="172dp"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
android:clipChildren="false"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/index_title_label"
|
||||||
|
style="@style/TitleLarge"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="24dp"
|
||||||
|
android:text="@string/label_placeholder"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/index_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
@ -52,6 +52,41 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingBottom="@dimen/global_padding_bottom">
|
android:paddingBottom="@dimen/global_padding_bottom">
|
||||||
|
|
||||||
|
<!-- Music Folder -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/library_music_folder_sector"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TitleLarge"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:text="@string/library_title_music_folder" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/music_folder_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingBottom="8dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/library_music_folder_placeholder"
|
||||||
|
layout="@layout/item_placeholder_album"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<!-- Album -->
|
<!-- Album -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/library_album_sector"
|
android:id="@+id/library_album_sector"
|
||||||
|
|
@ -67,6 +102,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingStart="8dp"
|
android:paddingStart="8dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
android:paddingEnd="8dp">
|
android:paddingEnd="8dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|
|
||||||
56
app/src/main/res/layout/item_library_music_directory.xml
Normal file
56
app/src/main/res/layout/item_library_music_directory.xml
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/music_directory_cover_image_view"
|
||||||
|
android:layout_width="52dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_margin="2dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/cover_image_separator"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/music_directory_cover_image_view"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/music_directory_title_text_view"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/music_directory_cover_image_view"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_directory_cover_image_view" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/music_directory_title_text_view"
|
||||||
|
style="@style/LabelMedium"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/label_placeholder"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/music_directory_cover_image_view"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/music_directory_more_button"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/cover_image_separator"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_directory_cover_image_view" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/music_directory_more_button"
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:background="@drawable/ic_more_vert"
|
||||||
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/music_directory_title_text_view"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_directory_title_text_view" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
52
app/src/main/res/layout/item_library_music_folder.xml
Normal file
52
app/src/main/res/layout/item_library_music_folder.xml
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/music_folder_cover_image_view"
|
||||||
|
android:layout_width="52dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/cover_image_separator"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/music_folder_cover_image_view"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/music_folder_title_text_view"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/music_folder_cover_image_view"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_folder_cover_image_view" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/music_folder_title_text_view"
|
||||||
|
style="@style/LabelMedium"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/label_placeholder"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/music_folder_cover_image_view"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/music_folder_more_button"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/cover_image_separator"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_folder_cover_image_view" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/music_folder_more_button"
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:background="@drawable/ic_more_vert"
|
||||||
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/music_folder_title_text_view"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_folder_title_text_view" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
56
app/src/main/res/layout/item_library_music_index.xml
Normal file
56
app/src/main/res/layout/item_library_music_index.xml
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/music_index_cover_image_view"
|
||||||
|
android:layout_width="52dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_margin="2dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/cover_image_separator"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/music_index_cover_image_view"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/music_index_title_text_view"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/music_index_cover_image_view"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_index_cover_image_view" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/music_index_title_text_view"
|
||||||
|
style="@style/LabelMedium"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/label_placeholder"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/music_index_cover_image_view"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/music_index_more_button"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/cover_image_separator"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_index_cover_image_view" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/music_index_more_button"
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:background="@drawable/ic_more_vert"
|
||||||
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/music_index_title_text_view"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/music_index_title_text_view" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -107,6 +107,9 @@
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_libraryFragment_to_albumListPageFragment"
|
android:id="@+id/action_libraryFragment_to_albumListPageFragment"
|
||||||
app:destination="@id/albumListPageFragment" />
|
app:destination="@id/albumListPageFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_libraryFragment_to_indexFragment"
|
||||||
|
app:destination="@id/indexFragment" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/downloadFragment"
|
android:id="@+id/downloadFragment"
|
||||||
|
|
@ -279,6 +282,21 @@
|
||||||
android:name="com.cappielloantonio.play.ui.fragment.PodcastChannelPageFragment"
|
android:name="com.cappielloantonio.play.ui.fragment.PodcastChannelPageFragment"
|
||||||
android:label="PodcastChannelPageFragment"
|
android:label="PodcastChannelPageFragment"
|
||||||
tools:layout="@layout/fragment_podcast_channel_page" />
|
tools:layout="@layout/fragment_podcast_channel_page" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/directoryFragment"
|
||||||
|
android:name="com.cappielloantonio.play.ui.fragment.DirectoryFragment"
|
||||||
|
android:label="DirectoryFragment"
|
||||||
|
tools:layout="@layout/fragment_directory" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/indexFragment"
|
||||||
|
android:name="com.cappielloantonio.play.ui.fragment.IndexFragment"
|
||||||
|
android:label="IndexFragment"
|
||||||
|
tools:layout="@layout/fragment_index">
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_indexFragment_to_directoryFragment"
|
||||||
|
app:destination="@id/directoryFragment" />
|
||||||
|
</fragment>
|
||||||
<dialog
|
<dialog
|
||||||
android:id="@+id/songBottomSheetDialog"
|
android:id="@+id/songBottomSheetDialog"
|
||||||
android:name="com.cappielloantonio.play.ui.fragment.bottomsheetdialog.SongBottomSheetDialog"
|
android:name="com.cappielloantonio.play.ui.fragment.bottomsheetdialog.SongBottomSheetDialog"
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@
|
||||||
<string name="home_title_podcast_channels_see_all_button">See all</string>
|
<string name="home_title_podcast_channels_see_all_button">See all</string>
|
||||||
<string name="label_dot_separator">•</string>
|
<string name="label_dot_separator">•</string>
|
||||||
<string name="label_placeholder">--</string>
|
<string name="label_placeholder">--</string>
|
||||||
|
<string name="library_title_music_folder">Music folders</string>
|
||||||
<string name="library_title_album">Albums</string>
|
<string name="library_title_album">Albums</string>
|
||||||
<string name="library_title_album_see_all_button">See all</string>
|
<string name="library_title_album_see_all_button">See all</string>
|
||||||
<string name="library_title_artist">Artists</string>
|
<string name="library_title_artist">Artists</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue