summaryrefslogtreecommitdiff
path: root/app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java')
-rw-r--r--app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java590
1 files changed, 590 insertions, 0 deletions
diff --git a/app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java
new file mode 100644
index 000000000..2b2808adc
--- /dev/null
+++ b/app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -0,0 +1,590 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.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 com.bumptech.glide.Glide;
+import com.joanzapata.iconify.Iconify;
+import com.joanzapata.iconify.widget.IconButton;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.List;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
+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.feed.EventDistributor;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.DownloadRequestException;
+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.IntentUtils;
+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 de.greenrobot.event.EventBus;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
+
+/**
+ * Displays information about a FeedItem and actions.
+ */
+public class ItemFragment extends Fragment implements OnSwipeGesture {
+
+ private static final String TAG = "ItemFragment";
+
+ private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE;
+
+ private static final String ARG_FEEDITEMS = "feeditems";
+ private static final String ARG_FEEDITEM_POS = "feeditem_pos";
+
+ private GestureDetectorCompat headerGestureDetector;
+ private GestureDetectorCompat webviewGestureDetector;
+
+ /**
+ * Creates a new instance of an ItemFragment
+ *
+ * @param feeditem The ID of the FeedItem that should be displayed.
+ * @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);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ private boolean itemsLoaded = false;
+ private long[] feedItems;
+ private int feedItemPos;
+ private FeedItem item;
+ private String webviewData;
+ private List<Downloader> downloaderList;
+
+ private ViewGroup root;
+ private WebView webvDescription;
+ private TextView txtvPodcast;
+ private TextView txtvTitle;
+ private TextView txtvDuration;
+ private TextView txtvPublished;
+ private ImageView imgvCover;
+ private ProgressBar progbarDownload;
+ private ProgressBar progbarLoading;
+ private IconButton butAction1;
+ private IconButton butAction2;
+ private Menu popupMenu;
+
+ private Subscription subscription;
+
+ /**
+ * URL that was selected via long-press.
+ */
+ private String selectedURL;
+
+ @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;
+ }
+ });
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View layout = inflater.inflate(R.layout.feeditem_fragment, container, false);
+
+ root = (ViewGroup) layout.findViewById(R.id.content_root);
+
+ LinearLayout header = (LinearLayout) root.findViewById(R.id.header);
+ if(feedItems.length > 0) {
+ header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event));
+ }
+
+ txtvPodcast = (TextView) layout.findViewById(R.id.txtvPodcast);
+ txtvPodcast.setOnClickListener(v -> openPodcast());
+ txtvTitle = (TextView) layout.findViewById(R.id.txtvTitle);
+ if(Build.VERSION.SDK_INT >= 23) {
+ txtvTitle.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
+ }
+ txtvDuration = (TextView) layout.findViewById(R.id.txtvDuration);
+ txtvPublished = (TextView) layout.findViewById(R.id.txtvPublished);
+ if (Build.VERSION.SDK_INT >= 14) { // ellipsize is causing problems on old versions, see #448
+ txtvTitle.setEllipsize(TextUtils.TruncateAt.END);
+ }
+ webvDescription = (WebView) layout.findViewById(R.id.webvDescription);
+ if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ if (Build.VERSION.SDK_INT >= 11
+ && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+ webvDescription.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.black));
+ }
+ webvDescription.getSettings().setUseWideViewPort(false);
+ 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) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ startActivity(intent);
+ }
+ return true;
+ }
+ });
+ registerForContextMenu(webvDescription);
+
+ imgvCover = (ImageView) layout.findViewById(R.id.imgvCover);
+ imgvCover.setOnClickListener(v -> openPodcast());
+ progbarDownload = (ProgressBar) layout.findViewById(R.id.progbarDownload);
+ progbarLoading = (ProgressBar) layout.findViewById(R.id.progbarLoading);
+ butAction1 = (IconButton) layout.findViewById(R.id.butAction1);
+ butAction2 = (IconButton) layout.findViewById(R.id.butAction2);
+
+ butAction1.setOnClickListener(v -> {
+ if (item == null) {
+ return;
+ }
+ DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getActivity());
+ actionButtonCallback.onActionButtonPressed(item);
+ FeedMedia media = item.getMedia();
+ if (media != null && media.isDownloaded()) {
+ // playback was started, dialog should close itself
+ ((MainActivity) getActivity()).dismissChildFragment();
+ }
+ });
+
+ butAction2.setOnClickListener(v -> {
+ if (item == null) {
+ return;
+ }
+
+ if (item.hasMedia()) {
+ FeedMedia media = item.getMedia();
+ if (!media.isDownloaded()) {
+ DBTasks.playMedia(getActivity(), media, true, true, true);
+ ((MainActivity) getActivity()).dismissChildFragment();
+ } else {
+ DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
+ }
+ } else if (item.getLink() != null) {
+ Uri uri = Uri.parse(item.getLink());
+ getActivity().startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ }
+ });
+
+ return layout;
+ }
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ load();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().registerSticky(this);
+ if(itemsLoaded) {
+ progbarLoading.setVisibility(View.GONE);
+ updateAppearance();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ if (webvDescription != null && root != null) {
+ root.removeView(webvDescription);
+ webvDescription.destroy();
+ }
+ }
+
+ @Override
+ public boolean onSwipeLeftToRight() {
+ Log.d(TAG, "onSwipeLeftToRight()");
+ feedItemPos = feedItemPos - 1;
+ if(feedItemPos < 0) {
+ feedItemPos = feedItems.length - 1;
+ }
+ load();
+ return true;
+ }
+
+ @Override
+ public boolean onSwipeRightToLeft() {
+ Log.d(TAG, "onSwipeRightToLeft()");
+ feedItemPos = (feedItemPos + 1) % feedItems.length;
+ load();
+ return true;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded() || item == null) {
+ return;
+ }
+// ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ inflater.inflate(R.menu.feeditem_options, menu);
+ popupMenu = menu;
+ if (item.hasMedia()) {
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null);
+ } else {
+ // these are already available via button1 and button2
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null,
+ 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:
+ try {
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
+ return true;
+ }
+ }
+ }
+
+ 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(null, webviewData, "text/html", "utf-8", "about:blank");
+ }
+ updateAppearance();
+ }
+
+ private void updateAppearance() {
+ if (item == null) {
+ Log.d(TAG, "updateAppearance item is null");
+ return;
+ }
+ getActivity().supportInvalidateOptionsMenu();
+ txtvPodcast.setText(item.getFeed().getTitle());
+ txtvTitle.setText(item.getTitle());
+
+ if (item.getPubDate() != null) {
+ String pubDateStr = DateUtils.formatAbbrev(getActivity(), item.getPubDate());
+ txtvPublished.setText(pubDateStr);
+ }
+
+ Glide.with(getActivity())
+ .load(item.getImageLocation())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
+ .into(imgvCover);
+
+ progbarDownload.setVisibility(View.GONE);
+ if (item.hasMedia() && downloaderList != null) {
+ for (Downloader downloader : downloaderList) {
+ if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
+ progbarDownload.setVisibility(View.VISIBLE);
+ progbarDownload.setProgress(downloader.getDownloadRequest().getProgressPercent());
+ }
+ }
+ }
+
+ FeedMedia media = item.getMedia();
+ String butAction1Icon = null;
+ int butAction1Text = 0;
+ String butAction2Icon = null;
+ int butAction2Text = 0;
+ if (media == null) {
+ if (!item.isPlayed()) {
+ butAction1Icon = "{fa-check 24sp}";
+ butAction1Text = R.string.mark_read_label;
+ }
+ if (item.getLink() != null) {
+ butAction2Icon = "{md-web 24sp}";
+ butAction2Text = R.string.visit_website_label;
+ }
+ } else {
+ if(media.getDuration() > 0) {
+ txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
+ }
+ boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
+ if (!media.isDownloaded()) {
+ butAction2Icon = "{md-settings-input-antenna 24sp}";
+ butAction2Text = R.string.stream_label;
+ } else {
+ butAction2Icon = "{md-delete 24sp}";
+ butAction2Text = R.string.remove_label;
+ }
+ if (isDownloading) {
+ butAction1Icon = "{md-cancel 24sp}";
+ butAction1Text = R.string.cancel_label;
+ } else if (media.isDownloaded()) {
+ butAction1Icon = "{md-play-arrow 24sp}";
+ butAction1Text = R.string.play_label;
+ } else {
+ butAction1Icon = "{md-file-download 24sp}";
+ butAction1Text = R.string.download_label;
+ }
+ }
+ if(butAction1Icon != null && butAction1Text != 0) {
+ butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text));
+ Iconify.addIcons(butAction1);
+ butAction1.setVisibility(View.VISIBLE);
+ } else {
+ butAction1.setVisibility(View.INVISIBLE);
+ }
+ if(butAction2Icon != null && butAction2Text != 0) {
+ butAction2.setText(butAction2Icon +"\u0020\u0020" + getActivity().getString(butAction2Text));
+ Iconify.addIcons(butAction2);
+ butAction2.setVisibility(View.VISIBLE);
+ } else {
+ butAction2.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ WebView.HitTestResult r = webvDescription.getHitTestResult();
+ if (r != null
+ && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
+ Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra());
+ selectedURL = r.getExtra();
+ webvDescription.showContextMenu();
+ return true;
+ }
+ selectedURL = null;
+ return false;
+ }
+ };
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ boolean handled = selectedURL != null;
+ if (selectedURL != null) {
+ switch (item.getItemId()) {
+ case R.id.open_in_browser_item:
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ getActivity().startActivity(intent);
+ }
+ break;
+ case R.id.share_url_item:
+ ShareUtils.shareLink(getActivity(), selectedURL);
+ break;
+ case R.id.copy_url_item:
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
+ ClipData clipData = ClipData.newPlainText(selectedURL,
+ selectedURL);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
+ } else {
+ android.text.ClipboardManager cm = (android.text.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setText(selectedURL);
+ }
+ Toast t = Toast.makeText(getActivity(),
+ R.string.copied_url_msg, Toast.LENGTH_SHORT);
+ t.show();
+ break;
+ default:
+ handled = false;
+ break;
+
+ }
+ selectedURL = null;
+ }
+ return handled;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenu.ContextMenuInfo menuInfo) {
+ if (selectedURL != null) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
+ R.string.open_in_browser_label);
+ }
+ menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
+ R.string.copy_url_label);
+ menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
+ R.string.share_url_label);
+ menu.setHeaderTitle(selectedURL);
+ }
+ }
+
+ private void openPodcast() {
+ Fragment fragment = ItemlistFragment.newInstance(item.getFeedId());
+ ((MainActivity)getActivity()).loadChildFragment(fragment);
+ }
+
+ public void onEventMainThread(FeedItemEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ for(FeedItem item : event.items) {
+ if(feedItems[feedItemPos] == item.getId()) {
+ load();
+ return;
+ }
+ }
+ }
+
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if(item == null || item.getMedia() == null) {
+ return;
+ }
+ long mediaId = item.getMedia().getId();
+ if(ArrayUtils.contains(update.mediaIds, mediaId)) {
+ if (itemsLoaded && getActivity() != null) {
+ updateAppearance();
+ }
+ }
+ }
+
+
+ private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Override
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((arg & EVENTS) != 0) {
+ load();
+ }
+ }
+ };
+
+ private void load() {
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ progbarLoading.setVisibility(View.VISIBLE);
+ subscription = Observable.fromCallable(this::loadInBackground)
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ progbarLoading.setVisibility(View.GONE);
+ item = result;
+ itemsLoaded = true;
+ onFragmentLoaded();
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
+
+ private FeedItem loadInBackground() {
+ FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
+ if (feedItem != null) {
+ Timeline t = new Timeline(getActivity(), feedItem);
+ webviewData = t.processShownotes(false);
+ }
+ return feedItem;
+ }
+
+}