From 0e97eab744c961980ee8e157906f1bdf1f3a21cf Mon Sep 17 00:00:00 2001 From: CappielloAntonio Date: Sat, 23 Mar 2024 21:33:11 +0100 Subject: [PATCH] feat: implemented customizable home, allowing users to toggle visibility of elements and change their order --- .../tempo/model/HomeSector.kt | 11 ++ .../adapter/HomeSectorHorizontalAdapter.java | 76 ++++++++++++ .../ui/dialog/HomeRearrangementDialog.java | 115 ++++++++++++++++++ .../ui/fragment/HomeTabMusicFragment.java | 112 +++++++++++++++++ .../cappielloantonio/tempo/util/Constants.kt | 15 +++ .../tempo/util/Preferences.kt | 12 ++ .../viewmodel/HomeRearrangementViewModel.java | 77 ++++++++++++ .../tempo/viewmodel/HomeViewModel.java | 26 ++++ .../res/layout/dialog_home_rearrangement.xml | 23 ++++ .../res/layout/fragment_home_tab_music.xml | 8 ++ .../layout/item_horizontal_home_sector.xml | 26 ++++ app/src/main/res/values/strings.xml | 6 + 12 files changed, 507 insertions(+) create mode 100644 app/src/main/java/com/cappielloantonio/tempo/model/HomeSector.kt create mode 100644 app/src/main/java/com/cappielloantonio/tempo/ui/adapter/HomeSectorHorizontalAdapter.java create mode 100644 app/src/main/java/com/cappielloantonio/tempo/ui/dialog/HomeRearrangementDialog.java create mode 100644 app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeRearrangementViewModel.java create mode 100644 app/src/main/res/layout/dialog_home_rearrangement.xml create mode 100644 app/src/main/res/layout/item_horizontal_home_sector.xml diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/HomeSector.kt b/app/src/main/java/com/cappielloantonio/tempo/model/HomeSector.kt new file mode 100644 index 00000000..79ca4356 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/model/HomeSector.kt @@ -0,0 +1,11 @@ +package com.cappielloantonio.tempo.model + +import androidx.annotation.Keep + +@Keep +data class HomeSector( + val id: String, + val sectorTitle: String, + var isVisible: Boolean, + val order: Int, +) \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/HomeSectorHorizontalAdapter.java b/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/HomeSectorHorizontalAdapter.java new file mode 100644 index 00000000..71855a7a --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/HomeSectorHorizontalAdapter.java @@ -0,0 +1,76 @@ +package com.cappielloantonio.tempo.ui.adapter; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.cappielloantonio.tempo.databinding.ItemHorizontalHomeSectorBinding; +import com.cappielloantonio.tempo.databinding.ItemHorizontalPlaylistDialogTrackBinding; +import com.cappielloantonio.tempo.glide.CustomGlideRequest; +import com.cappielloantonio.tempo.model.HomeSector; +import com.cappielloantonio.tempo.subsonic.models.Child; +import com.cappielloantonio.tempo.util.Constants; +import com.cappielloantonio.tempo.util.MusicUtil; + +import java.util.Collections; +import java.util.List; + +public class HomeSectorHorizontalAdapter extends RecyclerView.Adapter { + private List sectors; + + public HomeSectorHorizontalAdapter() { + this.sectors = Collections.emptyList(); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + ItemHorizontalHomeSectorBinding view = ItemHorizontalHomeSectorBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + HomeSector sector = sectors.get(position); + + holder.item.homeSectorTitleCheckBox.setText(sector.getSectorTitle()); + holder.item.homeSectorTitleCheckBox.setChecked(sector.isVisible()); + } + + @Override + public int getItemCount() { + return sectors.size(); + } + + public List getItems() { + return this.sectors; + } + + public void setItems(List sectors) { + this.sectors = sectors; + notifyDataSetChanged(); + } + + public HomeSector getItem(int id) { + return sectors.get(id); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + ItemHorizontalHomeSectorBinding item; + + ViewHolder(ItemHorizontalHomeSectorBinding item) { + super(item.getRoot()); + + this.item = item; + + this.item.homeSectorTitleCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> onCheck(isChecked)); + } + + private void onCheck(boolean isChecked) { + sectors.get(getBindingAdapterPosition()).setVisible(isChecked); + } + } +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/HomeRearrangementDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/HomeRearrangementDialog.java new file mode 100644 index 00000000..8ed67e42 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/HomeRearrangementDialog.java @@ -0,0 +1,115 @@ +package com.cappielloantonio.tempo.ui.dialog; + +import android.app.Dialog; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.cappielloantonio.tempo.R; +import com.cappielloantonio.tempo.databinding.DialogHomeRearrangementBinding; +import com.cappielloantonio.tempo.ui.adapter.HomeSectorHorizontalAdapter; +import com.cappielloantonio.tempo.viewmodel.HomeRearrangementViewModel; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import java.util.Collections; +import java.util.Objects; + +public class HomeRearrangementDialog extends DialogFragment { + private DialogHomeRearrangementBinding bind; + private HomeRearrangementViewModel homeRearrangementViewModel; + private HomeSectorHorizontalAdapter homeSectorHorizontalAdapter; + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + bind = DialogHomeRearrangementBinding.inflate(getLayoutInflater()); + + homeRearrangementViewModel = new ViewModelProvider(requireActivity()).get(HomeRearrangementViewModel.class); + + return new MaterialAlertDialogBuilder(requireContext()) + .setView(bind.getRoot()) + .setTitle(R.string.home_rearrangement_dialog_title) + .setPositiveButton(R.string.home_rearrangement_dialog_positive_button, (dialog, id) -> { }) + .setNeutralButton(R.string.home_rearrangement_dialog_neutral_button, (dialog, id) -> { }) + .setNegativeButton(R.string.home_rearrangement_dialog_negative_button, (dialog, id) -> dialog.cancel()) + .create(); + } + + @Override + public void onStart() { + super.onStart(); + + setButtonAction(); + initSectorView(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + homeRearrangementViewModel.closeDialog(); + bind = null; + } + + private void setButtonAction() { + androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog()); + + alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> { + homeRearrangementViewModel.saveHomeSectorList(homeSectorHorizontalAdapter.getItems()); + dismiss(); + }); + + alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> { + homeRearrangementViewModel.resetHomeSectorList(); + dismiss(); + }); + } + + private void initSectorView() { + bind.homeSectorItemRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); + bind.homeSectorItemRecyclerView.setHasFixedSize(true); + + homeSectorHorizontalAdapter = new HomeSectorHorizontalAdapter(); + bind.homeSectorItemRecyclerView.setAdapter(homeSectorHorizontalAdapter); + homeSectorHorizontalAdapter.setItems(homeRearrangementViewModel.getHomeSectorList()); + + new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) { + int originalPosition = -1; + int fromPosition = -1; + int toPosition = -1; + + @Override + public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { + if (originalPosition == -1) originalPosition = viewHolder.getBindingAdapterPosition(); + + fromPosition = viewHolder.getBindingAdapterPosition(); + toPosition = target.getBindingAdapterPosition(); + + Collections.swap(homeSectorHorizontalAdapter.getItems(), fromPosition, toPosition); + Objects.requireNonNull(recyclerView.getAdapter()).notifyItemMoved(fromPosition, toPosition); + + return false; + } + + @Override + public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { + super.clearView(recyclerView, viewHolder); + + homeRearrangementViewModel.orderSectorLiveListAfterSwap(homeSectorHorizontalAdapter.getItems()); + + originalPosition = -1; + fromPosition = -1; + toPosition = -1; + } + + @Override + public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { + + } + } + ).attachToRecyclerView(bind.homeSectorItemRecyclerView); + } +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/HomeTabMusicFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/HomeTabMusicFragment.java index 251699f0..424da187 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/HomeTabMusicFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/HomeTabMusicFragment.java @@ -32,6 +32,7 @@ import com.cappielloantonio.tempo.helper.recyclerview.DotsIndicatorDecoration; import com.cappielloantonio.tempo.helper.recyclerview.GridItemDecoration; import com.cappielloantonio.tempo.interfaces.ClickCallback; import com.cappielloantonio.tempo.model.Download; +import com.cappielloantonio.tempo.model.HomeSector; import com.cappielloantonio.tempo.service.DownloaderManager; import com.cappielloantonio.tempo.service.MediaManager; import com.cappielloantonio.tempo.service.MediaService; @@ -48,6 +49,8 @@ import com.cappielloantonio.tempo.ui.adapter.ShareHorizontalAdapter; import com.cappielloantonio.tempo.ui.adapter.SimilarTrackAdapter; import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter; import com.cappielloantonio.tempo.ui.adapter.YearAdapter; +import com.cappielloantonio.tempo.ui.dialog.BatteryOptimizationDialog; +import com.cappielloantonio.tempo.ui.dialog.HomeRearrangementDialog; import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.DownloadUtil; import com.cappielloantonio.tempo.util.MappingUtil; @@ -56,7 +59,9 @@ import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.util.UIUtil; import com.cappielloantonio.tempo.viewmodel.HomeViewModel; import com.google.android.material.snackbar.Snackbar; +import com.google.common.reflect.TypeToken; import com.google.common.util.concurrent.ListenableFuture; +import com.google.gson.Gson; import java.util.ArrayList; import java.util.List; @@ -119,6 +124,9 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { initRecentAddedAlbumView(); initGridView(); initSharesView(); + initHomeReorganizer(); + + reorder(); } @Override @@ -303,6 +311,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initDiscoverSongSlideView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_DISCOVERY)) return; + bind.discoverSongViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL); discoverSongAdapter = new DiscoverSongAdapter(this); @@ -329,6 +339,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initSimilarSongView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_MADE_FOR_YOU)) return; + bind.similarTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.similarTracksRecyclerView.setHasFixedSize(true); @@ -356,6 +368,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initArtistBestOf() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_BEST_OF)) return; + bind.bestOfArtistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.bestOfArtistRecyclerView.setHasFixedSize(true); @@ -381,6 +395,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initArtistRadio() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_RADIO_STATION)) return; + bind.radioArtistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.radioArtistRecyclerView.setHasFixedSize(true); @@ -408,6 +424,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initGridView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_TOP_SONGS)) return; + bind.gridTracksRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 3)); bind.gridTracksRecyclerView.addItemDecoration(new GridItemDecoration(3, 8, false)); bind.gridTracksRecyclerView.setHasFixedSize(true); @@ -432,6 +450,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initStarredTracksView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_STARRED_TRACKS)) return; + bind.starredTracksRecyclerView.setHasFixedSize(true); starredSongAdapter = new SongHorizontalAdapter(this, true, false); @@ -467,6 +487,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initStarredAlbumsView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_STARRED_ALBUMS)) return; + bind.starredAlbumsRecyclerView.setHasFixedSize(true); starredAlbumAdapter = new AlbumHorizontalAdapter(this, false); @@ -502,6 +524,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initStarredArtistsView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_STARRED_ARTISTS)) return; + bind.starredArtistsRecyclerView.setHasFixedSize(true); starredArtistAdapter = new ArtistHorizontalAdapter(this); @@ -539,6 +563,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initNewReleasesView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_NEW_RELEASES)) return; + bind.newReleasesRecyclerView.setHasFixedSize(true); newReleasesAlbumAdapter = new AlbumHorizontalAdapter(this, false); @@ -574,6 +600,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initYearSongView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_FLASHBACK)) return; + bind.yearsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.yearsRecyclerView.setHasFixedSize(true); @@ -599,6 +627,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initMostPlayedAlbumView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_MOST_PLAYED)) return; + bind.mostPlayedAlbumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.mostPlayedAlbumsRecyclerView.setHasFixedSize(true); @@ -625,6 +655,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initRecentPlayedAlbumView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_LAST_PLAYED)) return; + bind.recentlyPlayedAlbumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.recentlyPlayedAlbumsRecyclerView.setHasFixedSize(true); @@ -650,6 +682,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initRecentAddedAlbumView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_RECENTLY_ADDED)) return; + bind.recentlyAddedAlbumsRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); bind.recentlyAddedAlbumsRecyclerView.setHasFixedSize(true); @@ -675,6 +709,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { } private void initSharesView() { + if (!homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_SHARED)) return; + bind.sharesRecyclerView.setHasFixedSize(true); shareHorizontalAdapter = new ShareHorizontalAdapter(this); @@ -710,6 +746,13 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { ); } + private void initHomeReorganizer() { + bind.homeSectorRearrangementButton.setOnClickListener(v -> { + HomeRearrangementDialog dialog = new HomeRearrangementDialog(); + dialog.show(requireActivity().getSupportFragmentManager(), null); + }); + } + private void refreshSharesView() { final Handler handler = new Handler(); final Runnable runnable = () -> { @@ -735,6 +778,75 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback { }); } + public void reorder() { + if (bind != null && homeViewModel.getHomeSectorList() != null) { + bind.homeLinearLayoutContainer.removeAllViews(); + + for (HomeSector sector : homeViewModel.getHomeSectorList()) { + if (!sector.isVisible()) continue; + + switch (sector.getId()) { + case Constants.HOME_SECTOR_DISCOVERY: + bind.homeLinearLayoutContainer.addView(bind.homeDiscoverSector); + break; + case Constants.HOME_SECTOR_MADE_FOR_YOU: + bind.homeLinearLayoutContainer.addView(bind.homeSimilarTracksSector); + bind.homeLinearLayoutContainer.addView(bind.homeSimilarTracksPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_BEST_OF: + bind.homeLinearLayoutContainer.addView(bind.homeBestOfArtistSector); + bind.homeLinearLayoutContainer.addView(bind.homeBestOfArtistPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_RADIO_STATION: + bind.homeLinearLayoutContainer.addView(bind.homeRadioArtistSector); + bind.homeLinearLayoutContainer.addView(bind.homeRadioArtistPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_TOP_SONGS: + bind.homeLinearLayoutContainer.addView(bind.homeGridTracksSector); + break; + case Constants.HOME_SECTOR_STARRED_TRACKS: + bind.homeLinearLayoutContainer.addView(bind.starredTracksSector); + bind.homeLinearLayoutContainer.addView(bind.starredTracksPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_STARRED_ALBUMS: + bind.homeLinearLayoutContainer.addView(bind.starredAlbumsSector); + bind.homeLinearLayoutContainer.addView(bind.starredAlbumsPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_STARRED_ARTISTS: + bind.homeLinearLayoutContainer.addView(bind.starredArtistsSector); + bind.homeLinearLayoutContainer.addView(bind.starredArtistsPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_NEW_RELEASES: + bind.homeLinearLayoutContainer.addView(bind.homeNewReleasesSector); + bind.homeLinearLayoutContainer.addView(bind.homeNewReleasesPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_FLASHBACK: + bind.homeLinearLayoutContainer.addView(bind.homeFlashbackSector); + bind.homeLinearLayoutContainer.addView(bind.homeFlashbackPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_MOST_PLAYED: + bind.homeLinearLayoutContainer.addView(bind.homeMostPlayedAlbumsSector); + bind.homeLinearLayoutContainer.addView(bind.homeMostPlayedAlbumsPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_LAST_PLAYED: + bind.homeLinearLayoutContainer.addView(bind.homeRecentlyPlayedAlbumsSector); + bind.homeLinearLayoutContainer.addView(bind.homeRecentlyPlayedAlbumsPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_RECENTLY_ADDED: + bind.homeLinearLayoutContainer.addView(bind.homeRecentlyAddedAlbumsSector); + bind.homeLinearLayoutContainer.addView(bind.homeRecentlyAddedAlbumsPlaceholder.getRoot()); + break; + case Constants.HOME_SECTOR_SHARED: + bind.homeLinearLayoutContainer.addView(bind.sharesSector); + bind.homeLinearLayoutContainer.addView(bind.sharesPlaceholder.getRoot()); + break; + } + } + + bind.homeLinearLayoutContainer.addView(bind.homeSectorRearrangementButton); + } + } + private void initializeMediaBrowser() { mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync(); } diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/Constants.kt b/app/src/main/java/com/cappielloantonio/tempo/util/Constants.kt index 7615662e..a97ff060 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/Constants.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/util/Constants.kt @@ -91,4 +91,19 @@ object Constants { const val PLAYABLE_MEDIA_LIMIT = 100 const val PRE_PLAYABLE_MEDIA = 15 + + const val HOME_SECTOR_DISCOVERY = "HOME_SECTOR_DISCOVERY" + const val HOME_SECTOR_MADE_FOR_YOU = "HOME_SECTOR_MADE_FOR_YOU" + const val HOME_SECTOR_BEST_OF = "HOME_SECTOR_BEST_OF" + const val HOME_SECTOR_RADIO_STATION = "HOME_SECTOR_RADIO_STATION" + const val HOME_SECTOR_TOP_SONGS = "HOME_SECTOR_TOP_SONGS" + const val HOME_SECTOR_STARRED_TRACKS = "HOME_SECTOR_STARRED_TRACKS" + const val HOME_SECTOR_STARRED_ALBUMS = "HOME_SECTOR_STARRED_ALBUMS" + const val HOME_SECTOR_STARRED_ARTISTS = "HOME_SECTOR_STARRED_ARTISTS" + const val HOME_SECTOR_NEW_RELEASES = "HOME_SECTOR_NEW_RELEASES" + const val HOME_SECTOR_FLASHBACK = "HOME_SECTOR_FLASHBACK" + const val HOME_SECTOR_MOST_PLAYED = "HOME_SECTOR_MOST_PLAYED" + const val HOME_SECTOR_LAST_PLAYED = "HOME_SECTOR_LAST_PLAYED" + const val HOME_SECTOR_RECENTLY_ADDED = "HOME_SECTOR_RECENTLY_ADDED" + const val HOME_SECTOR_SHARED = "HOME_SECTOR_SHARED" } \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt b/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt index 8aa240b7..58c16fb5 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt @@ -1,6 +1,7 @@ package com.cappielloantonio.tempo.util import com.cappielloantonio.tempo.App +import com.cappielloantonio.tempo.model.HomeSector import com.cappielloantonio.tempo.subsonic.models.OpenSubsonicExtension import com.google.gson.Gson @@ -54,6 +55,7 @@ object Preferences { private const val MIN_STAR_RATING = "min_star_rating" private const val ALWAYS_ON_DISPLAY = "always_on_display" private const val AUDIO_QUALITY_PER_ITEM = "audio_quality_per_item" + private const val HOME_SECTOR_LIST = "home_sector_list" @JvmStatic @@ -384,4 +386,14 @@ object Preferences { fun showAudioQuality(): Boolean { return App.getInstance().preferences.getBoolean(AUDIO_QUALITY_PER_ITEM, false) } + + @JvmStatic + fun getHomeSectorList(): String? { + return App.getInstance().preferences.getString(HOME_SECTOR_LIST, null) + } + + @JvmStatic + fun setHomeSectorList(extension: List?) { + App.getInstance().preferences.edit().putString(HOME_SECTOR_LIST, Gson().toJson(extension)).apply() + } } \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeRearrangementViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeRearrangementViewModel.java new file mode 100644 index 00000000..fd7f193c --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeRearrangementViewModel.java @@ -0,0 +1,77 @@ +package com.cappielloantonio.tempo.viewmodel; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; + +import com.cappielloantonio.tempo.R; +import com.cappielloantonio.tempo.model.HomeSector; +import com.cappielloantonio.tempo.util.Constants; +import com.cappielloantonio.tempo.util.Preferences; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; + +import java.util.ArrayList; +import java.util.List; + +public class HomeRearrangementViewModel extends AndroidViewModel { + private List sectors = new ArrayList<>(); + + public HomeRearrangementViewModel(@NonNull Application application) { + super(application); + } + + public List getHomeSectorList() { + if (sectors != null && !sectors.isEmpty()) return sectors; + + if (Preferences.getHomeSectorList() != null && !Preferences.getHomeSectorList().equals("null")) { + sectors = new Gson().fromJson( + Preferences.getHomeSectorList(), + new TypeToken>() { + }.getType() + ); + } else { + sectors = fillStandardHomeSectorList(); + } + + return sectors; + } + + public void orderSectorLiveListAfterSwap(List sectors) { + this.sectors = sectors; + } + + public void saveHomeSectorList(List sectors) { + Preferences.setHomeSectorList(sectors); + } + + public void resetHomeSectorList() { + Preferences.setHomeSectorList(null); + } + + public void closeDialog() { + sectors = null; + } + + private List fillStandardHomeSectorList() { + List sectors = new ArrayList<>(); + + sectors.add(new HomeSector(Constants.HOME_SECTOR_DISCOVERY, getApplication().getString(R.string.home_title_discovery), true, 1)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_MADE_FOR_YOU, getApplication().getString(R.string.home_title_made_for_you), true, 2)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_BEST_OF, getApplication().getString(R.string.home_title_best_of), true, 3)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_RADIO_STATION, getApplication().getString(R.string.home_title_radio_station), true, 4)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_TOP_SONGS, getApplication().getString(R.string.home_title_top_songs), true, 5)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_STARRED_TRACKS, getApplication().getString(R.string.home_title_starred_tracks), true, 6)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_STARRED_ALBUMS, getApplication().getString(R.string.home_title_starred_albums), true, 7)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_STARRED_ARTISTS, getApplication().getString(R.string.home_title_starred_artists), true, 8)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_NEW_RELEASES, getApplication().getString(R.string.home_title_new_releases), true, 9)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_FLASHBACK, getApplication().getString(R.string.home_title_flashback), true, 10)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_MOST_PLAYED, getApplication().getString(R.string.home_title_most_played), true, 11)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_LAST_PLAYED, getApplication().getString(R.string.home_title_last_played), true, 12)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_RECENTLY_ADDED, getApplication().getString(R.string.home_title_recently_added), true, 13)); + sectors.add(new HomeSector(Constants.HOME_SECTOR_SHARED, getApplication().getString(R.string.home_title_shares), true, 14)); + + return sectors; + } +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java index 25c48055..097dca66 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java @@ -11,6 +11,7 @@ import androidx.lifecycle.MutableLiveData; import com.cappielloantonio.tempo.interfaces.StarCallback; import com.cappielloantonio.tempo.model.Chronology; import com.cappielloantonio.tempo.model.Favorite; +import com.cappielloantonio.tempo.model.HomeSector; import com.cappielloantonio.tempo.repository.AlbumRepository; import com.cappielloantonio.tempo.repository.ArtistRepository; import com.cappielloantonio.tempo.repository.ChronologyRepository; @@ -22,6 +23,8 @@ import com.cappielloantonio.tempo.subsonic.models.ArtistID3; import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Share; import com.cappielloantonio.tempo.util.Preferences; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; import java.util.ArrayList; import java.util.Calendar; @@ -59,10 +62,13 @@ public class HomeViewModel extends AndroidViewModel { private final MutableLiveData> artistBestOf = new MutableLiveData<>(null); private final MutableLiveData> shares = new MutableLiveData<>(null); + private List sectors; public HomeViewModel(@NonNull Application application) { super(application); + setHomeSectorList(); + songRepository = new SongRepository(); albumRepository = new AlbumRepository(); artistRepository = new ArtistRepository(); @@ -266,6 +272,26 @@ public class HomeViewModel extends AndroidViewModel { sharingRepository.getShares().observe(owner, this.shares::postValue); } + private void setHomeSectorList() { + if (Preferences.getHomeSectorList() != null && !Preferences.getHomeSectorList().equals("null")) { + sectors = new Gson().fromJson( + Preferences.getHomeSectorList(), + new TypeToken>() { + }.getType() + ); + } + } + + public List getHomeSectorList() { + return sectors; + } + + public boolean checkHomeSectorVisibility(String sectorId) { + return sectors != null && sectors.stream().filter(sector -> sector.getId().equals(sectorId)) + .findAny() + .orElse(null) != null; + } + public void setOfflineFavorite() { ArrayList favorites = getFavorites(); ArrayList favoritesToSave = getFavoritesToSave(favorites); diff --git a/app/src/main/res/layout/dialog_home_rearrangement.xml b/app/src/main/res/layout/dialog_home_rearrangement.xml new file mode 100644 index 00000000..0d43b99e --- /dev/null +++ b/app/src/main/res/layout/dialog_home_rearrangement.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home_tab_music.xml b/app/src/main/res/layout/fragment_home_tab_music.xml index b5c02871..cd019a4a 100644 --- a/app/src/main/res/layout/fragment_home_tab_music.xml +++ b/app/src/main/res/layout/fragment_home_tab_music.xml @@ -787,6 +787,14 @@ android:id="@+id/shares_placeholder" layout="@layout/item_placeholder_horizontal" android:visibility="gone" /> + +