From 7f4be7ad3e5ce39e4cf7043089cfc45c0e4bb158 Mon Sep 17 00:00:00 2001 From: antonio Date: Sun, 4 Jun 2023 20:43:08 +0200 Subject: [PATCH] feat: add ability to add podcast channels --- .../play/interfaces/PodcastCallback.java | 6 ++ .../play/repository/PodcastRepository.java | 17 ++++ .../subsonic/api/podcast/PodcastService.java | 3 + .../ui/dialog/PodcastChannelEditorDialog.java | 84 +++++++++++++++++++ .../ui/fragment/HomeTabPodcastFragment.java | 18 +++- .../PodcastChannelEditorViewModel.java | 28 +++++++ .../play/viewmodel/PodcastViewModel.java | 8 ++ .../layout/dialog_podcast_channel_editor.xml | 27 ++++++ .../res/layout/fragment_home_tab_podcast.xml | 12 ++- app/src/main/res/values/strings.xml | 3 + 10 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/cappielloantonio/play/interfaces/PodcastCallback.java create mode 100644 app/src/main/java/com/cappielloantonio/play/ui/dialog/PodcastChannelEditorDialog.java create mode 100644 app/src/main/java/com/cappielloantonio/play/viewmodel/PodcastChannelEditorViewModel.java create mode 100644 app/src/main/res/layout/dialog_podcast_channel_editor.xml diff --git a/app/src/main/java/com/cappielloantonio/play/interfaces/PodcastCallback.java b/app/src/main/java/com/cappielloantonio/play/interfaces/PodcastCallback.java new file mode 100644 index 00000000..9d5a43c7 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/interfaces/PodcastCallback.java @@ -0,0 +1,6 @@ +package com.cappielloantonio.play.interfaces; + +public interface PodcastCallback { + + void onDismiss(); +} diff --git a/app/src/main/java/com/cappielloantonio/play/repository/PodcastRepository.java b/app/src/main/java/com/cappielloantonio/play/repository/PodcastRepository.java index 593165f1..2440c051 100644 --- a/app/src/main/java/com/cappielloantonio/play/repository/PodcastRepository.java +++ b/app/src/main/java/com/cappielloantonio/play/repository/PodcastRepository.java @@ -83,6 +83,23 @@ public class PodcastRepository { }); } + public void createPodcastChannel(String url) { + App.getSubsonicClientInstance(false) + .getPodcastClient() + .createPodcastChannel(url) + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + + } + }); + } + public void deletePodcastChannel(String channelId) { App.getSubsonicClientInstance(false) .getPodcastClient() diff --git a/app/src/main/java/com/cappielloantonio/play/subsonic/api/podcast/PodcastService.java b/app/src/main/java/com/cappielloantonio/play/subsonic/api/podcast/PodcastService.java index e7bef3ad..b9861bdc 100644 --- a/app/src/main/java/com/cappielloantonio/play/subsonic/api/podcast/PodcastService.java +++ b/app/src/main/java/com/cappielloantonio/play/subsonic/api/podcast/PodcastService.java @@ -20,6 +20,9 @@ public interface PodcastService { @GET("refreshPodcasts") Call refreshPodcasts(@QueryMap Map params); + @GET("createPodcastChannel") + Call createPodcastChannel(@QueryMap Map params, @Query("url") String url); + @GET("deletePodcastChannel") Call deletePodcastChannel(@QueryMap Map params, @Query("id") String id); diff --git a/app/src/main/java/com/cappielloantonio/play/ui/dialog/PodcastChannelEditorDialog.java b/app/src/main/java/com/cappielloantonio/play/ui/dialog/PodcastChannelEditorDialog.java new file mode 100644 index 00000000..d11d6edb --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/ui/dialog/PodcastChannelEditorDialog.java @@ -0,0 +1,84 @@ +package com.cappielloantonio.play.ui.dialog; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.os.Bundle; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.lifecycle.ViewModelProvider; + +import com.cappielloantonio.play.R; +import com.cappielloantonio.play.databinding.DialogPodcastChannelEditorBinding; +import com.cappielloantonio.play.interfaces.PodcastCallback; +import com.cappielloantonio.play.viewmodel.PodcastChannelEditorViewModel; + +import java.util.Objects; + +public class PodcastChannelEditorDialog extends DialogFragment { + private DialogPodcastChannelEditorBinding bind; + private PodcastChannelEditorViewModel podcastChannelEditorViewModel; + private PodcastCallback podcastCallback; + + private String channelUrl; + + public PodcastChannelEditorDialog(PodcastCallback podcastCallback) { + this.podcastCallback = podcastCallback; + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + bind = DialogPodcastChannelEditorBinding.inflate(getLayoutInflater()); + podcastChannelEditorViewModel = new ViewModelProvider(requireActivity()).get(PodcastChannelEditorViewModel.class); + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + builder.setView(bind.getRoot()) + .setTitle(R.string.podcast_channel_editor_dialog_title) + .setPositiveButton(R.string.radio_editor_dialog_positive_button, (dialog, id) -> { + }) + .setNegativeButton(R.string.radio_editor_dialog_negative_button, (dialog, id) -> dialog.cancel()); + + return builder.create(); + } + + @Override + public void onStart() { + super.onStart(); + + setButtonAction(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + bind = null; + } + + private void setButtonAction() { + ((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> { + if (validateInput()) { + podcastChannelEditorViewModel.createChannel(channelUrl); + dismissDialog(); + } + }); + } + + private boolean validateInput() { + channelUrl = Objects.requireNonNull(bind.podcastChannelRssUrlNameTextView.getText()).toString().trim(); + + if (TextUtils.isEmpty(channelUrl)) { + bind.podcastChannelRssUrlNameTextView.setError(getString(R.string.error_required)); + return false; + } + + return true; + } + + private void dismissDialog() { + podcastCallback.onDismiss(); + Objects.requireNonNull(getDialog()).dismiss(); + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeTabPodcastFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeTabPodcastFragment.java index 64ea7392..f0cf0227 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeTabPodcastFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeTabPodcastFragment.java @@ -2,6 +2,7 @@ package com.cappielloantonio.play.ui.fragment; import android.content.ComponentName; import android.os.Bundle; +import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -19,11 +20,13 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.cappielloantonio.play.R; import com.cappielloantonio.play.databinding.FragmentHomeTabPodcastBinding; import com.cappielloantonio.play.interfaces.ClickCallback; +import com.cappielloantonio.play.interfaces.PodcastCallback; import com.cappielloantonio.play.service.MediaManager; import com.cappielloantonio.play.service.MediaService; import com.cappielloantonio.play.ui.activity.MainActivity; import com.cappielloantonio.play.ui.adapter.PodcastChannelHorizontalAdapter; import com.cappielloantonio.play.ui.adapter.PodcastEpisodeAdapter; +import com.cappielloantonio.play.ui.dialog.PodcastChannelEditorDialog; import com.cappielloantonio.play.util.Constants; import com.cappielloantonio.play.util.Preferences; import com.cappielloantonio.play.util.UIUtil; @@ -34,7 +37,7 @@ import java.util.Objects; import java.util.stream.Collectors; @UnstableApi -public class HomeTabPodcastFragment extends Fragment implements ClickCallback { +public class HomeTabPodcastFragment extends Fragment implements ClickCallback, PodcastCallback { private static final String TAG = "HomeTabPodcastFragment"; private FragmentHomeTabPodcastBinding bind; @@ -88,6 +91,11 @@ public class HomeTabPodcastFragment extends Fragment implements ClickCallback { } private void init() { + bind.podcastChannelsPreTextView.setOnClickListener(v -> { + PodcastChannelEditorDialog dialog = new PodcastChannelEditorDialog(this); + dialog.show(activity.getSupportFragmentManager(), null); + }); + bind.podcastChannelsTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_homeFragment_to_podcastChannelCatalogueFragment)); bind.hideSectionButton.setOnClickListener(v -> Preferences.setPodcastSectionHidden()); } @@ -169,4 +177,12 @@ public class HomeTabPodcastFragment extends Fragment implements ClickCallback { public void onPodcastChannelLongClick(Bundle bundle) { Navigation.findNavController(requireView()).navigate(R.id.podcastChannelBottomSheetDialog, bundle); } + + @Override + public void onDismiss() { + new Handler().postDelayed(() -> { + if (podcastViewModel != null) podcastViewModel.refreshPodcastChannels(getViewLifecycleOwner()); + if (podcastViewModel != null) podcastViewModel.refreshNewestPodcastEpisodes(getViewLifecycleOwner()); + }, 1000); + } } diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/PodcastChannelEditorViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/PodcastChannelEditorViewModel.java new file mode 100644 index 00000000..40ca0395 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/PodcastChannelEditorViewModel.java @@ -0,0 +1,28 @@ +package com.cappielloantonio.play.viewmodel; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; + +import com.cappielloantonio.play.repository.PodcastRepository; +import com.cappielloantonio.play.repository.RadioRepository; +import com.cappielloantonio.play.subsonic.models.InternetRadioStation; + +public class PodcastChannelEditorViewModel extends AndroidViewModel { + private static final String TAG = "RadioEditorViewModel"; + + private final PodcastRepository podcastRepository; + + private InternetRadioStation toEdit; + + public PodcastChannelEditorViewModel(@NonNull Application application) { + super(application); + + podcastRepository = new PodcastRepository(); + } + + public void createChannel(String url) { + podcastRepository.createPodcastChannel(url); + } +} diff --git a/app/src/main/java/com/cappielloantonio/play/viewmodel/PodcastViewModel.java b/app/src/main/java/com/cappielloantonio/play/viewmodel/PodcastViewModel.java index 2dcfac1c..6b616704 100644 --- a/app/src/main/java/com/cappielloantonio/play/viewmodel/PodcastViewModel.java +++ b/app/src/main/java/com/cappielloantonio/play/viewmodel/PodcastViewModel.java @@ -41,4 +41,12 @@ public class PodcastViewModel extends AndroidViewModel { return podcastChannels; } + + public void refreshNewestPodcastEpisodes(LifecycleOwner owner) { + podcastRepository.getNewestPodcastEpisodes(20).observe(owner, newestPodcastEpisodes::postValue); + } + + public void refreshPodcastChannels(LifecycleOwner owner) { + podcastRepository.getPodcastChannels(false, null).observe(owner, podcastChannels::postValue); + } } diff --git a/app/src/main/res/layout/dialog_podcast_channel_editor.xml b/app/src/main/res/layout/dialog_podcast_channel_editor.xml new file mode 100644 index 00000000..51657a07 --- /dev/null +++ b/app/src/main/res/layout/dialog_podcast_channel_editor.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home_tab_podcast.xml b/app/src/main/res/layout/fragment_home_tab_podcast.xml index 31660b34..8aaab099 100644 --- a/app/src/main/res/layout/fragment_home_tab_podcast.xml +++ b/app/src/main/res/layout/fragment_home_tab_podcast.xml @@ -26,6 +26,17 @@ android:orientation="vertical" android:paddingBottom="8dp"> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9b172dd4..db68a5e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -59,6 +59,7 @@ Genre Catalogue Browse Genres Add a new radio + Add a new podcast channel Start mix from a song you liked Looks like there are some starred tracks to sync Downloading these tracks may involve significant data usage @@ -124,6 +125,8 @@ Channels Description Episodes + RSS Url + Podcast Channel %1$s • %2$s Once you add a channel, you\'ll find it here