mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 17:43:32 +00:00
feat: add ability to add podcast channels
This commit is contained in:
parent
0248187f41
commit
7f4be7ad3e
10 changed files with 204 additions and 2 deletions
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.cappielloantonio.play.interfaces;
|
||||||
|
|
||||||
|
public interface PodcastCallback {
|
||||||
|
|
||||||
|
void onDismiss();
|
||||||
|
}
|
||||||
|
|
@ -83,6 +83,23 @@ public class PodcastRepository {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void createPodcastChannel(String url) {
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getPodcastClient()
|
||||||
|
.createPodcastChannel(url)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void deletePodcastChannel(String channelId) {
|
public void deletePodcastChannel(String channelId) {
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getPodcastClient()
|
.getPodcastClient()
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@ public interface PodcastService {
|
||||||
@GET("refreshPodcasts")
|
@GET("refreshPodcasts")
|
||||||
Call<ApiResponse> refreshPodcasts(@QueryMap Map<String, String> params);
|
Call<ApiResponse> refreshPodcasts(@QueryMap Map<String, String> params);
|
||||||
|
|
||||||
|
@GET("createPodcastChannel")
|
||||||
|
Call<ApiResponse> createPodcastChannel(@QueryMap Map<String, String> params, @Query("url") String url);
|
||||||
|
|
||||||
@GET("deletePodcastChannel")
|
@GET("deletePodcastChannel")
|
||||||
Call<ApiResponse> deletePodcastChannel(@QueryMap Map<String, String> params, @Query("id") String id);
|
Call<ApiResponse> deletePodcastChannel(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package com.cappielloantonio.play.ui.fragment;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
@ -19,11 +20,13 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
import com.cappielloantonio.play.databinding.FragmentHomeTabPodcastBinding;
|
import com.cappielloantonio.play.databinding.FragmentHomeTabPodcastBinding;
|
||||||
import com.cappielloantonio.play.interfaces.ClickCallback;
|
import com.cappielloantonio.play.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.play.interfaces.PodcastCallback;
|
||||||
import com.cappielloantonio.play.service.MediaManager;
|
import com.cappielloantonio.play.service.MediaManager;
|
||||||
import com.cappielloantonio.play.service.MediaService;
|
import com.cappielloantonio.play.service.MediaService;
|
||||||
import com.cappielloantonio.play.ui.activity.MainActivity;
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.play.ui.adapter.PodcastChannelHorizontalAdapter;
|
import com.cappielloantonio.play.ui.adapter.PodcastChannelHorizontalAdapter;
|
||||||
import com.cappielloantonio.play.ui.adapter.PodcastEpisodeAdapter;
|
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.Constants;
|
||||||
import com.cappielloantonio.play.util.Preferences;
|
import com.cappielloantonio.play.util.Preferences;
|
||||||
import com.cappielloantonio.play.util.UIUtil;
|
import com.cappielloantonio.play.util.UIUtil;
|
||||||
|
|
@ -34,7 +37,7 @@ import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class HomeTabPodcastFragment extends Fragment implements ClickCallback {
|
public class HomeTabPodcastFragment extends Fragment implements ClickCallback, PodcastCallback {
|
||||||
private static final String TAG = "HomeTabPodcastFragment";
|
private static final String TAG = "HomeTabPodcastFragment";
|
||||||
|
|
||||||
private FragmentHomeTabPodcastBinding bind;
|
private FragmentHomeTabPodcastBinding bind;
|
||||||
|
|
@ -88,6 +91,11 @@ public class HomeTabPodcastFragment extends Fragment implements ClickCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
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.podcastChannelsTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_homeFragment_to_podcastChannelCatalogueFragment));
|
||||||
bind.hideSectionButton.setOnClickListener(v -> Preferences.setPodcastSectionHidden());
|
bind.hideSectionButton.setOnClickListener(v -> Preferences.setPodcastSectionHidden());
|
||||||
}
|
}
|
||||||
|
|
@ -169,4 +177,12 @@ public class HomeTabPodcastFragment extends Fragment implements ClickCallback {
|
||||||
public void onPodcastChannelLongClick(Bundle bundle) {
|
public void onPodcastChannelLongClick(Bundle bundle) {
|
||||||
Navigation.findNavController(requireView()).navigate(R.id.podcastChannelBottomSheetDialog, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -41,4 +41,12 @@ public class PodcastViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
return podcastChannels;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
app/src/main/res/layout/dialog_podcast_channel_editor.xml
Normal file
27
app/src/main/res/layout/dialog_podcast_channel_editor.xml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:textColorHint="?android:textColorHint"
|
||||||
|
app:endIconMode="clear_text"
|
||||||
|
app:endIconTint="?android:textColorSecondary"
|
||||||
|
app:errorEnabled="true">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/podcast_channel_rss_url_name_text_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/podcast_channel_editor_dialog_hint_rss_url"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
android:textCursorDrawable="@null" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -26,6 +26,17 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingBottom="8dp">
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/podcast_channels_pre_text_view"
|
||||||
|
style="@style/TitleMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:text="@string/home_subtitle_new_podcast_channel"
|
||||||
|
android:textAllCaps="true" />
|
||||||
|
|
||||||
<!-- Label and button -->
|
<!-- Label and button -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
@ -38,7 +49,6 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:text="@string/home_title_podcast_channels" />
|
android:text="@string/home_title_podcast_channels" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@
|
||||||
<string name="genre_catalogue_title">Genre Catalogue</string>
|
<string name="genre_catalogue_title">Genre Catalogue</string>
|
||||||
<string name="genre_catalogue_title_expanded">Browse Genres</string>
|
<string name="genre_catalogue_title_expanded">Browse Genres</string>
|
||||||
<string name="home_subtitle_new_internet_radio_station">Add a new radio</string>
|
<string name="home_subtitle_new_internet_radio_station">Add a new radio</string>
|
||||||
|
<string name="home_subtitle_new_podcast_channel">Add a new podcast channel</string>
|
||||||
<string name="home_subtitle_made_for_you">Start mix from a song you liked</string>
|
<string name="home_subtitle_made_for_you">Start mix from a song you liked</string>
|
||||||
<string name="home_sync_starred_title">Looks like there are some starred tracks to sync</string>
|
<string name="home_sync_starred_title">Looks like there are some starred tracks to sync</string>
|
||||||
<string name="home_sync_starred_subtitle">Downloading these tracks may involve significant data usage</string>
|
<string name="home_sync_starred_subtitle">Downloading these tracks may involve significant data usage</string>
|
||||||
|
|
@ -124,6 +125,8 @@
|
||||||
<string name="podcast_channel_catalogue_title">Channels</string>
|
<string name="podcast_channel_catalogue_title">Channels</string>
|
||||||
<string name="podcast_channel_page_title_description_section">Description</string>
|
<string name="podcast_channel_page_title_description_section">Description</string>
|
||||||
<string name="podcast_channel_page_title_episode_section">Episodes</string>
|
<string name="podcast_channel_page_title_episode_section">Episodes</string>
|
||||||
|
<string name="podcast_channel_editor_dialog_hint_rss_url">RSS Url</string>
|
||||||
|
<string name="podcast_channel_editor_dialog_title">Podcast Channel</string>
|
||||||
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
<string name="podcast_release_date_duration_formatter">%1$s • %2$s</string>
|
||||||
|
|
||||||
<string name="podcast_info_empty_subtitle">Once you add a channel, you\'ll find it here</string>
|
<string name="podcast_info_empty_subtitle">Once you add a channel, you\'ll find it here</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue