parent
ec92722c04
commit
77104c9038
|
@ -9,8 +9,6 @@ import android.media.AudioManager;
|
|||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -19,7 +17,6 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
|
@ -32,14 +29,11 @@ import androidx.fragment.app.FragmentContainerView;
|
|||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import de.danoeh.antennapod.fragment.AllEpisodesFragment;
|
||||
import de.danoeh.antennapod.fragment.CompletedDownloadsFragment;
|
||||
import de.danoeh.antennapod.playback.cast.CastEnabledActivity;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
@ -47,14 +41,15 @@ import org.greenrobot.eventbus.Subscribe;
|
|||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.event.MessageEvent;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.dialog.RatingDialog;
|
||||
import de.danoeh.antennapod.event.MessageEvent;
|
||||
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
||||
import de.danoeh.antennapod.fragment.AudioPlayerFragment;
|
||||
import de.danoeh.antennapod.fragment.CompletedDownloadsFragment;
|
||||
import de.danoeh.antennapod.fragment.InboxFragment;
|
||||
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
||||
import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
||||
|
@ -63,9 +58,11 @@ import de.danoeh.antennapod.fragment.QueueFragment;
|
|||
import de.danoeh.antennapod.fragment.SearchFragment;
|
||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||
import de.danoeh.antennapod.fragment.TransitionEffect;
|
||||
import de.danoeh.antennapod.playback.cast.CastEnabledActivity;
|
||||
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
|
||||
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
|
||||
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||
import de.danoeh.antennapod.ui.home.HomeFragment;
|
||||
import de.danoeh.antennapod.view.LockableBottomSheetBehavior;
|
||||
|
||||
/**
|
||||
|
@ -222,13 +219,6 @@ public class MainActivity extends CastEnabledActivity {
|
|||
private void checkFirstLaunch() {
|
||||
SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
||||
if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) {
|
||||
loadFragment(AddFeedFragment.TAG, null);
|
||||
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||
if (drawerLayout != null) { // Tablet layout does not have a drawer
|
||||
drawerLayout.openDrawer(navDrawer);
|
||||
}
|
||||
}, 1500);
|
||||
|
||||
// for backward compatibility, we only change defaults for fresh installs
|
||||
UserPreferences.setUpdateInterval(12);
|
||||
AutoUpdateManager.restartUpdateAlarm(this);
|
||||
|
@ -264,6 +254,9 @@ public class MainActivity extends CastEnabledActivity {
|
|||
Log.d(TAG, "loadFragment(tag: " + tag + ", args: " + args + ")");
|
||||
Fragment fragment;
|
||||
switch (tag) {
|
||||
case HomeFragment.TAG:
|
||||
fragment = new HomeFragment();
|
||||
break;
|
||||
case QueueFragment.TAG:
|
||||
fragment = new QueueFragment();
|
||||
break;
|
||||
|
@ -286,9 +279,9 @@ public class MainActivity extends CastEnabledActivity {
|
|||
fragment = new SubscriptionFragment();
|
||||
break;
|
||||
default:
|
||||
// default to the queue
|
||||
fragment = new QueueFragment();
|
||||
tag = QueueFragment.TAG;
|
||||
// default to home screen
|
||||
fragment = new HomeFragment();
|
||||
tag = HomeFragment.TAG;
|
||||
args = null;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public class EpisodeItemListAdapter extends SelectableAdapter<EpisodeItemViewHol
|
|||
private List<FeedItem> episodes = new ArrayList<>();
|
||||
private FeedItem longPressedItem;
|
||||
int longPressedPosition = 0; // used to init actionMode
|
||||
private int dummyViews = 0;
|
||||
|
||||
public EpisodeItemListAdapter(MainActivity mainActivity) {
|
||||
super(mainActivity);
|
||||
|
@ -45,6 +46,10 @@ public class EpisodeItemListAdapter extends SelectableAdapter<EpisodeItemViewHol
|
|||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
public void setDummyViews(int dummyViews) {
|
||||
this.dummyViews = dummyViews;
|
||||
}
|
||||
|
||||
public void updateItems(List<FeedItem> items) {
|
||||
episodes = items;
|
||||
notifyDataSetChanged();
|
||||
|
@ -64,6 +69,11 @@ public class EpisodeItemListAdapter extends SelectableAdapter<EpisodeItemViewHol
|
|||
|
||||
@Override
|
||||
public final void onBindViewHolder(EpisodeItemViewHolder holder, int pos) {
|
||||
if (pos >= episodes.size()) {
|
||||
holder.bindDummy();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset state of recycled views
|
||||
holder.coverHolder.setVisibility(View.VISIBLE);
|
||||
holder.dragHandle.setVisibility(View.GONE);
|
||||
|
@ -155,13 +165,16 @@ public class EpisodeItemListAdapter extends SelectableAdapter<EpisodeItemViewHol
|
|||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
if (position >= episodes.size()) {
|
||||
return RecyclerView.NO_ID; // Dummy views
|
||||
}
|
||||
FeedItem item = episodes.get(position);
|
||||
return item != null ? item.getId() : RecyclerView.NO_POSITION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return episodes.size();
|
||||
return dummyViews + episodes.size();
|
||||
}
|
||||
|
||||
protected FeedItem getItem(int index) {
|
||||
|
|
|
@ -11,20 +11,25 @@ import de.danoeh.antennapod.activity.MainActivity;
|
|||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
||||
import de.danoeh.antennapod.ui.common.SquareImageView;
|
||||
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FeedSearchResultAdapter extends RecyclerView.Adapter<FeedSearchResultAdapter.Holder> {
|
||||
|
||||
public class HorizontalFeedListAdapter extends RecyclerView.Adapter<HorizontalFeedListAdapter.Holder> {
|
||||
private final WeakReference<MainActivity> mainActivityRef;
|
||||
private final List<Feed> data = new ArrayList<>();
|
||||
private int dummyViews = 0;
|
||||
|
||||
public FeedSearchResultAdapter(MainActivity mainActivity) {
|
||||
public HorizontalFeedListAdapter(MainActivity mainActivity) {
|
||||
this.mainActivityRef = new WeakReference<>(mainActivity);
|
||||
}
|
||||
|
||||
public void setDummyViews(int dummyViews) {
|
||||
this.dummyViews = dummyViews;
|
||||
}
|
||||
|
||||
public void updateData(List<Feed> newData) {
|
||||
data.clear();
|
||||
data.addAll(newData);
|
||||
|
@ -34,12 +39,21 @@ public class FeedSearchResultAdapter extends RecyclerView.Adapter<FeedSearchResu
|
|||
@NonNull
|
||||
@Override
|
||||
public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View convertView = View.inflate(mainActivityRef.get(), R.layout.searchlist_item_feed, null);
|
||||
View convertView = View.inflate(mainActivityRef.get(), R.layout.horizontal_feed_item, null);
|
||||
return new Holder(convertView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull Holder holder, int position) {
|
||||
if (position >= data.size()) {
|
||||
holder.itemView.setAlpha(0.1f);
|
||||
Glide.with(mainActivityRef.get()).clear(holder.imageView);
|
||||
holder.imageView.setImageResource(
|
||||
ThemeUtils.getDrawableFromAttr(mainActivityRef.get(), android.R.attr.textColorSecondary));
|
||||
return;
|
||||
}
|
||||
|
||||
holder.itemView.setAlpha(1.0f);
|
||||
final Feed podcast = data.get(position);
|
||||
holder.imageView.setContentDescription(podcast.getTitle());
|
||||
holder.imageView.setOnClickListener(v ->
|
||||
|
@ -56,12 +70,15 @@ public class FeedSearchResultAdapter extends RecyclerView.Adapter<FeedSearchResu
|
|||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
if (position >= data.size()) {
|
||||
return RecyclerView.NO_ID; // Dummy views
|
||||
}
|
||||
return data.get(position).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
return dummyViews + data.size();
|
||||
}
|
||||
|
||||
static class Holder extends RecyclerView.ViewHolder {
|
|
@ -0,0 +1,138 @@
|
|||
package de.danoeh.antennapod.adapter;
|
||||
|
||||
import android.view.ContextMenu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.fragment.ItemPagerFragment;
|
||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.view.viewholder.HorizontalItemViewHolder;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class HorizontalItemListAdapter extends RecyclerView.Adapter<HorizontalItemViewHolder>
|
||||
implements View.OnCreateContextMenuListener {
|
||||
private final WeakReference<MainActivity> mainActivityRef;
|
||||
private List<FeedItem> data = new ArrayList<>();
|
||||
private FeedItem longPressedItem;
|
||||
private int dummyViews = 0;
|
||||
|
||||
public HorizontalItemListAdapter(MainActivity mainActivity) {
|
||||
this.mainActivityRef = new WeakReference<>(mainActivity);
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
public void setDummyViews(int dummyViews) {
|
||||
this.dummyViews = dummyViews;
|
||||
}
|
||||
|
||||
public void updateData(List<FeedItem> newData) {
|
||||
data = newData;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public HorizontalItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new HorizontalItemViewHolder(mainActivityRef.get(), parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull HorizontalItemViewHolder holder, int position) {
|
||||
if (position >= data.size()) {
|
||||
holder.bindDummy();
|
||||
return;
|
||||
}
|
||||
|
||||
final FeedItem item = data.get(position);
|
||||
holder.bind(item);
|
||||
|
||||
holder.card.setOnCreateContextMenuListener(this);
|
||||
holder.card.setOnLongClickListener(v -> {
|
||||
longPressedItem = item;
|
||||
return false;
|
||||
});
|
||||
holder.secondaryActionIcon.setOnCreateContextMenuListener(this);
|
||||
holder.secondaryActionIcon.setOnLongClickListener(v -> {
|
||||
longPressedItem = item;
|
||||
return false;
|
||||
});
|
||||
holder.card.setOnClickListener(v -> {
|
||||
MainActivity activity = mainActivityRef.get();
|
||||
if (activity != null) {
|
||||
long[] ids = FeedItemUtil.getIds(data);
|
||||
int clickPosition = ArrayUtils.indexOf(ids, item.getId());
|
||||
activity.loadChildFragment(ItemPagerFragment.newInstance(ids, clickPosition));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
if (position >= data.size()) {
|
||||
return RecyclerView.NO_ID; // Dummy views
|
||||
}
|
||||
return data.get(position).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return dummyViews + data.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewRecycled(@NonNull HorizontalItemViewHolder holder) {
|
||||
super.onViewRecycled(holder);
|
||||
// Set all listeners to null. This is required to prevent leaking fragments that have set a listener.
|
||||
// Activity -> recycledViewPool -> ViewHolder -> Listener -> Fragment (can not be garbage collected)
|
||||
holder.card.setOnClickListener(null);
|
||||
holder.card.setOnCreateContextMenuListener(null);
|
||||
holder.card.setOnLongClickListener(null);
|
||||
holder.secondaryActionIcon.setOnClickListener(null);
|
||||
holder.secondaryActionIcon.setOnCreateContextMenuListener(null);
|
||||
holder.secondaryActionIcon.setOnLongClickListener(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #notifyItemChanged(int)} is final, so we can not override.
|
||||
* Calling {@link #notifyItemChanged(int)} may bind the item to a new ViewHolder and execute a transition.
|
||||
* This causes flickering and breaks the download animation that stores the old progress in the View.
|
||||
* Instead, we tell the adapter to use partial binding by calling {@link #notifyItemChanged(int, Object)}.
|
||||
* We actually ignore the payload and always do a full bind but calling the partial bind method ensures
|
||||
* that ViewHolders are always re-used.
|
||||
*
|
||||
* @param position Position of the item that has changed
|
||||
*/
|
||||
public void notifyItemChangedCompat(int position) {
|
||||
notifyItemChanged(position, "foo");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FeedItem getLongPressedItem() {
|
||||
return longPressedItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
MenuInflater inflater = mainActivityRef.get().getMenuInflater();
|
||||
if (longPressedItem == null) {
|
||||
return;
|
||||
}
|
||||
menu.clear();
|
||||
inflater.inflate(R.menu.feeditemlist_context, menu);
|
||||
menu.setHeaderTitle(longPressedItem.getTitle());
|
||||
FeedItemMenuHandler.onPrepareMenu(menu, longPressedItem, R.id.skip_episode_item);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -36,6 +36,7 @@ import de.danoeh.antennapod.fragment.NavDrawerFragment;
|
|||
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
|
||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||
import de.danoeh.antennapod.ui.home.HomeFragment;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
@ -112,6 +113,8 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
|
|||
|
||||
private @DrawableRes int getDrawable(String tag) {
|
||||
switch (tag) {
|
||||
case HomeFragment.TAG:
|
||||
return R.drawable.ic_home;
|
||||
case QueueFragment.TAG:
|
||||
return R.drawable.ic_playlist_play;
|
||||
case InboxFragment.TAG:
|
||||
|
|
|
@ -13,7 +13,6 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
@ -29,18 +28,19 @@ import de.danoeh.antennapod.activity.PreferenceActivity;
|
|||
import de.danoeh.antennapod.adapter.NavListAdapter;
|
||||
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
|
||||
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.event.FeedListUpdateEvent;
|
||||
import de.danoeh.antennapod.event.QueueEvent;
|
||||
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
|
||||
import de.danoeh.antennapod.dialog.TagSettingsDialog;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.NavDrawerData;
|
||||
import de.danoeh.antennapod.dialog.RemoveFeedDialog;
|
||||
import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog;
|
||||
import de.danoeh.antennapod.dialog.RenameItemDialog;
|
||||
import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog;
|
||||
import de.danoeh.antennapod.dialog.TagSettingsDialog;
|
||||
import de.danoeh.antennapod.event.FeedListUpdateEvent;
|
||||
import de.danoeh.antennapod.event.QueueEvent;
|
||||
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.ui.home.HomeFragment;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
@ -65,6 +65,7 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
|
|||
public static final String TAG = "NavDrawerFragment";
|
||||
|
||||
public static final String[] NAV_DRAWER_TAGS = {
|
||||
HomeFragment.TAG,
|
||||
QueueFragment.TAG,
|
||||
InboxFragment.TAG,
|
||||
AllEpisodesFragment.TAG,
|
||||
|
@ -430,7 +431,7 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
|
|||
|
||||
public static String getLastNavFragment(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
||||
String lastFragment = prefs.getString(PREF_LAST_FRAGMENT_TAG, QueueFragment.TAG);
|
||||
String lastFragment = prefs.getString(PREF_LAST_FRAGMENT_TAG, HomeFragment.TAG);
|
||||
Log.d(TAG, "getLastNavFragment() -> " + lastFragment);
|
||||
return lastFragment;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import com.google.android.material.chip.Chip;
|
|||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
|
||||
import de.danoeh.antennapod.adapter.FeedSearchResultAdapter;
|
||||
import de.danoeh.antennapod.adapter.HorizontalFeedListAdapter;
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
||||
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
|
||||
|
@ -65,7 +65,7 @@ public class SearchFragment extends Fragment {
|
|||
private static final int SEARCH_DEBOUNCE_INTERVAL = 1500;
|
||||
|
||||
private EpisodeItemListAdapter adapter;
|
||||
private FeedSearchResultAdapter adapterFeeds;
|
||||
private HorizontalFeedListAdapter adapterFeeds;
|
||||
private Disposable disposable;
|
||||
private ProgressBar progressBar;
|
||||
private EmptyViewHandler emptyViewHandler;
|
||||
|
@ -144,7 +144,7 @@ public class SearchFragment extends Fragment {
|
|||
LinearLayoutManager layoutManagerFeeds = new LinearLayoutManager(getActivity());
|
||||
layoutManagerFeeds.setOrientation(RecyclerView.HORIZONTAL);
|
||||
recyclerViewFeeds.setLayoutManager(layoutManagerFeeds);
|
||||
adapterFeeds = new FeedSearchResultAdapter((MainActivity) getActivity());
|
||||
adapterFeeds = new HorizontalFeedListAdapter((MainActivity) getActivity());
|
||||
recyclerViewFeeds.setAdapter(adapterFeeds);
|
||||
|
||||
emptyViewHandler = new EmptyViewHandler(getContext());
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
package de.danoeh.antennapod.ui.home;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentContainerView;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.databinding.HomeFragmentBinding;
|
||||
import de.danoeh.antennapod.event.FeedListUpdateEvent;
|
||||
import de.danoeh.antennapod.fragment.SearchFragment;
|
||||
import de.danoeh.antennapod.ui.home.sections.DownloadsSection;
|
||||
import de.danoeh.antennapod.ui.home.sections.EpisodesSurpriseSection;
|
||||
import de.danoeh.antennapod.ui.home.sections.InboxSection;
|
||||
import de.danoeh.antennapod.ui.home.sections.QueueSection;
|
||||
import de.danoeh.antennapod.ui.home.sections.SubscriptionsSection;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Shows unread or recently published episodes
|
||||
*/
|
||||
public class HomeFragment extends Fragment implements Toolbar.OnMenuItemClickListener {
|
||||
|
||||
public static final String TAG = "HomeFragment";
|
||||
public static final String PREF_NAME = "PrefHomeFragment";
|
||||
public static final String PREF_HIDDEN_SECTIONS = "PrefHomeSectionsString";
|
||||
|
||||
private static final String KEY_UP_ARROW = "up_arrow";
|
||||
private boolean displayUpArrow;
|
||||
private HomeFragmentBinding viewBinding;
|
||||
private boolean isUpdatingFeeds = false;
|
||||
private Disposable disposable;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
viewBinding = HomeFragmentBinding.inflate(inflater);
|
||||
viewBinding.toolbar.inflateMenu(R.menu.home);
|
||||
viewBinding.toolbar.setOnMenuItemClickListener(this);
|
||||
if (savedInstanceState != null) {
|
||||
displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW);
|
||||
}
|
||||
((MainActivity) requireActivity()).setupToolbarToggle(viewBinding.toolbar, displayUpArrow);
|
||||
refreshToolbarState();
|
||||
populateSectionList();
|
||||
updateWelcomeScreenVisibility();
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
private void populateSectionList() {
|
||||
viewBinding.homeContainer.removeAllViews();
|
||||
|
||||
List<String> hiddenSections = getHiddenSections(getContext());
|
||||
String[] sectionTags = getResources().getStringArray(R.array.home_section_tags);
|
||||
for (String sectionTag : sectionTags) {
|
||||
if (hiddenSections.contains(sectionTag)) {
|
||||
continue;
|
||||
}
|
||||
addSection(getSection(sectionTag));
|
||||
}
|
||||
}
|
||||
|
||||
private void addSection(Fragment section) {
|
||||
FragmentContainerView containerView = new FragmentContainerView(getContext());
|
||||
containerView.setId(View.generateViewId());
|
||||
viewBinding.homeContainer.addView(containerView);
|
||||
getChildFragmentManager().beginTransaction().add(containerView.getId(), section).commit();
|
||||
}
|
||||
|
||||
private Fragment getSection(String tag) {
|
||||
switch (tag) {
|
||||
case QueueSection.TAG:
|
||||
return new QueueSection();
|
||||
case InboxSection.TAG:
|
||||
return new InboxSection();
|
||||
case EpisodesSurpriseSection.TAG:
|
||||
return new EpisodesSurpriseSection();
|
||||
case SubscriptionsSection.TAG:
|
||||
return new SubscriptionsSection();
|
||||
case DownloadsSection.TAG:
|
||||
return new DownloadsSection();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getHiddenSections(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(HomeFragment.PREF_NAME, Context.MODE_PRIVATE);
|
||||
String hiddenSectionsString = prefs.getString(HomeFragment.PREF_HIDDEN_SECTIONS, "");
|
||||
return new ArrayList<>(Arrays.asList(TextUtils.split(hiddenSectionsString, ",")));
|
||||
}
|
||||
|
||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
||||
() -> DownloadService.isRunning && DownloadService.isDownloadingFeeds();
|
||||
|
||||
private void refreshToolbarState() {
|
||||
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(viewBinding.toolbar.getMenu(),
|
||||
R.id.refresh_item, updateRefreshMenuItemChecker);
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DownloadEvent event) {
|
||||
Log.d(TAG, "onEventMainThread() called with DownloadEvent");
|
||||
if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
|
||||
refreshToolbarState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
if (item.getItemId() == R.id.homesettings_items) {
|
||||
HomeSectionsSettingsDialog.open(getContext(), (dialogInterface, i) -> populateSectionList());
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.refresh_item) {
|
||||
AutoUpdateManager.runImmediate(requireContext());
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.action_search) {
|
||||
((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance());
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onFeedListChanged(FeedListUpdateEvent event) {
|
||||
updateWelcomeScreenVisibility();
|
||||
}
|
||||
|
||||
private void updateWelcomeScreenVisibility() {
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
disposable = Observable.fromCallable(() -> DBReader.getNavDrawerData().items.size())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(numSubscriptions -> {
|
||||
viewBinding.welcomeContainer.setVisibility(numSubscriptions == 0 ? View.VISIBLE : View.GONE);
|
||||
viewBinding.homeContainer.setVisibility(numSubscriptions == 0 ? View.GONE : View.VISIBLE);
|
||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package de.danoeh.antennapod.ui.home;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
|
||||
import de.danoeh.antennapod.adapter.HorizontalItemListAdapter;
|
||||
import de.danoeh.antennapod.databinding.HomeSectionBinding;
|
||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Section on the HomeFragment
|
||||
*/
|
||||
public abstract class HomeSection extends Fragment implements View.OnCreateContextMenuListener {
|
||||
public static final String TAG = "HomeSection";
|
||||
protected HomeSectionBinding viewBinding;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
viewBinding = HomeSectionBinding.inflate(inflater);
|
||||
viewBinding.titleLabel.setText(getSectionTitle());
|
||||
viewBinding.moreButton.setText(getString(R.string.navigate_arrows, getMoreLinkTitle()));
|
||||
viewBinding.moreButton.setOnClickListener((view) -> handleMoreClick());
|
||||
if (TextUtils.isEmpty(getMoreLinkTitle())) {
|
||||
viewBinding.moreButton.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(@NonNull MenuItem item) {
|
||||
if (!getUserVisibleHint() || !isVisible() || !isMenuVisible()) {
|
||||
// The method is called on all fragments in a ViewPager, so this needs to be ignored in invisible ones.
|
||||
// Apparently, none of the visibility check method works reliably on its own, so we just use all.
|
||||
return false;
|
||||
}
|
||||
FeedItem longPressedItem;
|
||||
if (viewBinding.recyclerView.getAdapter() instanceof EpisodeItemListAdapter) {
|
||||
EpisodeItemListAdapter adapter = (EpisodeItemListAdapter) viewBinding.recyclerView.getAdapter();
|
||||
longPressedItem = adapter.getLongPressedItem();
|
||||
} else if (viewBinding.recyclerView.getAdapter() instanceof HorizontalItemListAdapter) {
|
||||
HorizontalItemListAdapter adapter = (HorizontalItemListAdapter) viewBinding.recyclerView.getAdapter();
|
||||
longPressedItem = adapter.getLongPressedItem();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (longPressedItem == null) {
|
||||
Log.i(TAG, "Selected item or listAdapter was null, ignoring selection");
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), longPressedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
EventBus.getDefault().register(this);
|
||||
registerForContextMenu(viewBinding.recyclerView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
EventBus.getDefault().unregister(this);
|
||||
unregisterForContextMenu(viewBinding.recyclerView);
|
||||
}
|
||||
|
||||
protected abstract String getSectionTitle();
|
||||
|
||||
protected abstract String getMoreLinkTitle();
|
||||
|
||||
protected abstract void handleMoreClick();
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package de.danoeh.antennapod.ui.home;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import de.danoeh.antennapod.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HomeSectionsSettingsDialog {
|
||||
public static void open(Context context, DialogInterface.OnClickListener onSettingsChanged) {
|
||||
final List<String> hiddenSections = HomeFragment.getHiddenSections(context);
|
||||
String[] sectionLabels = context.getResources().getStringArray(R.array.home_section_titles);
|
||||
String[] sectionTags = context.getResources().getStringArray(R.array.home_section_tags);
|
||||
final boolean[] checked = new boolean[sectionLabels.length];
|
||||
for (int i = 0; i < sectionLabels.length; i++) {
|
||||
String tag = sectionTags[i];
|
||||
if (!hiddenSections.contains(tag)) {
|
||||
checked[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(R.string.configure_home);
|
||||
builder.setMultiChoiceItems(sectionLabels, checked, (dialog, which, isChecked) -> {
|
||||
if (isChecked) {
|
||||
hiddenSections.remove(sectionTags[which]);
|
||||
} else {
|
||||
hiddenSections.add(sectionTags[which]);
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
|
||||
SharedPreferences prefs = context.getSharedPreferences(HomeFragment.PREF_NAME, Context.MODE_PRIVATE);
|
||||
prefs.edit().putString(HomeFragment.PREF_HIDDEN_SECTIONS, TextUtils.join(",", hiddenSections)).apply();
|
||||
onSettingsChanged.onClick(dialog, which);
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel_label, null);
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package de.danoeh.antennapod.ui.home.sections;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
|
||||
import de.danoeh.antennapod.core.event.DownloadLogEvent;
|
||||
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.event.FeedItemEvent;
|
||||
import de.danoeh.antennapod.event.PlayerStatusEvent;
|
||||
import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.fragment.CompletedDownloadsFragment;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.ui.home.HomeSection;
|
||||
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DownloadsSection extends HomeSection {
|
||||
public static final String TAG = "DownloadsSection";
|
||||
private static final int NUM_EPISODES = 2;
|
||||
private EpisodeItemListAdapter adapter;
|
||||
private List<FeedItem> items;
|
||||
private Disposable disposable;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
viewBinding.recyclerView.setPadding(0, 0, 0, 0);
|
||||
viewBinding.recyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
|
||||
viewBinding.recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false));
|
||||
viewBinding.recyclerView.setRecycledViewPool(((MainActivity) requireActivity()).getRecycledViewPool());
|
||||
adapter = new EpisodeItemListAdapter((MainActivity) requireActivity()) {
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
MenuItemUtils.setOnClickListeners(menu, DownloadsSection.this::onContextItemSelected);
|
||||
}
|
||||
};
|
||||
adapter.setDummyViews(NUM_EPISODES);
|
||||
viewBinding.recyclerView.setAdapter(adapter);
|
||||
loadItems();
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMoreClick() {
|
||||
((MainActivity) requireActivity()).loadChildFragment(new CompletedDownloadsFragment());
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(FeedItemEvent event) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||
if (adapter == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < adapter.getItemCount(); i++) {
|
||||
EpisodeItemViewHolder holder = (EpisodeItemViewHolder)
|
||||
viewBinding.recyclerView.findViewHolderForAdapterPosition(i);
|
||||
if (holder != null && holder.isCurrentlyPlayingItem()) {
|
||||
holder.notifyPlaybackPositionUpdated(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onDownloadLogChanged(DownloadLogEvent event) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onPlayerStatusChanged(PlayerStatusEvent event) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSectionTitle() {
|
||||
return getString(R.string.home_downloads_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMoreLinkTitle() {
|
||||
return getString(R.string.downloads_label);
|
||||
}
|
||||
|
||||
private void loadItems() {
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
disposable = Observable.fromCallable(DBReader::getDownloadedItems)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(downloads -> {
|
||||
if (downloads.size() > NUM_EPISODES) {
|
||||
downloads = downloads.subList(0, NUM_EPISODES);
|
||||
}
|
||||
items = downloads;
|
||||
adapter.setDummyViews(0);
|
||||
adapter.updateItems(items);
|
||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
package de.danoeh.antennapod.ui.home.sections;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.HorizontalItemListAdapter;
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
||||
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.event.FeedItemEvent;
|
||||
import de.danoeh.antennapod.event.PlayerStatusEvent;
|
||||
import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.fragment.AllEpisodesFragment;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.ui.home.HomeSection;
|
||||
import de.danoeh.antennapod.view.viewholder.HorizontalItemViewHolder;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class EpisodesSurpriseSection extends HomeSection {
|
||||
public static final String TAG = "EpisodesSurpriseSection";
|
||||
private static final int NUM_EPISODES = 8;
|
||||
private static int seed = 0;
|
||||
private HorizontalItemListAdapter listAdapter;
|
||||
private Disposable disposable;
|
||||
private List<FeedItem> episodes;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
viewBinding.shuffleButton.setVisibility(View.VISIBLE);
|
||||
viewBinding.shuffleButton.setOnClickListener(v -> {
|
||||
seed = new Random().nextInt();
|
||||
viewBinding.recyclerView.scrollToPosition(0);
|
||||
loadItems();
|
||||
});
|
||||
listAdapter = new HorizontalItemListAdapter((MainActivity) getActivity()) {
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
MenuItemUtils.setOnClickListeners(menu, EpisodesSurpriseSection.this::onContextItemSelected);
|
||||
}
|
||||
};
|
||||
listAdapter.setDummyViews(NUM_EPISODES);
|
||||
viewBinding.recyclerView.setLayoutManager(
|
||||
new LinearLayoutManager(getContext(), RecyclerView.HORIZONTAL, false));
|
||||
viewBinding.recyclerView.setAdapter(listAdapter);
|
||||
if (seed == 0) {
|
||||
seed = new Random().nextInt();
|
||||
}
|
||||
loadItems();
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMoreClick() {
|
||||
((MainActivity) requireActivity()).loadChildFragment(new AllEpisodesFragment());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSectionTitle() {
|
||||
return getString(R.string.home_surprise_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMoreLinkTitle() {
|
||||
return getString(R.string.episodes_label);
|
||||
}
|
||||
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onPlayerStatusChanged(PlayerStatusEvent event) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(FeedItemEvent event) {
|
||||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||
if (episodes == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0, size = event.items.size(); i < size; i++) {
|
||||
FeedItem item = event.items.get(i);
|
||||
int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
|
||||
if (pos >= 0) {
|
||||
episodes.remove(pos);
|
||||
episodes.add(pos, item);
|
||||
listAdapter.notifyItemChangedCompat(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DownloadEvent event) {
|
||||
Log.d(TAG, "onEventMainThread() called with DownloadEvent");
|
||||
DownloaderUpdate update = event.update;
|
||||
if (listAdapter != null && update.mediaIds.length > 0) {
|
||||
for (long mediaId : update.mediaIds) {
|
||||
int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
|
||||
if (pos >= 0) {
|
||||
listAdapter.notifyItemChangedCompat(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||
if (listAdapter == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < listAdapter.getItemCount(); i++) {
|
||||
HorizontalItemViewHolder holder = (HorizontalItemViewHolder)
|
||||
viewBinding.recyclerView.findViewHolderForAdapterPosition(i);
|
||||
if (holder != null && holder.isCurrentlyPlayingItem()) {
|
||||
holder.notifyPlaybackPositionUpdated(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadItems() {
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
disposable = Observable.fromCallable(() -> DBReader.getRandomEpisodes(NUM_EPISODES, seed))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(episodes -> {
|
||||
this.episodes = episodes;
|
||||
listAdapter.setDummyViews(0);
|
||||
listAdapter.updateData(episodes);
|
||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package de.danoeh.antennapod.ui.home.sections;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
||||
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.event.FeedItemEvent;
|
||||
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
|
||||
import de.danoeh.antennapod.fragment.InboxFragment;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.storage.database.PodDBAdapter;
|
||||
import de.danoeh.antennapod.ui.home.HomeSection;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InboxSection extends HomeSection {
|
||||
public static final String TAG = "InboxSection";
|
||||
private static final int NUM_EPISODES = 2;
|
||||
private EpisodeItemListAdapter adapter;
|
||||
private List<FeedItem> items;
|
||||
private Disposable disposable;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
viewBinding.recyclerView.setPadding(0, 0, 0, 0);
|
||||
viewBinding.recyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
|
||||
viewBinding.recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false));
|
||||
viewBinding.recyclerView.setRecycledViewPool(((MainActivity) requireActivity()).getRecycledViewPool());
|
||||
adapter = new EpisodeItemListAdapter((MainActivity) requireActivity()) {
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
MenuItemUtils.setOnClickListeners(menu, InboxSection.this::onContextItemSelected);
|
||||
}
|
||||
};
|
||||
adapter.setDummyViews(NUM_EPISODES);
|
||||
viewBinding.recyclerView.setAdapter(adapter);
|
||||
loadItems();
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMoreClick() {
|
||||
((MainActivity) requireActivity()).loadChildFragment(new InboxFragment());
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(FeedItemEvent event) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DownloadEvent event) {
|
||||
Log.d(TAG, "onEventMainThread() called with DownloadEvent");
|
||||
DownloaderUpdate update = event.update;
|
||||
if (adapter != null && update.mediaIds.length > 0) {
|
||||
for (long mediaId : update.mediaIds) {
|
||||
int pos = FeedItemUtil.indexOfItemWithMediaId(items, mediaId);
|
||||
if (pos >= 0) {
|
||||
adapter.notifyItemChangedCompat(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSectionTitle() {
|
||||
return getString(R.string.home_new_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMoreLinkTitle() {
|
||||
return getString(R.string.inbox_label);
|
||||
}
|
||||
|
||||
private void loadItems() {
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
disposable = Observable.fromCallable(() ->
|
||||
new Pair<>(DBReader.getNewItemsList(0, NUM_EPISODES),
|
||||
PodDBAdapter.getInstance().getNumberOfNewItems()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(data -> {
|
||||
items = data.first;
|
||||
adapter.setDummyViews(0);
|
||||
adapter.updateItems(items);
|
||||
viewBinding.numNewItemsLabel.setVisibility(View.VISIBLE);
|
||||
viewBinding.numNewItemsLabel.setText(String.valueOf(data.second));
|
||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
package de.danoeh.antennapod.ui.home.sections;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.HorizontalItemListAdapter;
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
||||
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.event.FeedItemEvent;
|
||||
import de.danoeh.antennapod.event.PlayerStatusEvent;
|
||||
import de.danoeh.antennapod.event.QueueEvent;
|
||||
import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.ui.home.HomeSection;
|
||||
import de.danoeh.antennapod.view.viewholder.HorizontalItemViewHolder;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class QueueSection extends HomeSection {
|
||||
public static final String TAG = "QueueSection";
|
||||
private static final int NUM_EPISODES = 8;
|
||||
private HorizontalItemListAdapter listAdapter;
|
||||
private Disposable disposable;
|
||||
private List<FeedItem> queue;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
listAdapter = new HorizontalItemListAdapter((MainActivity) getActivity()) {
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
MenuItemUtils.setOnClickListeners(menu, QueueSection.this::onContextItemSelected);
|
||||
}
|
||||
};
|
||||
listAdapter.setDummyViews(NUM_EPISODES);
|
||||
viewBinding.recyclerView.setLayoutManager(
|
||||
new LinearLayoutManager(getContext(), RecyclerView.HORIZONTAL, false));
|
||||
viewBinding.recyclerView.setAdapter(listAdapter);
|
||||
loadItems();
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMoreClick() {
|
||||
((MainActivity) requireActivity()).loadChildFragment(new QueueFragment());
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onQueueChanged(QueueEvent event) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onPlayerStatusChanged(PlayerStatusEvent event) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(FeedItemEvent event) {
|
||||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||
if (queue == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0, size = event.items.size(); i < size; i++) {
|
||||
FeedItem item = event.items.get(i);
|
||||
int pos = FeedItemUtil.indexOfItemWithId(queue, item.getId());
|
||||
if (pos >= 0) {
|
||||
queue.remove(pos);
|
||||
queue.add(pos, item);
|
||||
listAdapter.notifyItemChangedCompat(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DownloadEvent event) {
|
||||
Log.d(TAG, "onEventMainThread() called with DownloadEvent");
|
||||
DownloaderUpdate update = event.update;
|
||||
if (listAdapter != null && update.mediaIds.length > 0) {
|
||||
for (long mediaId : update.mediaIds) {
|
||||
int pos = FeedItemUtil.indexOfItemWithMediaId(queue, mediaId);
|
||||
if (pos >= 0) {
|
||||
listAdapter.notifyItemChangedCompat(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||
if (listAdapter == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < listAdapter.getItemCount(); i++) {
|
||||
HorizontalItemViewHolder holder = (HorizontalItemViewHolder)
|
||||
viewBinding.recyclerView.findViewHolderForAdapterPosition(i);
|
||||
if (holder != null && holder.isCurrentlyPlayingItem()) {
|
||||
holder.notifyPlaybackPositionUpdated(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSectionTitle() {
|
||||
return getString(R.string.home_continue_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMoreLinkTitle() {
|
||||
return getString(R.string.queue_label);
|
||||
}
|
||||
|
||||
private void loadItems() {
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
disposable = Observable.fromCallable(() -> DBReader.getPausedQueue(NUM_EPISODES))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(queue -> {
|
||||
this.queue = queue;
|
||||
listAdapter.setDummyViews(0);
|
||||
listAdapter.updateData(queue);
|
||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package de.danoeh.antennapod.ui.home.sections;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.HorizontalFeedListAdapter;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.event.FeedListUpdateEvent;
|
||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.ui.home.HomeSection;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SubscriptionsSection extends HomeSection {
|
||||
public static final String TAG = "SubscriptionsSection";
|
||||
private static final int NUM_FEEDS = 8;
|
||||
private HorizontalFeedListAdapter listAdapter;
|
||||
private Disposable disposable;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
viewBinding.recyclerView.setLayoutManager(
|
||||
new LinearLayoutManager(getActivity(), RecyclerView.HORIZONTAL, false));
|
||||
listAdapter = new HorizontalFeedListAdapter((MainActivity) getActivity());
|
||||
listAdapter.setDummyViews(NUM_FEEDS);
|
||||
viewBinding.recyclerView.setAdapter(listAdapter);
|
||||
loadItems();
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMoreClick() {
|
||||
((MainActivity) requireActivity()).loadChildFragment(new SubscriptionFragment());
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onFeedListChanged(FeedListUpdateEvent event) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSectionTitle() {
|
||||
return getString(R.string.home_classics_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMoreLinkTitle() {
|
||||
return getString(R.string.subscriptions_label);
|
||||
}
|
||||
|
||||
private void loadItems() {
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
disposable = Observable.fromCallable(() -> DBReader.getStatistics(true, 0, Long.MAX_VALUE).feedTime)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(statisticsData -> {
|
||||
Collections.sort(statisticsData, (item1, item2) ->
|
||||
Long.compare(item2.timePlayed, item1.timePlayed));
|
||||
List<Feed> feeds = new ArrayList<>();
|
||||
for (int i = 0; i < statisticsData.size() && i < NUM_FEEDS; i++) {
|
||||
feeds.add(statisticsData.get(i).feed);
|
||||
}
|
||||
listAdapter.setDummyViews(0);
|
||||
listAdapter.updateData(feeds);
|
||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
}
|
||||
}
|
|
@ -198,6 +198,30 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
}
|
||||
|
||||
public void bindDummy() {
|
||||
container.setAlpha(0.1f);
|
||||
secondaryActionIcon.setImageDrawable(null);
|
||||
isInbox.setVisibility(View.VISIBLE);
|
||||
isVideo.setVisibility(View.GONE);
|
||||
isFavorite.setVisibility(View.GONE);
|
||||
isInQueue.setVisibility(View.GONE);
|
||||
title.setText("███████");
|
||||
pubDate.setText("████");
|
||||
duration.setText("████");
|
||||
secondaryActionProgress.setPercentage(0, null);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
position.setVisibility(View.GONE);
|
||||
dragHandle.setVisibility(View.GONE);
|
||||
size.setText("");
|
||||
itemView.setBackgroundResource(ThemeUtils.getDrawableFromAttr(activity, R.attr.selectableItemBackground));
|
||||
placeholder.setText("");
|
||||
new CoverLoader(activity)
|
||||
.withResource(ThemeUtils.getDrawableFromAttr(activity, android.R.attr.textColorSecondary))
|
||||
.withPlaceholderView(placeholder)
|
||||
.withCoverView(cover)
|
||||
.load();
|
||||
}
|
||||
|
||||
private void updateDuration(PlaybackPositionEvent event) {
|
||||
int currentPosition = event.getPosition();
|
||||
int timeDuration = event.getDuration();
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package de.danoeh.antennapod.view.viewholder;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.CoverLoader;
|
||||
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
|
||||
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.util.DateFormatter;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.ui.common.CircularProgressBar;
|
||||
import de.danoeh.antennapod.ui.common.SquareImageView;
|
||||
|
||||
public class HorizontalItemViewHolder extends RecyclerView.ViewHolder {
|
||||
public final View card;
|
||||
public final ImageView secondaryActionIcon;
|
||||
private final SquareImageView cover;
|
||||
private final TextView title;
|
||||
private final TextView date;
|
||||
private final ProgressBar progressBar;
|
||||
private final CircularProgressBar circularProgressBar;
|
||||
|
||||
private final MainActivity activity;
|
||||
private FeedItem item;
|
||||
|
||||
public HorizontalItemViewHolder(MainActivity activity, ViewGroup parent) {
|
||||
super(LayoutInflater.from(activity).inflate(R.layout.horizontal_itemlist_item, parent, false));
|
||||
this.activity = activity;
|
||||
|
||||
card = itemView.findViewById(R.id.card);
|
||||
cover = itemView.findViewById(R.id.cover);
|
||||
title = itemView.findViewById(R.id.titleLabel);
|
||||
date = itemView.findViewById(R.id.dateLabel);
|
||||
secondaryActionIcon = itemView.findViewById(R.id.secondaryActionIcon);
|
||||
circularProgressBar = itemView.findViewById(R.id.circularProgressBar);
|
||||
progressBar = itemView.findViewById(R.id.progressBar);
|
||||
itemView.setTag(this);
|
||||
}
|
||||
|
||||
public void bind(FeedItem item) {
|
||||
this.item = item;
|
||||
|
||||
card.setAlpha(1.0f);
|
||||
new CoverLoader(activity)
|
||||
.withUri(ImageResourceUtils.getEpisodeListImageLocation(item))
|
||||
.withFallbackUri(item.getFeed().getImageUrl())
|
||||
.withCoverView(cover)
|
||||
.load();
|
||||
title.setText(item.getTitle());
|
||||
date.setText(DateFormatter.formatAbbrev(activity, item.getPubDate()));
|
||||
ItemActionButton actionButton = ItemActionButton.forItem(item);
|
||||
actionButton.configure(secondaryActionIcon, secondaryActionIcon, activity);
|
||||
secondaryActionIcon.setFocusable(false);
|
||||
|
||||
FeedMedia media = item.getMedia();
|
||||
if (media == null) {
|
||||
circularProgressBar.setPercentage(0, item);
|
||||
} else {
|
||||
if (item.getMedia().getDuration() > 0) {
|
||||
progressBar.setProgress(100 * item.getMedia().getPosition() / item.getMedia().getDuration());
|
||||
}
|
||||
if (DownloadService.isDownloadingFile(media.getDownload_url())) {
|
||||
final DownloadRequest downloadRequest = DownloadService.findRequest(media.getDownload_url());
|
||||
float percent = 0.01f * downloadRequest.getProgressPercent();
|
||||
circularProgressBar.setPercentage(Math.max(percent, 0.01f), item);
|
||||
} else if (media.isDownloaded()) {
|
||||
circularProgressBar.setPercentage(1, item); // Do not animate 100% -> 0%
|
||||
} else {
|
||||
circularProgressBar.setPercentage(0, item); // Animate X% -> 0%
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void bindDummy() {
|
||||
card.setAlpha(0.1f);
|
||||
new CoverLoader(activity)
|
||||
.withResource(android.R.color.transparent)
|
||||
.withCoverView(cover)
|
||||
.load();
|
||||
title.setText("████ █████");
|
||||
date.setText("███");
|
||||
secondaryActionIcon.setImageDrawable(null);
|
||||
circularProgressBar.setPercentage(0, null);
|
||||
progressBar.setProgress(50);
|
||||
}
|
||||
|
||||
public boolean isCurrentlyPlayingItem() {
|
||||
return item.getMedia() != null && FeedItemUtil.isCurrentlyPlaying(item.getMedia());
|
||||
}
|
||||
|
||||
public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
|
||||
progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="?attr/actionBarTheme"
|
||||
app:title="@string/home_label"
|
||||
app:navigationIcon="?homeAsUpIndicator" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/welcomeContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:paddingHorizontal="32dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_gravity="start"
|
||||
android:src="@drawable/ic_curved_arrow" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/home_welcome_title"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/home_welcome_text"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/homeContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="12dp" />
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@id/moreButton"
|
||||
android:layout_alignBottom="@id/moreButton"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="18sp"
|
||||
tools:text="Title" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/shuffleButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/titleLabel"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_toEndOf="@+id/titleLabel"
|
||||
android:layout_toRightOf="@+id/titleLabel"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:srcCompat="@drawable/ic_shuffle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/numNewItemsLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="20dp"
|
||||
android:layout_alignBottom="@+id/titleLabel"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_toEndOf="@+id/titleLabel"
|
||||
android:layout_toRightOf="@+id/titleLabel"
|
||||
android:background="@drawable/bg_pill"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:text="6" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/moreButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="0dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:paddingVertical="0dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
tools:text="@string/discover_more"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/moreButton"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:clipToPadding="false"
|
||||
android:clipToOutline="false"
|
||||
android:clipChildren="false"
|
||||
android:paddingHorizontal="16dp" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:squareImageView="http://schemas.android.com/apk/de.danoeh.antennapod"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="96dp"
|
||||
android:padding="4dp"
|
||||
android:clipToPadding="false"
|
||||
android:clipToOutline="false"
|
||||
android:clipChildren="false">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardBackgroundColor="@color/non_square_icon_background"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardPreventCornerOverlap="false"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<de.danoeh.antennapod.ui.common.SquareImageView
|
||||
android:id="@+id/discovery_cover"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="96dp"
|
||||
android:elevation="4dp"
|
||||
android:outlineProvider="bounds"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="?android:attr/windowBackground"
|
||||
squareImageView:direction="height" />
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:squareImageView="http://schemas.android.com/apk/de.danoeh.antennapod"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:padding="4dp">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
app:cardCornerRadius="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/background_elevated"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#dddddd"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="128dp"
|
||||
android:background="@color/image_readability_tint">
|
||||
|
||||
<de.danoeh.antennapod.ui.common.SquareImageView
|
||||
android:id="@+id/cover"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:outlineProvider="bounds"
|
||||
tools:src="@tools:sample/avatars"
|
||||
squareImageView:direction="width" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="8dp"
|
||||
android:padding="3dp"
|
||||
app:srcCompat="@drawable/bg_circle" />
|
||||
|
||||
<de.danoeh.antennapod.ui.common.CircularProgressBar
|
||||
android:id="@+id/circularProgressBar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
app:foregroundColor="?attr/colorOnPrimary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/secondaryActionIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="8dp"
|
||||
android:padding="12dp"
|
||||
android:clickable="true"
|
||||
android:foreground="?attr/selectableItemBackgroundBorderless"
|
||||
app:tintMode="src_atop"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
app:srcCompat="@drawable/ic_play_24dp" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="4dp"
|
||||
android:max="100"
|
||||
android:layout_gravity="bottom"
|
||||
style="?attr/progressBarTheme"
|
||||
tools:background="@android:color/holo_blue_light" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleLabel"
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:paddingHorizontal="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:lines="2"
|
||||
android:singleLine="false"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="14sp"
|
||||
tools:text="@sample/episodes.json/data/title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:paddingHorizontal="4dp"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="textStart"
|
||||
android:textSize="14sp"
|
||||
style="@style/AntennaPod.TextView.ListItemSecondaryTitle" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
|
@ -13,7 +13,8 @@
|
|||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="?attr/actionBarTheme"
|
||||
android:layout_alignParentTop="true"
|
||||
app:title="@string/queue_label" />
|
||||
app:title="@string/queue_label"
|
||||
app:navigationIcon="?homeAsUpIndicator" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/info_bar"
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:squareImageView="http://schemas.android.com/apk/de.danoeh.antennapod"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="96dp"
|
||||
android:padding="4dp"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<de.danoeh.antennapod.ui.common.SquareImageView
|
||||
android:id="@+id/discovery_cover"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="96dp"
|
||||
android:elevation="4dp"
|
||||
android:outlineProvider="bounds"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="?android:attr/windowBackground"
|
||||
squareImageView:direction="height" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
@ -10,7 +11,8 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="?attr/actionBarTheme"
|
||||
android:layout_alignParentTop="true" />
|
||||
android:layout_alignParentTop="true"
|
||||
app:navigationIcon="?homeAsUpIndicator" />
|
||||
|
||||
<de.danoeh.antennapod.view.EpisodeItemListRecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:custom="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@drawable/ic_search"
|
||||
custom:showAsAction="always"
|
||||
android:title="@string/search_label"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh_item"
|
||||
android:title="@string/refresh_label"
|
||||
android:menuCategory="container"
|
||||
custom:showAsAction="always"
|
||||
android:icon="@drawable/ic_refresh"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/homesettings_items"
|
||||
android:icon="@drawable/ic_settings"
|
||||
android:menuCategory="container"
|
||||
android:title="@string/configure_home"
|
||||
custom:showAsAction="never"/>
|
||||
|
||||
</menu>
|
|
@ -396,6 +396,18 @@ public final class DBReader {
|
|||
}
|
||||
}
|
||||
|
||||
public static List<FeedItem> getRandomEpisodes(int limit, int seed) {
|
||||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||
adapter.open();
|
||||
try (Cursor cursor = adapter.getRandomEpisodesCursor(limit, seed)) {
|
||||
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
|
||||
loadAdditionalFeedItemListData(items);
|
||||
return items;
|
||||
} finally {
|
||||
adapter.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getTotalEpisodeCount(FeedItemFilter filter) {
|
||||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||
adapter.open();
|
||||
|
@ -615,6 +627,19 @@ public final class DBReader {
|
|||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static List<FeedItem> getPausedQueue(int limit) {
|
||||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||
adapter.open();
|
||||
try (Cursor cursor = adapter.getPausedQueueCursor(limit)) {
|
||||
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
|
||||
loadAdditionalFeedItemListData(items);
|
||||
return items;
|
||||
} finally {
|
||||
adapter.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a specific FeedItem from the database.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?attr/colorPrimary" />
|
||||
<corners android:radius="30dp" />
|
||||
<size android:width="60dp" android:height="60dp"/>
|
||||
</shape>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="?attr/colorPrimary" />
|
||||
<corners android:radius="20dp" />
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:autoMirrored="true">
|
||||
<path android:strokeColor="?attr/action_icon_color" android:strokeAlpha="0.4" android:strokeWidth="0.4" android:pathData="M 24.563 19.667 C 20.794 22.382 13.26 21.82 11.04 17.74 C 8.82 13.66 16.36 4.77 20.17 8.59 C 23.98 12.4 16.78 16.34 11.93 15.72 C 7.08 15.1 4.792 10.756 2.54 4.87"/>
|
||||
<path android:fillColor="?attr/action_icon_color" android:fillAlpha="0.4" android:pathData="M 0.608 5.581 L 4.568 4.368 L 1.183 0.599" />
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?attr/action_icon_color"
|
||||
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?attr/action_icon_color"
|
||||
android:pathData="M10.59,9.17L5.41,4 4,5.41l5.17,5.17 1.42,-1.41zM14.5,4l2.04,2.04L4,18.59 5.41,20 17.96,7.46 20,9.5L20,4h-5.5zM14.83,13.41l-1.41,1.41 3.13,3.13L14.5,20L20,20v-5.5l-2.04,2.04 -3.13,-3.13z"/>
|
||||
</vector>
|
|
@ -150,6 +150,7 @@
|
|||
</string-array>
|
||||
|
||||
<string-array name="nav_drawer_titles">
|
||||
<item>@string/home_label</item>
|
||||
<item>@string/queue_label</item>
|
||||
<item>@string/inbox_label</item>
|
||||
<item>@string/episodes_label</item>
|
||||
|
@ -188,6 +189,22 @@
|
|||
<item>3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="home_section_titles">
|
||||
<item>@string/home_continue_title</item>
|
||||
<item>@string/home_new_title</item>
|
||||
<item>@string/home_surprise_title</item>
|
||||
<item>@string/home_classics_title</item>
|
||||
<item>@string/home_downloads_title</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="home_section_tags">
|
||||
<item>QueueSection</item>
|
||||
<item>InboxSection</item>
|
||||
<item>EpisodesSurpriseSection</item>
|
||||
<item>SubscriptionsSection</item>
|
||||
<item>DownloadsSection</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="media_player_options">
|
||||
<item>@string/media_player_exoplayer_recommended</item>
|
||||
<item>@string/media_player_builtin</item>
|
||||
|
@ -265,6 +282,7 @@
|
|||
</string-array>
|
||||
|
||||
<string-array name="back_button_go_to_pages">
|
||||
<item>@string/home_label</item>
|
||||
<item>@string/queue_label</item>
|
||||
<item>@string/inbox_label</item>
|
||||
<item>@string/episodes_label</item>
|
||||
|
@ -272,6 +290,7 @@
|
|||
</string-array>
|
||||
|
||||
<string-array name="back_button_go_to_pages_tags">
|
||||
<item>HomeFragment</item>
|
||||
<item>QueueFragment</item>
|
||||
<item>InboxFragment</item>
|
||||
<item>EpisodesFragment</item>
|
||||
|
|
|
@ -45,7 +45,7 @@ public class FeedItemFilter implements Serializable {
|
|||
this(TextUtils.split(properties, ","));
|
||||
}
|
||||
|
||||
public FeedItemFilter(String[] properties) {
|
||||
public FeedItemFilter(String... properties) {
|
||||
this.properties = properties;
|
||||
|
||||
// see R.arrays.feed_filter_values
|
||||
|
|
|
@ -994,6 +994,17 @@ public class PodDBAdapter {
|
|||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
public final Cursor getPausedQueueCursor(int limit) {
|
||||
//playback position > 0 (paused), rank by last played, then rest of queue
|
||||
final String query = SELECT_FEED_ITEMS_AND_MEDIA
|
||||
+ " INNER JOIN " + TABLE_NAME_QUEUE
|
||||
+ " ON " + SELECT_KEY_ITEM_ID + " = " + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM
|
||||
+ " ORDER BY " + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + ">0 DESC , "
|
||||
+ TABLE_NAME_FEED_MEDIA + "." + KEY_LAST_PLAYED_TIME + " DESC , " + TABLE_NAME_QUEUE + "." + KEY_ID
|
||||
+ " LIMIT " + limit;
|
||||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
public final Cursor getFavoritesCursor(int offset, int limit) {
|
||||
final String query = SELECT_FEED_ITEMS_AND_MEDIA
|
||||
+ " INNER JOIN " + TABLE_NAME_FAVORITES
|
||||
|
@ -1051,6 +1062,25 @@ public class PodDBAdapter {
|
|||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
public Cursor getRandomEpisodesCursor(int limit, int seed) {
|
||||
final String allItemsRandomOrder = SELECT_FEED_ITEMS_AND_MEDIA
|
||||
+ " WHERE (" + KEY_READ + " = " + FeedItem.NEW + " OR " + KEY_READ + " = " + FeedItem.UNPLAYED + ") "
|
||||
// Only from the last two years. Older episodes frequently contain broken covers and stuff like that
|
||||
+ " AND " + KEY_PUBDATE + " > " + (System.currentTimeMillis() - 1000L * 3600L * 24L * 356L * 2)
|
||||
+ " ORDER BY " + randomEpisodeNumber(seed);
|
||||
final String query = "SELECT * FROM (" + allItemsRandomOrder + ")"
|
||||
+ " GROUP BY " + KEY_FEED
|
||||
+ " ORDER BY " + randomEpisodeNumber(seed * 3) + " DESC LIMIT " + limit;
|
||||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* SQLite does not support random seeds. Create our own "random" number based on that seed and the item ID
|
||||
*/
|
||||
private String randomEpisodeNumber(int seed) {
|
||||
return "((" + SELECT_KEY_ITEM_ID + " * " + seed + ") % 46471)";
|
||||
}
|
||||
|
||||
public final Cursor getTotalEpisodeCountCursor(FeedItemFilter filter) {
|
||||
String filterQuery = FeedItemFilterQuery.generateFrom(filter);
|
||||
String whereClause = "".equals(filterQuery) ? "" : " WHERE " + filterQuery;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<string name="statistics_label">Statistics</string>
|
||||
<string name="add_feed_label">Add Podcast</string>
|
||||
<string name="episodes_label">Episodes</string>
|
||||
<string name="home_label">Home</string>
|
||||
<string name="queue_label">Queue</string>
|
||||
<string name="inbox_label">Inbox</string>
|
||||
<string name="favorite_episodes_label">Favorites</string>
|
||||
|
@ -50,6 +51,17 @@
|
|||
<string name="statistics_counting_range">Played between %1$s and %2$s</string>
|
||||
<string name="statistics_counting_total">Played in total</string>
|
||||
|
||||
<!-- Home fragment -->
|
||||
<string name="home_surprise_title">Get surprised</string>
|
||||
<string name="home_classics_title">Check your classics</string>
|
||||
<string name="home_continue_title">Continue listening</string>
|
||||
<string name="home_new_title">Review the new</string>
|
||||
<string name="home_downloads_title">Manage downloads</string>
|
||||
<string name="home_welcome_title">Welcome to AntennaPod!</string>
|
||||
<string name="home_welcome_text">You are not subscribed to any podcasts yet. Open the side menu to add a podcast.</string>
|
||||
<string name="navigate_arrows">%s »</string>
|
||||
<string name="configure_home">Configure Home Screen</string>
|
||||
|
||||
<!-- Download Statistics fragment -->
|
||||
<string name="total_size_downloaded_podcasts">Total size of episodes on the device</string>
|
||||
|
||||
|
|
Loading…
Reference in New Issue