Merge pull request #3611 from ByteHamster/feeditem-viewpager

Switched from gestures to ViewPager
This commit is contained in:
H. Lehmann 2019-11-15 17:13:37 +01:00 committed by GitHub
commit a6e4c4756c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 236 additions and 144 deletions

View File

@ -40,6 +40,7 @@ import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
import de.danoeh.antennapod.fragment.ItemPagerFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
@ -245,7 +246,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
MainActivity mainActivity = mainActivityRef.get();
if (mainActivity != null) {
long[] ids = itemAccess.getItemsIds().toArray();
mainActivity.loadChildFragment(ItemFragment.newInstance(ids, getAdapterPosition()));
mainActivity.loadChildFragment(ItemPagerFragment.newInstance(ids, getAdapterPosition()));
}
}

View File

@ -1,7 +1,6 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.view.MotionEventCompat;
@ -26,10 +25,10 @@ import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.fragment.ItemPagerFragment;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
@ -44,7 +43,6 @@ import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
@ -165,7 +163,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
if (activity != null) {
long[] ids = itemAccess.getQueueIds().toArray();
int position = ArrayUtils.indexOf(ids, item.getId());
activity.loadChildFragment(ItemFragment.newInstance(ids, position));
activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
}

View File

@ -83,7 +83,7 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(items);
((MainActivity) requireActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
((MainActivity) requireActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
@Override

View File

@ -344,7 +344,7 @@ public class FeedItemlistFragment extends ListFragment {
position -= l.getHeaderViewsCount();
MainActivity activity = (MainActivity) getActivity();
long[] ids = FeedItemUtil.getIds(feed.getItems());
activity.loadChildFragment(ItemFragment.newInstance(ids, position));
activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
activity.getSupportActionBar().setTitle(feed.getTitle());
}

View File

@ -6,52 +6,40 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.core.content.ContextCompat;
import androidx.core.view.GestureDetectorCompat;
import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import org.apache.commons.lang3.ArrayUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
@ -61,61 +49,45 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.view.OnSwipeGesture;
import de.danoeh.antennapod.view.SwipeGestureDetector;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import org.apache.commons.lang3.ArrayUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
/**
* Displays information about a FeedItem and actions.
*/
public class ItemFragment extends Fragment implements OnSwipeGesture {
public class ItemFragment extends Fragment {
private static final String TAG = "ItemFragment";
private static final String ARG_FEEDITEMS = "feeditems";
private static final String ARG_FEEDITEM_POS = "feeditem_pos";
private GestureDetectorCompat headerGestureDetector;
private GestureDetectorCompat webviewGestureDetector;
private static final String ARG_FEEDITEM = "feeditem";
/**
* Creates a new instance of an ItemFragment
*
* @param feeditem The ID of the FeedItem that should be displayed.
* @param feeditem The ID of the FeedItem to show
* @return The ItemFragment instance
*/
public static ItemFragment newInstance(long feeditem) {
return newInstance(new long[] { feeditem }, 0);
}
/**
* Creates a new instance of an ItemFragment
*
* @param feeditems The IDs of the FeedItems that belong to the same list
* @param feedItemPos The position of the FeedItem that is currently shown
* @return The ItemFragment instance
*/
public static ItemFragment newInstance(long[] feeditems, int feedItemPos) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
args.putLongArray(ARG_FEEDITEMS, feeditems);
args.putInt(ARG_FEEDITEM_POS, feedItemPos);
args.putLong(ARG_FEEDITEM, feeditem);
fragment.setArguments(args);
return fragment;
}
private boolean itemsLoaded = false;
private long[] feedItems;
private int feedItemPos;
private long itemId;
private FeedItem item;
private String webviewData;
private List<Downloader> downloaderList;
@ -131,7 +103,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private ProgressBar progbarLoading;
private IconButton butAction1;
private IconButton butAction2;
private Menu popupMenu;
private Disposable disposable;
@ -143,20 +114,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
feedItems = getArguments().getLongArray(ARG_FEEDITEMS);
feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS);
headerGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this));
webviewGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this) {
// necessary for the longclick context menu to work properly
@Override
public boolean onDown(MotionEvent e) {
return false;
}
});
itemId = getArguments().getLong(ARG_FEEDITEM);
}
@Override
@ -166,11 +125,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
root = layout.findViewById(R.id.content_root);
LinearLayout header = root.findViewById(R.id.header);
if(feedItems.length > 0) {
header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event));
}
txtvPodcast = layout.findViewById(R.id.txtvPodcast);
txtvPodcast.setOnClickListener(v -> openPodcast());
txtvTitle = layout.findViewById(R.id.txtvTitle);
@ -201,10 +155,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webvDescription.getSettings().setLoadWithOverviewMode(true);
if(feedItems.length > 0) {
webvDescription.setOnLongClickListener(webViewLongClickListener);
}
webvDescription.setOnTouchListener((v, event) -> webviewGestureDetector.onTouchEvent(event));
webvDescription.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
@ -257,6 +209,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
return layout;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
load();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@ -266,7 +224,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
load();
}
@Override
@ -296,68 +253,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
@Override
public boolean onSwipeLeftToRight() {
return swipeFeedItem(-1);
}
@Override
public boolean onSwipeRightToLeft() {
return swipeFeedItem(+1);
}
private boolean swipeFeedItem(int position) {
Log.d(TAG, String.format("onSwipe() shift: %s", position));
feedItemPos = (feedItemPos + position) % feedItems.length;
if (feedItemPos < 0) {
feedItemPos = feedItems.length - 1;
}
load();
return true;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if(!isAdded() || item == null) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
if (Flavors.FLAVOR == Flavors.PLAY) {
((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
inflater.inflate(R.menu.feeditem_options, menu);
popupMenu = menu;
if (item.hasMedia()) {
FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item);
} else {
// these are already available via button1 and button2
FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item,
R.id.mark_read_item, R.id.visit_website_item);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch(menuItem.getItemId()) {
case R.id.open_podcast:
openPodcast();
return true;
default:
return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item);
}
}
private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() {
@Override
public void setItemVisibility(int id, boolean visible) {
MenuItem item = popupMenu.findItem(id);
if (item != null) {
item.setVisible(visible);
}
}
};
private void onFragmentLoaded() {
if (webviewData != null) {
webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank");
@ -370,7 +265,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
Log.d(TAG, "updateAppearance item is null");
return;
}
getActivity().supportInvalidateOptionsMenu();
txtvPodcast.setText(item.getFeed().getTitle());
txtvTitle.setText(item.getTitle());
@ -529,8 +423,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
for(FeedItem item : event.items) {
if(feedItems[feedItemPos] == item.getId()) {
for (FeedItem item : event.items) {
if (this.item.getId() == item.getId()) {
load();
return;
}
@ -559,7 +453,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
private void load() {
if(disposable != null) {
if (disposable != null) {
disposable.dispose();
}
progbarLoading.setVisibility(View.VISIBLE);
@ -576,7 +470,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Nullable
private FeedItem loadInBackground() {
FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
FeedItem feedItem = DBReader.getFeedItem(itemId);
Context context = getContext();
if (feedItem != null && context != null) {
Timeline t = new Timeline(context, feedItem);

View File

@ -0,0 +1,193 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.ViewPager;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Displays information about a list of FeedItems.
*/
public class ItemPagerFragment extends Fragment {
private static final String ARG_FEEDITEMS = "feeditems";
private static final String ARG_FEEDITEM_POS = "feeditem_pos";
/**
* Creates a new instance of an ItemPagerFragment.
*
* @param feeditem The ID of the FeedItem that should be displayed.
* @return The ItemFragment instance
*/
public static ItemPagerFragment newInstance(long feeditem) {
return newInstance(new long[] { feeditem }, 0);
}
/**
* Creates a new instance of an ItemPagerFragment.
*
* @param feeditems The IDs of the FeedItems that belong to the same list
* @param feedItemPos The position of the FeedItem that is currently shown
* @return The ItemFragment instance
*/
public static ItemPagerFragment newInstance(long[] feeditems, int feedItemPos) {
ItemPagerFragment fragment = new ItemPagerFragment();
Bundle args = new Bundle();
args.putLongArray(ARG_FEEDITEMS, feeditems);
args.putInt(ARG_FEEDITEM_POS, feedItemPos);
fragment.setArguments(args);
return fragment;
}
private long[] feedItems;
private int feedItemPos;
private FeedItem item;
private Disposable disposable;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
feedItems = getArguments().getLongArray(ARG_FEEDITEMS);
feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View layout = inflater.inflate(R.layout.feeditem_pager_fragment, container, false);
ViewPager pager = layout.findViewById(R.id.pager);
// FragmentStatePagerAdapter documentation:
// > When using FragmentStatePagerAdapter the host ViewPager must have a valid ID set.
// When opening multiple ItemPagerFragments by clicking "item" -> "visit podcast" -> "item" -> etc,
// the ID is no longer unique and FragmentStatePagerAdapter does not display any pages.
int newId = ViewCompat.generateViewId();
pager.setId(newId);
pager.setAdapter(new ItemPagerAdapter());
pager.setCurrentItem(feedItemPos);
loadItem(feedItems[feedItemPos]);
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
loadItem(feedItems[position]);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
return layout;
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (disposable != null) {
disposable.dispose();
}
}
private void loadItem(long itemId) {
if (disposable != null) {
disposable.dispose();
}
disposable = Observable.fromCallable(() -> DBReader.getFeedItem(itemId))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
item = result;
getActivity().invalidateOptionsMenu();
}, Throwable::printStackTrace);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (!isAdded() || item == null) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
if (Flavors.FLAVOR == Flavors.PLAY) {
((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
inflater.inflate(R.menu.feeditem_options, menu);
FeedItemMenuHandler.MenuInterface popupMenuInterface = (id, visible) -> {
MenuItem item = menu.findItem(id);
if (item != null) {
item.setVisible(visible);
}
};
if (item.hasMedia()) {
FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item);
} else {
// these are already available via button1 and button2
FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item,
R.id.mark_read_item, R.id.visit_website_item);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.open_podcast:
openPodcast();
return true;
default:
return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item);
}
}
private void openPodcast() {
Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId());
((MainActivity) getActivity()).loadChildFragment(fragment);
}
private class ItemPagerAdapter extends FragmentStatePagerAdapter {
ItemPagerAdapter() {
super(getFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@NonNull
@Override
public Fragment getItem(int position) {
return ItemFragment.newInstance(feedItems[position]);
}
@Override
public int getCount() {
return feedItems.length;
}
}
}

View File

@ -106,7 +106,7 @@ public class PlaybackHistoryFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(playbackHistory);
((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
((MainActivity) getActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
@Override

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -52,7 +52,7 @@ public class Timeline {
+ "a.timecode {"
+ "color: #669900;"
+ "}"
+ "img {"
+ "img, iframe {"
+ "display: block;"
+ "margin: 10 auto;"
+ "max-width: %s;"