From a4919ef6e908c13c42553fba7434e249e0d0d97f Mon Sep 17 00:00:00 2001
From: birdbird <6892457-tzugen@users.noreply.gitlab.com>
Date: Thu, 4 Aug 2022 12:35:53 +0000
Subject: [PATCH] Migrate to use SafeArgs
---
build.gradle | 1 +
.../api/subsonic/models/AlbumListType.kt | 4 +-
detekt-baseline.xml | 5 +-
gradle/libs.versions.toml | 1 +
ultrasonic/build.gradle | 1 +
.../fragment/PlaylistsFragment.java | 342 ----------------
.../ultrasonic/fragment/PodcastFragment.java | 112 ------
.../fragment/SelectGenreFragment.java | 135 -------
.../ultrasonic/fragment/SharesFragment.java | 331 ----------------
.../ultrasonic/activity/NavigationActivity.kt | 47 ++-
.../ultrasonic/data/ActiveServerProvider.kt | 6 +-
.../ultrasonic/fragment/AlbumListFragment.kt | 65 +++-
.../ultrasonic/fragment/ArtistListFragment.kt | 60 +--
.../ultrasonic/fragment/BookmarksFragment.kt | 1 -
.../ultrasonic/fragment/DownloadsFragment.kt | 2 +-
.../ultrasonic/fragment/EditServerFragment.kt | 7 +
.../fragment/EndlessScrollListener.kt | 2 +-
.../ultrasonic/fragment/EntryListFragment.kt | 43 ++-
.../moire/ultrasonic/fragment/MainFragment.kt | 79 ++--
.../ultrasonic/fragment/MultiListFragment.kt | 38 +-
.../ultrasonic/fragment/NowPlayingFragment.kt | 26 +-
.../fragment/OnBackPressedHandler.kt | 7 +
.../ultrasonic/fragment/PlayerFragment.kt | 56 +--
.../ultrasonic/fragment/SearchFragment.kt | 76 ++--
.../fragment/TrackCollectionFragment.kt | 157 ++++----
.../fragment/legacy/PlaylistsFragment.kt | 345 +++++++++++++++++
.../fragment/legacy/PodcastFragment.kt | 94 +++++
.../fragment/legacy/SelectGenreFragment.kt | 107 +++++
.../fragment/legacy/SharesFragment.kt | 364 ++++++++++++++++++
.../moire/ultrasonic/model/AlbumListModel.kt | 160 ++++----
.../moire/ultrasonic/model/ArtistListModel.kt | 8 +-
.../ultrasonic/model/GenericListModel.kt | 32 +-
.../moire/ultrasonic/model/SearchListModel.kt | 12 -
.../org/moire/ultrasonic/playback/Plan.md | 17 -
.../ultrasonic/service/CachedMusicService.kt | 10 +-
.../service/MediaPlayerController.kt | 2 +-
.../moire/ultrasonic/service/MusicService.kt | 4 +-
.../ultrasonic/service/OfflineMusicService.kt | 17 +-
.../ultrasonic/service/RESTMusicService.kt | 2 +-
.../ultrasonic/subsonic/DownloadHandler.kt | 13 +-
.../moire/ultrasonic/subsonic/ShareHandler.kt | 63 +--
.../org/moire/ultrasonic/util/Constants.kt | 26 +-
.../main/res/navigation/navigation_graph.xml | 167 +++++++-
43 files changed, 1617 insertions(+), 1430 deletions(-)
delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlaylistsFragment.java
delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PodcastFragment.java
delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectGenreFragment.java
delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SharesFragment.java
create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/legacy/PlaylistsFragment.kt
create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/legacy/PodcastFragment.kt
create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/legacy/SelectGenreFragment.kt
create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/legacy/SharesFragment.kt
delete mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/Plan.md
diff --git a/build.gradle b/build.gradle
index c44d7f84..efa20eed 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,6 +17,7 @@ buildscript {
classpath libs.kotlin
classpath libs.ktlintGradle
classpath libs.detekt
+ classpath libs.navigationSafeArgs
}
}
diff --git a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/AlbumListType.kt b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/AlbumListType.kt
index 6d58eb0d..8d399c2a 100644
--- a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/AlbumListType.kt
+++ b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/AlbumListType.kt
@@ -16,7 +16,8 @@ enum class AlbumListType(val typeName: String) {
SORTED_BY_ARTIST("alphabeticalByArtist"),
STARRED("starred"),
BY_YEAR("byYear"),
- BY_GENRE("byGenre");
+ BY_GENRE("byGenre"),
+ BY_ARTIST("albumsByArtist");
override fun toString(): String {
return typeName
@@ -35,6 +36,7 @@ enum class AlbumListType(val typeName: String) {
in STARRED.typeName -> STARRED
in BY_YEAR.typeName -> BY_YEAR
in BY_GENRE.typeName -> BY_GENRE
+ in BY_ARTIST.typeName -> BY_ARTIST
else -> throw IllegalArgumentException("Unknown type: $typeName")
}
diff --git a/detekt-baseline.xml b/detekt-baseline.xml
index 205ff394..b625cb3f 100644
--- a/detekt-baseline.xml
+++ b/detekt-baseline.xml
@@ -9,7 +9,9 @@
ImplicitDefaultLocale:ShareHandler.kt$ShareHandler$String.format("%d:%s", timeSpanAmount, timeSpanType)
LongMethod:EditServerFragment.kt$EditServerFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)
LongMethod:NavigationActivity.kt$NavigationActivity$override fun onCreate(savedInstanceState: Bundle?)
- LongMethod:ShareHandler.kt$ShareHandler$private fun showDialog( fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken )
+ LongMethod:PlaylistsFragment.kt$PlaylistsFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean
+ LongMethod:ShareHandler.kt$ShareHandler$private fun showDialog( fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken, additionalId: String? )
+ LongMethod:SharesFragment.kt$SharesFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean
LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, passedData: Array<ServerSetting>, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -> Unit), private val serverEditRequestedCallback: ((Int) -> Unit) )
MagicNumber:ActiveServerProvider.kt$ActiveServerProvider$8192
MagicNumber:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$0.05f
@@ -19,7 +21,6 @@
TooGenericExceptionCaught:FileLoggerTree.kt$FileLoggerTree$x: Throwable
TooGenericExceptionCaught:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$x: Throwable
TooGenericExceptionCaught:JukeboxMediaPlayer.kt$JukeboxMediaPlayer.TaskQueue$x: Throwable
- TooGenericExceptionThrown:Downloader.kt$Downloader.DownloadTask$throw RuntimeException( String.format( Locale.ROOT, "Download of '%s' was cancelled", downloadFile.track ) )
TooManyFunctions:RESTMusicService.kt$RESTMusicService : MusicService
UtilityClassWithPublicConstructor:FragmentTitle.kt$FragmentTitle
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d6912a5c..b3fb3732 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -64,6 +64,7 @@ navigationUi = { module = "androidx.navigation:navigation-ui", versio
navigationFragmentKtx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigation" }
navigationUiKtx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigation" }
navigationFeature = { module = "androidx.navigation:navigation-dynamic-features-fragment", version.ref = "navigation" }
+navigationSafeArgs = { module = "androidx.navigation:navigation-safe-args-gradle-plugin", version.ref = "navigation"}
preferences = { module = "androidx.preference:preference", version.ref = "preferences" }
media = { module = "androidx.media:media", version.ref = "media" }
media3exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
diff --git a/ultrasonic/build.gradle b/ultrasonic/build.gradle
index 16892319..bb6ae42f 100644
--- a/ultrasonic/build.gradle
+++ b/ultrasonic/build.gradle
@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
+apply plugin: "androidx.navigation.safeargs.kotlin"
apply from: "../gradle_scripts/code_quality.gradle"
android {
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlaylistsFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlaylistsFragment.java
deleted file mode 100644
index ba7e136e..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlaylistsFragment.java
+++ /dev/null
@@ -1,342 +0,0 @@
-package org.moire.ultrasonic.fragment;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.method.LinkMovementMethod;
-import android.text.util.Linkify;
-import android.view.ContextMenu;
-import android.view.LayoutInflater;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.navigation.Navigation;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import org.jetbrains.annotations.NotNull;
-import org.moire.ultrasonic.R;
-import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException;
-import org.moire.ultrasonic.data.ActiveServerProvider;
-import org.moire.ultrasonic.domain.Playlist;
-import org.moire.ultrasonic.service.MusicService;
-import org.moire.ultrasonic.service.MusicServiceFactory;
-import org.moire.ultrasonic.service.OfflineException;
-import org.moire.ultrasonic.subsonic.DownloadHandler;
-import org.moire.ultrasonic.util.BackgroundTask;
-import org.moire.ultrasonic.util.CacheCleaner;
-import org.moire.ultrasonic.util.CancellationToken;
-import org.moire.ultrasonic.util.Constants;
-import org.moire.ultrasonic.util.LoadingTask;
-import org.moire.ultrasonic.util.FragmentBackgroundTask;
-import org.moire.ultrasonic.util.Util;
-import org.moire.ultrasonic.view.PlaylistAdapter;
-
-import java.util.List;
-
-import kotlin.Lazy;
-
-import static org.koin.java.KoinJavaComponent.inject;
-
-/**
- * Displays the playlists stored on the server
- */
-public class PlaylistsFragment extends Fragment {
-
- private SwipeRefreshLayout refreshPlaylistsListView;
- private ListView playlistsListView;
- private View emptyTextView;
- private PlaylistAdapter playlistAdapter;
-
- private final Lazy downloadHandler = inject(DownloadHandler.class);
- private CancellationToken cancellationToken;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- Util.applyTheme(this.getContext());
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.select_playlist, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- cancellationToken = new CancellationToken();
-
- refreshPlaylistsListView = view.findViewById(R.id.select_playlist_refresh);
- playlistsListView = view.findViewById(R.id.select_playlist_list);
-
- refreshPlaylistsListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
- {
- @Override
- public void onRefresh() {
- load(true);
- }
- });
-
- emptyTextView = view.findViewById(R.id.select_playlist_empty);
- playlistsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- Playlist playlist = (Playlist) parent.getItemAtPosition(position);
-
- if (playlist == null)
- {
- return;
- }
-
- Bundle bundle = new Bundle();
- bundle.putString(Constants.INTENT_ID, playlist.getId());
- bundle.putString(Constants.INTENT_PLAYLIST_ID, playlist.getId());
- bundle.putString(Constants.INTENT_PLAYLIST_NAME, playlist.getName());
- Navigation.findNavController(getView()).navigate(R.id.trackCollectionFragment, bundle);
- }
- });
- registerForContextMenu(playlistsListView);
- FragmentTitle.Companion.setTitle(this, R.string.playlist_label);
-
- load(false);
- }
-
- @Override
- public void onDestroyView() {
- cancellationToken.cancel();
- super.onDestroyView();
- }
-
- private void load(final boolean refresh)
- {
- BackgroundTask> task = new FragmentBackgroundTask>(getActivity(), true, refreshPlaylistsListView, cancellationToken)
- {
- @Override
- protected List doInBackground() throws Throwable
- {
- MusicService musicService = MusicServiceFactory.getMusicService();
- List playlists = musicService.getPlaylists(refresh);
-
- if (!ActiveServerProvider.Companion.isOffline())
- new CacheCleaner().cleanPlaylists(playlists);
- return playlists;
- }
-
- @Override
- protected void done(List result)
- {
- playlistsListView.setAdapter(playlistAdapter = new PlaylistAdapter(getContext(), result));
- emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
- }
- };
- task.execute();
- }
-
- @Override
- public void onCreateContextMenu(@NotNull ContextMenu menu, @NotNull View view, ContextMenu.ContextMenuInfo menuInfo)
- {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- MenuInflater inflater = getActivity().getMenuInflater();
- if (ActiveServerProvider.Companion.isOffline()) inflater.inflate(R.menu.select_playlist_context_offline, menu);
- else inflater.inflate(R.menu.select_playlist_context, menu);
-
- MenuItem downloadMenuItem = menu.findItem(R.id.playlist_menu_download);
-
- if (downloadMenuItem != null)
- {
- downloadMenuItem.setVisible(!ActiveServerProvider.Companion.isOffline());
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem)
- {
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- if (info == null)
- {
- return false;
- }
-
- Playlist playlist = (Playlist) playlistsListView.getItemAtPosition(info.position);
- if (playlist == null)
- {
- return false;
- }
-
- Bundle bundle;
- int itemId = menuItem.getItemId();
- if (itemId == R.id.playlist_menu_pin) {
- downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), true, true, false, false, true, false, false);
- } else if (itemId == R.id.playlist_menu_unpin) {
- downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, true);
- } else if (itemId == R.id.playlist_menu_download) {
- downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, false);
- } else if (itemId == R.id.playlist_menu_play_now) {
- bundle = new Bundle();
- bundle.putString(Constants.INTENT_PLAYLIST_ID, playlist.getId());
- bundle.putString(Constants.INTENT_PLAYLIST_NAME, playlist.getName());
- bundle.putBoolean(Constants.INTENT_AUTOPLAY, true);
- Navigation.findNavController(getView()).navigate(R.id.trackCollectionFragment, bundle);
- } else if (itemId == R.id.playlist_menu_play_shuffled) {
- bundle = new Bundle();
- bundle.putString(Constants.INTENT_PLAYLIST_ID, playlist.getId());
- bundle.putString(Constants.INTENT_PLAYLIST_NAME, playlist.getName());
- bundle.putBoolean(Constants.INTENT_AUTOPLAY, true);
- bundle.putBoolean(Constants.INTENT_SHUFFLE, true);
- Navigation.findNavController(getView()).navigate(R.id.trackCollectionFragment, bundle);
- } else if (itemId == R.id.playlist_menu_delete) {
- deletePlaylist(playlist);
- } else if (itemId == R.id.playlist_info) {
- displayPlaylistInfo(playlist);
- } else if (itemId == R.id.playlist_update_info) {
- updatePlaylistInfo(playlist);
- } else {
- return super.onContextItemSelected(menuItem);
- }
- return true;
- }
-
- private void deletePlaylist(final Playlist playlist)
- {
- new AlertDialog.Builder(getContext()).setIcon(R.drawable.ic_baseline_warning).setTitle(R.string.common_confirm).setMessage(getResources().getString(R.string.delete_playlist, playlist.getName())).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
- {
- @Override
- public void onClick(DialogInterface dialog, int which)
- {
- new LoadingTask(getActivity(), refreshPlaylistsListView, cancellationToken)
- {
- @Override
- protected Void doInBackground() throws Throwable
- {
- MusicService musicService = MusicServiceFactory.getMusicService();
- musicService.deletePlaylist(playlist.getId());
- return null;
- }
-
- @Override
- protected void done(Void result)
- {
- playlistAdapter.remove(playlist);
- playlistAdapter.notifyDataSetChanged();
- Util.toast(getContext(), getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
- }
-
- @Override
- protected void error(Throwable error)
- {
- String msg;
- msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.menu_deleted_playlist_error, playlist.getName()), getErrorMessage(error));
-
- Util.toast(getContext(), msg, false);
- }
- }.execute();
- }
-
- }).setNegativeButton(R.string.common_cancel, null).show();
- }
-
- private void displayPlaylistInfo(final Playlist playlist)
- {
- final TextView textView = new TextView(getContext());
- textView.setPadding(5, 5, 5, 5);
-
- final Spannable message = new SpannableString("Owner: " + playlist.getOwner() + "\nComments: " +
- ((playlist.getComment() == null) ? "" : playlist.getComment()) +
- "\nSong Count: " + playlist.getSongCount() +
- ((playlist.getPublic() == null) ? "" : ("\nPublic: " + playlist.getPublic()) + ((playlist.getCreated() == null) ? "" : ("\nCreation Date: " + playlist.getCreated().replace('T', ' ')))));
-
- Linkify.addLinks(message, Linkify.WEB_URLS);
- textView.setText(message);
- textView.setMovementMethod(LinkMovementMethod.getInstance());
-
- new AlertDialog.Builder(getContext()).setTitle(playlist.getName()).setCancelable(true).setIcon(R.drawable.ic_baseline_info).setView(textView).show();
- }
-
- private void updatePlaylistInfo(final Playlist playlist)
- {
- View dialogView = getLayoutInflater().inflate(R.layout.update_playlist, null);
-
- if (dialogView == null)
- {
- return;
- }
-
- final EditText nameBox = dialogView.findViewById(R.id.get_playlist_name);
- final EditText commentBox = dialogView.findViewById(R.id.get_playlist_comment);
- final CheckBox publicBox = dialogView.findViewById(R.id.get_playlist_public);
-
- nameBox.setText(playlist.getName());
- commentBox.setText(playlist.getComment());
- Boolean pub = playlist.getPublic();
-
- if (pub == null)
- {
- publicBox.setEnabled(false);
- }
- else
- {
- publicBox.setChecked(pub);
- }
-
- AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
-
- alertDialog.setIcon(R.drawable.ic_baseline_warning);
- alertDialog.setTitle(R.string.playlist_update_info);
- alertDialog.setView(dialogView);
- alertDialog.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
- {
- @Override
- public void onClick(DialogInterface dialog, int which)
- {
- new LoadingTask(getActivity(), refreshPlaylistsListView, cancellationToken)
- {
- @Override
- protected Void doInBackground() throws Throwable
- {
- Editable nameBoxText = nameBox.getText();
- Editable commentBoxText = commentBox.getText();
- String name = nameBoxText != null ? nameBoxText.toString() : null;
- String comment = commentBoxText != null ? commentBoxText.toString() : null;
-
- MusicService musicService = MusicServiceFactory.getMusicService();
- musicService.updatePlaylist(playlist.getId(), name, comment, publicBox.isChecked());
- return null;
- }
-
- @Override
- protected void done(Void result)
- {
- load(true);
- Util.toast(getContext(), getResources().getString(R.string.playlist_updated_info, playlist.getName()));
- }
-
- @Override
- protected void error(Throwable error)
- {
- String msg;
- msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.playlist_updated_info_error, playlist.getName()), getErrorMessage(error));
-
- Util.toast(getContext(), msg, false);
- }
- }.execute();
- }
-
- });
- alertDialog.setNegativeButton(R.string.common_cancel, null);
- alertDialog.show();
- }
-}
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PodcastFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PodcastFragment.java
deleted file mode 100644
index 00210dd0..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PodcastFragment.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package org.moire.ultrasonic.fragment;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ListView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.navigation.Navigation;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import org.moire.ultrasonic.R;
-import org.moire.ultrasonic.domain.PodcastsChannel;
-import org.moire.ultrasonic.service.MusicService;
-import org.moire.ultrasonic.service.MusicServiceFactory;
-import org.moire.ultrasonic.util.BackgroundTask;
-import org.moire.ultrasonic.util.CancellationToken;
-import org.moire.ultrasonic.util.Constants;
-import org.moire.ultrasonic.util.FragmentBackgroundTask;
-import org.moire.ultrasonic.util.Util;
-import org.moire.ultrasonic.view.PodcastsChannelsAdapter;
-
-import java.util.List;
-
-/**
- * Displays the podcasts available on the server
- */
-public class PodcastFragment extends Fragment {
-
- private View emptyTextView;
- ListView channelItemsListView = null;
- private CancellationToken cancellationToken;
- private SwipeRefreshLayout swipeRefresh;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- Util.applyTheme(this.getContext());
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.podcasts, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- cancellationToken = new CancellationToken();
- swipeRefresh = view.findViewById(R.id.podcasts_refresh);
- swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
- {
- @Override
- public void onRefresh() {
- load(view.getContext(), true);
- }
- });
-
- FragmentTitle.Companion.setTitle(this, R.string.podcasts_label);
-
- emptyTextView = view.findViewById(R.id.select_podcasts_empty);
- channelItemsListView = view.findViewById(R.id.podcasts_channels_items_list);
- channelItemsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- PodcastsChannel pc = (PodcastsChannel) parent.getItemAtPosition(position);
- if (pc == null) {
- return;
- }
-
- Bundle bundle = new Bundle();
- bundle.putString(Constants.INTENT_PODCAST_CHANNEL_ID, pc.getId());
- Navigation.findNavController(view).navigate(R.id.trackCollectionFragment, bundle);
- }
- });
-
- load(view.getContext(), false);
- }
-
- @Override
- public void onDestroyView() {
- cancellationToken.cancel();
- super.onDestroyView();
- }
-
- private void load(final Context context, final boolean refresh)
- {
- BackgroundTask> task = new FragmentBackgroundTask>(getActivity(), true, swipeRefresh, cancellationToken)
- {
- @Override
- protected List doInBackground() throws Throwable
- {
- MusicService musicService = MusicServiceFactory.getMusicService();
- return musicService.getPodcastsChannels(refresh);
- }
-
- @Override
- protected void done(List result)
- {
- channelItemsListView.setAdapter(new PodcastsChannelsAdapter(context, result));
- emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
- }
- };
- task.execute();
- }
-}
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectGenreFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectGenreFragment.java
deleted file mode 100644
index dc6f3382..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectGenreFragment.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package org.moire.ultrasonic.fragment;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ListView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.navigation.Navigation;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import org.moire.ultrasonic.R;
-import org.moire.ultrasonic.domain.Genre;
-import org.moire.ultrasonic.service.MusicService;
-import org.moire.ultrasonic.service.MusicServiceFactory;
-import org.moire.ultrasonic.util.BackgroundTask;
-import org.moire.ultrasonic.util.CancellationToken;
-import org.moire.ultrasonic.util.Constants;
-import org.moire.ultrasonic.util.FragmentBackgroundTask;
-import org.moire.ultrasonic.util.Settings;
-import org.moire.ultrasonic.util.Util;
-import org.moire.ultrasonic.view.GenreAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import timber.log.Timber;
-
-/**
- * Displays the available genres in the media library
- */
-public class SelectGenreFragment extends Fragment {
-
- private SwipeRefreshLayout refreshGenreListView;
- private ListView genreListView;
- private View emptyView;
- private CancellationToken cancellationToken;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- Util.applyTheme(this.getContext());
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.select_genre, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- cancellationToken = new CancellationToken();
- refreshGenreListView = view.findViewById(R.id.select_genre_refresh);
- genreListView = view.findViewById(R.id.select_genre_list);
-
- refreshGenreListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
- {
- @Override
- public void onRefresh()
- {
- load(true);
- }
- });
-
- genreListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- Genre genre = (Genre) parent.getItemAtPosition(position);
-
- if (genre != null)
- {
- Bundle bundle = new Bundle();
- bundle.putString(Constants.INTENT_GENRE_NAME, genre.getName());
- bundle.putInt(Constants.INTENT_ALBUM_LIST_SIZE, Settings.getMaxSongs());
- bundle.putInt(Constants.INTENT_ALBUM_LIST_OFFSET, 0);
- Navigation.findNavController(view).navigate(R.id.trackCollectionFragment, bundle);
- }
- }
- });
-
- emptyView = view.findViewById(R.id.select_genre_empty);
- registerForContextMenu(genreListView);
-
- FragmentTitle.Companion.setTitle(this, R.string.main_genres_title);
- load(false);
- }
-
- @Override
- public void onDestroyView() {
- cancellationToken.cancel();
- super.onDestroyView();
- }
-
- private void load(final boolean refresh)
- {
- BackgroundTask> task = new FragmentBackgroundTask>(getActivity(), true, refreshGenreListView, cancellationToken)
- {
- @Override
- protected List doInBackground()
- {
- MusicService musicService = MusicServiceFactory.getMusicService();
-
- List genres = new ArrayList<>();
-
- try
- {
- genres = musicService.getGenres(refresh);
- }
- catch (Exception x)
- {
- Timber.e(x, "Failed to load genres");
- }
-
- return genres;
- }
-
- @Override
- protected void done(List result)
- {
- emptyView.setVisibility(result == null || result.isEmpty() ? View.VISIBLE : View.GONE);
-
- if (result != null)
- {
- genreListView.setAdapter(new GenreAdapter(getContext(), result));
- }
- }
- };
- task.execute();
- }
-}
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SharesFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SharesFragment.java
deleted file mode 100644
index 4abc6df6..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SharesFragment.java
+++ /dev/null
@@ -1,331 +0,0 @@
-package org.moire.ultrasonic.fragment;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.method.LinkMovementMethod;
-import android.text.util.Linkify;
-import android.view.ContextMenu;
-import android.view.LayoutInflater;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.navigation.Navigation;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import org.jetbrains.annotations.NotNull;
-import org.moire.ultrasonic.R;
-import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException;
-import org.moire.ultrasonic.domain.Share;
-import org.moire.ultrasonic.service.MusicService;
-import org.moire.ultrasonic.service.MusicServiceFactory;
-import org.moire.ultrasonic.service.OfflineException;
-import org.moire.ultrasonic.subsonic.DownloadHandler;
-import org.moire.ultrasonic.util.BackgroundTask;
-import org.moire.ultrasonic.util.CancellationToken;
-import org.moire.ultrasonic.util.Constants;
-import org.moire.ultrasonic.util.LoadingTask;
-import org.moire.ultrasonic.util.FragmentBackgroundTask;
-import org.moire.ultrasonic.util.TimeSpan;
-import org.moire.ultrasonic.util.TimeSpanPicker;
-import org.moire.ultrasonic.util.Util;
-import org.moire.ultrasonic.view.ShareAdapter;
-
-import java.util.List;
-
-import kotlin.Lazy;
-
-import static org.koin.java.KoinJavaComponent.inject;
-
-/**
- * Displays the shares in the media library
- */
-public class SharesFragment extends Fragment {
-
- private SwipeRefreshLayout refreshSharesListView;
- private ListView sharesListView;
- private View emptyTextView;
- private ShareAdapter shareAdapter;
-
- private final Lazy downloadHandler = inject(DownloadHandler.class);
- private CancellationToken cancellationToken;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- Util.applyTheme(this.getContext());
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.select_share, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- cancellationToken = new CancellationToken();
-
- refreshSharesListView = view.findViewById(R.id.select_share_refresh);
- sharesListView = view.findViewById(R.id.select_share_list);
-
- refreshSharesListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
- {
- @Override
- public void onRefresh()
- {
- load(true);
- }
- });
-
- emptyTextView = view.findViewById(R.id.select_share_empty);
- sharesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- Share share = (Share) parent.getItemAtPosition(position);
-
- if (share == null)
- {
- return;
- }
-
- Bundle bundle = new Bundle();
- bundle.putString(Constants.INTENT_SHARE_ID, share.getId());
- bundle.putString(Constants.INTENT_SHARE_NAME, share.getName());
- Navigation.findNavController(view).navigate(R.id.trackCollectionFragment, bundle);
- }
- });
- registerForContextMenu(sharesListView);
- FragmentTitle.Companion.setTitle(this, R.string.button_bar_shares);
-
- load(false);
- }
-
- @Override
- public void onDestroyView() {
- cancellationToken.cancel();
- super.onDestroyView();
- }
-
- private void load(final boolean refresh)
- {
- BackgroundTask> task = new FragmentBackgroundTask>(getActivity(), true, refreshSharesListView, cancellationToken)
- {
- @Override
- protected List doInBackground() throws Throwable
- {
- MusicService musicService = MusicServiceFactory.getMusicService();
- return musicService.getShares(refresh);
- }
-
- @Override
- protected void done(List result)
- {
- sharesListView.setAdapter(shareAdapter = new ShareAdapter(getContext(), result));
- emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
- }
- };
- task.execute();
- }
-
- @Override
- public void onCreateContextMenu(@NotNull ContextMenu menu, @NotNull View view, ContextMenu.ContextMenuInfo menuInfo)
- {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- MenuInflater inflater = getActivity().getMenuInflater();
- inflater.inflate(R.menu.select_share_context, menu);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem)
- {
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- if (info == null) return false;
-
- Share share = (Share) sharesListView.getItemAtPosition(info.position);
- if (share == null || share.getId() == null) return false;
-
- int itemId = menuItem.getItemId();
- if (itemId == R.id.share_menu_pin) {
- downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), true, true, false, false, true, false, false);
- } else if (itemId == R.id.share_menu_unpin) {
- downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, false, false, true, false, true);
- } else if (itemId == R.id.share_menu_download) {
- downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, false, false, true, false, false);
- } else if (itemId == R.id.share_menu_play_now) {
- downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, true, false, false, false, false);
- } else if (itemId == R.id.share_menu_play_shuffled) {
- downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, true, true, false, false, false);
- } else if (itemId == R.id.share_menu_delete) {
- deleteShare(share);
- } else if (itemId == R.id.share_info) {
- displayShareInfo(share);
- } else if (itemId == R.id.share_update_info) {
- updateShareInfo(share);
- } else {
- return super.onContextItemSelected(menuItem);
- }
- return true;
- }
-
- private void deleteShare(final Share share)
- {
- new AlertDialog.Builder(getContext()).setIcon(R.drawable.ic_baseline_warning).setTitle(R.string.common_confirm).setMessage(getResources().getString(R.string.delete_playlist, share.getName())).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
- {
- @Override
- public void onClick(DialogInterface dialog, int which)
- {
- new LoadingTask(getActivity(), refreshSharesListView, cancellationToken)
- {
- @Override
- protected Void doInBackground() throws Throwable
- {
- MusicService musicService = MusicServiceFactory.getMusicService();
- musicService.deleteShare(share.getId());
- return null;
- }
-
- @Override
- protected void done(Void result)
- {
- shareAdapter.remove(share);
- shareAdapter.notifyDataSetChanged();
- Util.toast(getContext(), getResources().getString(R.string.menu_deleted_share, share.getName()));
- }
-
- @Override
- protected void error(Throwable error)
- {
- String msg;
- msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.menu_deleted_share_error, share.getName()), getErrorMessage(error));
-
- Util.toast(getContext(), msg, false);
- }
- }.execute();
- }
-
- }).setNegativeButton(R.string.common_cancel, null).show();
- }
-
- private void displayShareInfo(final Share share)
- {
- final TextView textView = new TextView(getContext());
- textView.setPadding(5, 5, 5, 5);
-
- final Spannable message = new SpannableString("Owner: " + share.getUsername() +
- "\nComments: " + ((share.getDescription() == null) ? "" : share.getDescription()) +
- "\nURL: " + share.getUrl() +
- "\nEntry Count: " + share.getEntries().size() +
- "\nVisit Count: " + share.getVisitCount() +
- ((share.getCreated() == null) ? "" : ("\nCreation Date: " + share.getCreated().replace('T', ' '))) +
- ((share.getLastVisited() == null) ? "" : ("\nLast Visited Date: " + share.getLastVisited().replace('T', ' '))) +
- ((share.getExpires() == null) ? "" : ("\nExpiration Date: " + share.getExpires().replace('T', ' '))));
-
- Linkify.addLinks(message, Linkify.WEB_URLS);
- textView.setText(message);
- textView.setMovementMethod(LinkMovementMethod.getInstance());
-
- new AlertDialog.Builder(getContext()).setTitle("Share Details").setCancelable(true).setIcon(R.drawable.ic_baseline_info).setView(textView).show();
- }
-
- private void updateShareInfo(final Share share)
- {
- View dialogView = getLayoutInflater().inflate(R.layout.share_details, null);
- if (dialogView == null)
- {
- return;
- }
-
- final EditText shareDescription = dialogView.findViewById(R.id.share_description);
- final TimeSpanPicker timeSpanPicker = dialogView.findViewById(R.id.date_picker);
-
- shareDescription.setText(share.getDescription());
-
- CheckBox hideDialogCheckBox = dialogView.findViewById(R.id.hide_dialog);
- CheckBox saveAsDefaultsCheckBox = dialogView.findViewById(R.id.save_as_defaults);
- CheckBox noExpirationCheckBox = dialogView.findViewById(R.id.timeSpanDisableCheckBox);
-
- noExpirationCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
- {
- @Override
- public void onCheckedChanged(CompoundButton compoundButton, boolean b)
- {
- timeSpanPicker.setEnabled(!b);
- }
- });
-
- noExpirationCheckBox.setChecked(true);
-
- timeSpanPicker.setTimeSpanDisableText(getResources().getText(R.string.no_expiration));
-
- hideDialogCheckBox.setVisibility(View.GONE);
- saveAsDefaultsCheckBox.setVisibility(View.GONE);
-
- AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
-
- alertDialog.setIcon(R.drawable.ic_baseline_warning);
- alertDialog.setTitle(R.string.playlist_update_info);
- alertDialog.setView(dialogView);
- alertDialog.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
- {
- @Override
- public void onClick(DialogInterface dialog, int which)
- {
- new LoadingTask(getActivity(), refreshSharesListView, cancellationToken)
- {
- @Override
- protected Void doInBackground() throws Throwable
- {
- long millis = timeSpanPicker.getTimeSpan().getTotalMilliseconds();
-
- if (millis > 0)
- {
- millis = TimeSpan.getCurrentTime().add(millis).getTotalMilliseconds();
- }
-
- Editable shareDescriptionText = shareDescription.getText();
- String description = shareDescriptionText != null ? shareDescriptionText.toString() : null;
-
- MusicService musicService = MusicServiceFactory.getMusicService();
- musicService.updateShare(share.getId(), description, millis);
- return null;
- }
-
- @Override
- protected void done(Void result)
- {
- load(true);
- Util.toast(getContext(), getResources().getString(R.string.playlist_updated_info, share.getName()));
- }
-
- @Override
- protected void error(Throwable error)
- {
- String msg;
- msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.playlist_updated_info_error, share.getName()), getErrorMessage(error));
-
- Util.toast(getContext(), msg, false);
- }
- }.execute();
- }
- });
-
- alertDialog.setNegativeButton(R.string.common_cancel, null);
- alertDialog.show();
- }
-}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt
index d3ae8caf..a0c607c0 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt
@@ -44,6 +44,7 @@ import com.google.android.material.navigation.NavigationView
import io.reactivex.rxjava3.disposables.CompositeDisposable
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
+import org.moire.ultrasonic.NavigationGraphDirections
import org.moire.ultrasonic.R
import org.moire.ultrasonic.app.UApp
import org.moire.ultrasonic.data.ActiveServerProvider
@@ -76,6 +77,9 @@ class NavigationActivity : AppCompatActivity() {
private var bookmarksMenuItem: MenuItem? = null
private var sharesMenuItem: MenuItem? = null
private var podcastsMenuItem: MenuItem? = null
+ private var playlistsMenuItem: MenuItem? = null
+ private var downloadsMenuItem: MenuItem? = null
+
private var nowPlayingView: FragmentContainerView? = null
private var nowPlayingHidden = false
private var navigationView: NavigationView? = null
@@ -274,15 +278,25 @@ class NavigationActivity : AppCompatActivity() {
private fun setupNavigationMenu(navController: NavController) {
navigationView?.setupWithNavController(navController)
- // The exit menu is handled here manually
- val exitItem: MenuItem? = navigationView?.menu?.findItem(R.id.menu_exit)
- exitItem?.setOnMenuItemClickListener { item ->
- if (item.itemId == R.id.menu_exit) {
- setResult(Constants.RESULT_CLOSE_ALL)
- mediaPlayerController.stopJukeboxService()
- finish()
- exit()
+ // The fragments which expect SafeArgs need to be navigated to with SafeArgs (even when
+ // they are empty)!
+ navigationView?.setNavigationItemSelectedListener {
+ when (it.itemId) {
+ R.id.mediaLibraryFragment -> {
+ navController.navigate(NavigationGraphDirections.toMediaLibrary())
+ }
+ R.id.bookmarksFragment -> {
+ navController.navigate(NavigationGraphDirections.toBookmarks())
+ }
+ R.id.menu_exit -> {
+ setResult(Constants.RESULT_CLOSE_ALL)
+ mediaPlayerController.stopJukeboxService()
+ finish()
+ exit()
+ }
+ else -> navController.navigate(it.itemId)
}
+ drawerLayout?.closeDrawer(GravityCompat.START)
true
}
@@ -290,6 +304,9 @@ class NavigationActivity : AppCompatActivity() {
bookmarksMenuItem = navigationView?.menu?.findItem(R.id.bookmarksFragment)
sharesMenuItem = navigationView?.menu?.findItem(R.id.sharesFragment)
podcastsMenuItem = navigationView?.menu?.findItem(R.id.podcastFragment)
+ playlistsMenuItem = navigationView?.menu?.findItem(R.id.playlistsFragment)
+ downloadsMenuItem = navigationView?.menu?.findItem(R.id.downloadsFragment)
+
selectServerButton =
navigationView?.getHeaderView(0)?.findViewById(R.id.header_select_server)
selectServerButton?.setOnClickListener {
@@ -457,17 +474,17 @@ class NavigationActivity : AppCompatActivity() {
}
private fun setMenuForServerCapabilities() {
- if (ActiveServerProvider.isOffline()) {
- chatMenuItem?.isVisible = false
- bookmarksMenuItem?.isVisible = false
- sharesMenuItem?.isVisible = false
- podcastsMenuItem?.isVisible = false
- return
- }
+ val isOnline = !ActiveServerProvider.isOffline()
val activeServer = activeServerProvider.getActiveServer()
+
+ // Note: Offline capabilities are defined in ActiveServerProvider, OFFLINE_DB.
+ // If you add Offline support for some of these features you need
+ // to switch the boolean to true there.
chatMenuItem?.isVisible = activeServer.chatSupport != false
bookmarksMenuItem?.isVisible = activeServer.bookmarkSupport != false
sharesMenuItem?.isVisible = activeServer.shareSupport != false
podcastsMenuItem?.isVisible = activeServer.podcastSupport != false
+ playlistsMenuItem?.isVisible = isOnline
+ downloadsMenuItem?.isVisible = isOnline
}
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt
index d7b50e7a..bba08b76 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt
@@ -207,7 +207,11 @@ class ActiveServerProvider(
allowSelfSignedCertificate = false,
ldapSupport = false,
musicFolderId = "",
- minimumApiVersion = null
+ minimumApiVersion = null,
+ bookmarkSupport = false,
+ podcastSupport = false,
+ shareSupport = false,
+ chatSupport = false
)
/**
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumListFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumListFragment.kt
index 2557aea4..ea918f33 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumListFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumListFragment.kt
@@ -1,23 +1,28 @@
/*
* AlbumListFragment.kt
- * Copyright (C) 2009-2021 Ultrasonic developers
+ * Copyright (C) 2009-2022 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
+@file:Suppress("NAME_SHADOWING")
+
package org.moire.ultrasonic.fragment
import android.os.Bundle
import android.view.View
import androidx.fragment.app.viewModels
import androidx.lifecycle.LiveData
+import androidx.lifecycle.viewModelScope
import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.RecyclerView
+import kotlinx.coroutines.launch
import org.moire.ultrasonic.R
import org.moire.ultrasonic.adapters.AlbumRowBinder
+import org.moire.ultrasonic.api.subsonic.models.AlbumListType
import org.moire.ultrasonic.domain.Album
import org.moire.ultrasonic.model.AlbumListModel
-import org.moire.ultrasonic.util.Constants
/**
* Displays a list of Albums from the media library
@@ -39,33 +44,58 @@ class AlbumListFragment : EntryListFragment() {
*/
override val refreshOnCreation: Boolean = false
+ private val navArgs: AlbumListFragmentArgs by navArgs()
+
/**
* The central function to pass a query to the model and return a LiveData object
*/
override fun getLiveData(
- args: Bundle?,
refresh: Boolean
): LiveData> {
- if (args == null) throw IllegalArgumentException("Required arguments are missing")
+ fetchAlbums(refresh)
- val refresh2 = args.getBoolean(Constants.INTENT_REFRESH) || refresh
- val append = args.getBoolean(Constants.INTENT_APPEND)
-
- return listModel.getAlbumList(refresh2 or append, refreshListView!!, args)
+ return listModel.list
}
+ private fun fetchAlbums(refresh: Boolean = navArgs.refresh, append: Boolean = navArgs.append) {
+ val refresh = navArgs.refresh || refresh
+
+ listModel.viewModelScope.launch(handler) {
+ refreshListView?.isRefreshing = true
+
+ if (navArgs.type == AlbumListType.BY_ARTIST) {
+ listModel.getAlbumsOfArtist(
+ refresh = navArgs.refresh,
+ id = navArgs.id!!,
+ name = navArgs.title
+ )
+ } else {
+ listModel.getAlbums(
+ albumListType = navArgs.type,
+ size = navArgs.size,
+ offset = navArgs.offset,
+ append = append,
+ refresh = refresh or append
+ )
+ }
+ refreshListView?.isRefreshing = false
+ }
+ }
+
+ // TODO: Make generic
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ setTitle(navArgs.title)
+
// Attach our onScrollListener
listView = view.findViewById(recyclerViewId).apply {
val scrollListener = object : EndlessScrollListener(viewManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, view: RecyclerView?) {
// Triggered only when new data needs to be appended to the list
// Add whatever code is needed to append new items to the bottom of the list
- val appendArgs = getArgumentsClone()
- appendArgs.putBoolean(Constants.INTENT_APPEND, true)
- getLiveData(appendArgs)
+ fetchAlbums(append = true)
}
}
addOnScrollListener(scrollListener)
@@ -83,11 +113,12 @@ class AlbumListFragment : EntryListFragment() {
}
override fun onItemClick(item: Album) {
- val bundle = Bundle()
- bundle.putString(Constants.INTENT_ID, item.id)
- bundle.putBoolean(Constants.INTENT_IS_ALBUM, item.isDirectory)
- bundle.putString(Constants.INTENT_NAME, item.title)
- bundle.putString(Constants.INTENT_PARENT_ID, item.parent)
- findNavController().navigate(R.id.trackCollectionFragment, bundle)
+ val action = AlbumListFragmentDirections.albumListToTrackCollection(
+ item.id,
+ isAlbum = item.isDirectory,
+ name = item.title,
+ parentId = item.parent
+ )
+ findNavController().navigate(action)
}
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt
index 2446e05a..eef035d9 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt
@@ -1,18 +1,25 @@
+/*
+ * ArtistListFragment.kt
+ * Copyright (C) 2009-2022 Ultrasonic developers
+ *
+ * Distributed under terms of the GNU GPLv3 license.
+ */
+
package org.moire.ultrasonic.fragment
import android.os.Bundle
import android.view.View
import androidx.fragment.app.viewModels
import androidx.lifecycle.LiveData
-import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
import org.moire.ultrasonic.R
import org.moire.ultrasonic.adapters.ArtistRowBinder
+import org.moire.ultrasonic.api.subsonic.models.AlbumListType
import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.ArtistOrIndex
import org.moire.ultrasonic.domain.Index
import org.moire.ultrasonic.model.ArtistListModel
-import org.moire.ultrasonic.util.Constants
/**
* Displays the list of Artists or Indexes (folders) from the media library
@@ -29,16 +36,18 @@ class ArtistListFragment : EntryListFragment() {
*/
override val mainLayout = R.layout.list_layout_generic
+ private val navArgs: ArtistListFragmentArgs by navArgs()
+
/**
* The central function to pass a query to the model and return a LiveData object
*/
- override fun getLiveData(args: Bundle?, refresh: Boolean): LiveData> {
- val refresh2 = args?.getBoolean(Constants.INTENT_REFRESH) ?: false || refresh
- return listModel.getItems(refresh2, refreshListView!!)
+ override fun getLiveData(refresh: Boolean): LiveData> {
+ return listModel.getItems(navArgs.refresh || refresh, refreshListView!!)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ setTitle(navArgs.title)
viewAdapter.register(
ArtistRowBinder(
@@ -55,29 +64,24 @@ class ArtistListFragment : EntryListFragment() {
* If we are showing artists, we need to go to AlbumList
*/
override fun onItemClick(item: ArtistOrIndex) {
- Companion.onItemClick(item, findNavController())
- }
-
- companion object {
- fun onItemClick(item: ArtistOrIndex, navController: NavController) {
- val bundle = Bundle()
-
- // Common arguments
- bundle.putString(Constants.INTENT_ID, item.id)
- bundle.putString(Constants.INTENT_NAME, item.name)
- bundle.putString(Constants.INTENT_PARENT_ID, item.id)
- bundle.putBoolean(Constants.INTENT_ARTIST, (item is Artist))
-
- // Check type
- if (item is Index) {
- navController.navigate(R.id.artistsListToTrackCollection, bundle)
- } else {
- bundle.putString(Constants.INTENT_ALBUM_LIST_TYPE, Constants.ALBUMS_OF_ARTIST)
- bundle.putString(Constants.INTENT_ALBUM_LIST_TITLE, item.name)
- bundle.putInt(Constants.INTENT_ALBUM_LIST_SIZE, 1000)
- bundle.putInt(Constants.INTENT_ALBUM_LIST_OFFSET, 0)
- navController.navigate(R.id.artistsListToAlbumsList, bundle)
- }
+ // Check type
+ val action = if (item is Index) {
+ ArtistListFragmentDirections.artistsListToTrackCollection(
+ id = item.id,
+ name = item.name,
+ parentId = item.id,
+ isArtist = (item is Artist)
+ )
+ } else {
+ ArtistListFragmentDirections.artistsListToAlbumsList(
+ type = AlbumListType.BY_ARTIST,
+ id = item.id,
+ title = item.name,
+ size = 1000,
+ offset = 0
+ )
}
+
+ findNavController().navigate(action)
}
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/BookmarksFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/BookmarksFragment.kt
index 25aa3b98..8cbf1a74 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/BookmarksFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/BookmarksFragment.kt
@@ -37,7 +37,6 @@ class BookmarksFragment : TrackCollectionFragment() {
}
override fun getLiveData(
- args: Bundle?,
refresh: Boolean
): LiveData> {
listModel.viewModelScope.launch(handler) {
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt
index 3b6b302b..8a93da28 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt
@@ -41,7 +41,7 @@ class DownloadsFragment : MultiListFragment