summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/danoeh/antennapod/fragment
diff options
context:
space:
mode:
authorH. Lehmann <ByteHamster@users.noreply.github.com>2019-08-11 14:57:44 +0200
committerGitHub <noreply@github.com>2019-08-11 14:57:44 +0200
commit1315c9e20b30e62b8022f831a0bcf5779b5bb2cb (patch)
tree65dd884ea83389d366d971ca5635e6e9f6c95dc4 /app/src/main/java/de/danoeh/antennapod/fragment
parentab864cd171a563177a8f565fc2744e88ba669516 (diff)
parentce64c412ac02041d877e4770381f29710a06ba17 (diff)
downloadAntennaPod-1315c9e20b30e62b8022f831a0bcf5779b5bb2cb.zip
Merge branch 'develop' into make_multidex_on_debug_build_only
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod/fragment')
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java99
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java299
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java125
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java173
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java135
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java115
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java77
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java39
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java177
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java77
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java190
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java58
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java108
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java202
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java47
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java113
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java154
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java119
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java36
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java103
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java195
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java184
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java144
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java147
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java175
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java92
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java244
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java165
30 files changed, 2695 insertions, 1164 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
index ee2373da8..35bcaa76e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -2,13 +2,20 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
+import android.provider.MediaStore;
import android.support.v4.app.Fragment;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
@@ -27,11 +34,28 @@ public class AddFeedFragment extends Fragment {
*/
private static final String ARG_FEED_URL = "feedurl";
+ private EditText combinedFeedSearchBox;
+ private MainActivity activity;
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.addfeed, container, false);
+ activity = (MainActivity) getActivity();
+ activity.getSupportActionBar().setTitle(R.string.add_feed_label);
+
+ setupAdvancedSearchButtons(root);
+ setupSeachBox(root);
+
+ View butOpmlImport = root.findViewById(R.id.btn_opml_import);
+ butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(),
+ OpmlImportFromPathActivity.class)));
+
+ return root;
+ }
+
+ private void setupSeachBox(View root) {
final EditText etxtFeedurl = root.findViewById(R.id.etxtFeedurl);
Bundle args = getArguments();
@@ -39,32 +63,69 @@ public class AddFeedFragment extends Fragment {
etxtFeedurl.setText(args.getString(ARG_FEED_URL));
}
- Button butSearchITunes = root.findViewById(R.id.butSearchItunes);
- Button butBrowserGpoddernet = root.findViewById(R.id.butBrowseGpoddernet);
- Button butSearchFyyd = root.findViewById(R.id.butSearchFyyd);
- Button butOpmlImport = root.findViewById(R.id.butOpmlImport);
- Button butConfirm = root.findViewById(R.id.butConfirm);
+ Button butConfirmAddUrl = root.findViewById(R.id.butConfirm);
+ butConfirmAddUrl.setOnClickListener(v -> {
+ addUrl(etxtFeedurl.getText().toString());
+ });
- final MainActivity activity = (MainActivity) getActivity();
- activity.getSupportActionBar().setTitle(R.string.add_feed_label);
+ combinedFeedSearchBox = root.findViewById(R.id.combinedFeedSearchBox);
+ combinedFeedSearchBox.setOnEditorActionListener((v, actionId, event) -> {
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+ performSearch();
+ return true;
+ }
+ return false;
+ });
+ }
- butSearchITunes.setOnClickListener(v -> activity.loadChildFragment(new ItunesSearchFragment()));
+ private void setupAdvancedSearchButtons(View root) {
+ View butAdvancedSearch = root.findViewById(R.id.advanced_search);
+ registerForContextMenu(butAdvancedSearch);
+ butAdvancedSearch.setOnClickListener(v -> butAdvancedSearch.showContextMenu());
+ }
- butBrowserGpoddernet.setOnClickListener(v -> activity.loadChildFragment(new GpodnetMainFragment()));
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ getActivity().getMenuInflater().inflate(R.menu.advanced_search, menu);
+ }
- butSearchFyyd.setOnClickListener(v -> activity.loadChildFragment(new FyydSearchFragment()));
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.search_fyyd:
+ activity.loadChildFragment(new FyydSearchFragment());
+ return true;
+ case R.id.search_gpodder:
+ activity.loadChildFragment(new GpodnetMainFragment());
+ return true;
+ case R.id.search_itunes:
+ activity.loadChildFragment(new ItunesSearchFragment());
+ return true;
+ }
+ return false;
+ }
- butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(),
- OpmlImportFromPathActivity.class)));
- butConfirm.setOnClickListener(v -> {
- Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
- intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, etxtFeedurl.getText().toString());
- intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label));
- startActivity(intent);
- });
+ private void addUrl(String url) {
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, url);
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label));
+ startActivity(intent);
+ }
- return root;
+ private void performSearch() {
+ String query = combinedFeedSearchBox.getText().toString();
+
+ if (query.startsWith("http")) {
+ addUrl(query);
+ return;
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putString(CombinedSearchFragment.ARGUMENT_QUERY, query);
+ CombinedSearchFragment fragment = new CombinedSearchFragment();
+ fragment.setArguments(bundle);
+ activity.loadChildFragment(fragment);
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index ef522d3b3..62d798cf6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -5,6 +5,7 @@ import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
+import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
@@ -24,12 +25,16 @@ import android.widget.Toast;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
-import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
@@ -39,6 +44,7 @@ import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -49,7 +55,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.greenrobot.event.EventBus;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -74,12 +80,12 @@ public class AllEpisodesFragment extends Fragment {
RecyclerView recyclerView;
AllEpisodesRecycleAdapter listAdapter;
private ProgressBar progLoading;
+ EmptyViewHandler emptyView;
- List<FeedItem> episodes;
- private List<Downloader> downloaderList;
-
- private boolean itemsLoaded = false;
- private boolean viewsCreated = false;
+ @NonNull
+ List<FeedItem> episodes = new ArrayList<>();
+ @NonNull
+ private List<Downloader> downloaderList = new ArrayList<>();
private boolean isUpdatingFeeds;
boolean isMenuInvalidationAllowed = false;
@@ -87,36 +93,32 @@ public class AllEpisodesFragment extends Fragment {
Disposable disposable;
private LinearLayoutManager layoutManager;
- boolean showOnlyNewEpisodes() { return false; }
- String getPrefName() { return DEFAULT_PREF_NAME; }
+ boolean showOnlyNewEpisodes() {
+ return false;
+ }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setHasOptionsMenu(true);
+ String getPrefName() {
+ return DEFAULT_PREF_NAME;
}
@Override
public void onStart() {
super.onStart();
+ setHasOptionsMenu(true);
EventDistributor.getInstance().register(contentUpdate);
- if (viewsCreated && itemsLoaded) {
- onFragmentLoaded();
- }
+ EventBus.getDefault().register(this);
+ loadItems();
}
@Override
public void onResume() {
super.onResume();
- EventBus.getDefault().registerSticky(this);
- loadItems();
registerForContextMenu(recyclerView);
}
@Override
public void onPause() {
super.onPause();
- EventBus.getDefault().unregister(this);
saveScrollPosition();
unregisterForContextMenu(recyclerView);
}
@@ -124,23 +126,18 @@ public class AllEpisodesFragment extends Fragment {
@Override
public void onStop() {
super.onStop();
+ EventBus.getDefault().unregister(this);
EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
}
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- resetViewState();
- }
-
private void saveScrollPosition() {
int firstItem = layoutManager.findFirstVisibleItemPosition();
View firstItemView = layoutManager.findViewByPosition(firstItem);
float topOffset;
- if(firstItemView == null) {
+ if (firstItemView == null) {
topOffset = 0;
} else {
topOffset = firstItemView.getTop();
@@ -167,43 +164,35 @@ public class AllEpisodesFragment extends Fragment {
}
}
- void resetViewState() {
- viewsCreated = false;
- listAdapter = null;
- }
-
-
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
+ if (!isAdded()) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- inflater.inflate(R.menu.episodes, menu);
-
- MenuItem searchItem = menu.findItem(R.id.action_search);
- final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
- return true;
- }
+ inflater.inflate(R.menu.episodes, menu);
+
+ MenuItem searchItem = menu.findItem(R.id.action_search);
+ final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
+ MenuItemUtils.adjustTextColor(getActivity(), sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) requireActivity()).loadChildFragment(SearchFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
- isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
- }
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
@Override
@@ -211,11 +200,11 @@ public class AllEpisodesFragment extends Fragment {
super.onPrepareOptionsMenu(menu);
MenuItem markAllRead = menu.findItem(R.id.mark_all_read_item);
if (markAllRead != null) {
- markAllRead.setVisible(!showOnlyNewEpisodes() && episodes != null && !episodes.isEmpty());
+ markAllRead.setVisible(!showOnlyNewEpisodes() && !episodes.isEmpty());
}
- MenuItem markAllSeen = menu.findItem(R.id.mark_all_seen_item);
- if(markAllSeen != null) {
- markAllSeen.setVisible(showOnlyNewEpisodes() && episodes != null && !episodes.isEmpty());
+ MenuItem removeAllNewFlags = menu.findItem(R.id.remove_all_new_flags_item);
+ if (removeAllNewFlags != null) {
+ removeAllNewFlags.setVisible(showOnlyNewEpisodes() && !episodes.isEmpty());
}
}
@@ -243,19 +232,19 @@ public class AllEpisodesFragment extends Fragment {
};
markAllReadConfirmationDialog.createNewDialog().show();
return true;
- case R.id.mark_all_seen_item:
- ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(getActivity(),
- R.string.mark_all_seen_label,
- R.string.mark_all_seen_confirmation_msg) {
+ case R.id.remove_all_new_flags_item:
+ ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getActivity(),
+ R.string.remove_all_new_flags_label,
+ R.string.remove_all_new_flags_confirmation_msg) {
@Override
public void onConfirmButtonPressed(DialogInterface dialog) {
dialog.dismiss();
- DBWriter.markNewItemsSeen();
- Toast.makeText(getActivity(), R.string.mark_all_seen_msg, Toast.LENGTH_SHORT).show();
+ DBWriter.removeAllNewFlags();
+ Toast.makeText(getActivity(), R.string.removed_all_new_flags_msg, Toast.LENGTH_SHORT).show();
}
};
- markAllSeenConfirmationDialog.createNewDialog().show();
+ removeAllNewFlagsConfirmationDialog.createNewDialog().show();
return true;
default:
return false;
@@ -269,98 +258,102 @@ public class AllEpisodesFragment extends Fragment {
@Override
public boolean onContextItemSelected(MenuItem item) {
Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]");
- if(!isVisible()) {
+ if (!getUserVisibleHint()) {
return false;
}
- if(item.getItemId() == R.id.share_item) {
+ if (!isVisible()) {
+ return false;
+ }
+ if (item.getItemId() == R.id.share_item) {
return true; // avoids that the position is reset when we need it in the submenu
}
- FeedItem selectedItem = listAdapter.getSelectedItem();
- if (selectedItem == null) {
- Log.i(TAG, "Selected item was null, ignoring selection");
+ if (listAdapter.getSelectedItem() == null) {
+ Log.i(TAG, "Selected item or listAdapter was null, ignoring selection");
return super.onContextItemSelected(item);
}
+ FeedItem selectedItem = listAdapter.getSelectedItem();
- // Mark as seen contains UI logic specific to All/New/FavoriteSegments,
+ // Remove new flag contains UI logic specific to All/New/FavoriteSegments,
// e.g., Undo with Snackbar,
// and is handled by this class rather than the generic FeedItemMenuHandler
- // Undo is useful for Mark as seen, given there is no UI to undo it otherwise,
+ // Undo is useful for Remove new flag, given there is no UI to undo it otherwise,
// i.e., there is context menu item for Mark as new
- if (R.id.mark_as_seen_item == item.getItemId()) {
- markItemAsSeenWithUndo(selectedItem);
+ if (R.id.remove_new_flag_item == item.getItemId()) {
+ removeNewFlagWithUndo(selectedItem);
return true;
}
return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
}
+ @NonNull
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return onCreateViewHelper(inflater, container, savedInstanceState,
- R.layout.all_episodes_fragment);
- }
-
- View onCreateViewHelper(LayoutInflater inflater,
- ViewGroup container,
- Bundle savedInstanceState,
- int fragmentResource) {
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
+ View root = inflater.inflate(R.layout.all_episodes_fragment, container, false);
- View root = inflater.inflate(fragmentResource, container, false);
-
+ layoutManager = new LinearLayoutManager(getActivity());
recyclerView = root.findViewById(android.R.id.list);
+ recyclerView.setLayoutManager(layoutManager);
+ recyclerView.setHasFixedSize(true);
+ recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.setVisibility(View.GONE);
+
RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
- layoutManager = new LinearLayoutManager(getActivity());
- recyclerView.setLayoutManager(layoutManager);
- recyclerView.setHasFixedSize(true);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
progLoading = root.findViewById(R.id.progLoading);
+ progLoading.setVisibility(View.VISIBLE);
- if (!itemsLoaded) {
- progLoading.setVisibility(View.VISIBLE);
- }
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.attachToRecyclerView(recyclerView);
+ emptyView.setIcon(R.attr.feed);
+ emptyView.setTitle(R.string.no_all_episodes_head_label);
+ emptyView.setMessage(R.string.no_all_episodes_label);
- viewsCreated = true;
-
- if (itemsLoaded) {
- onFragmentLoaded();
- }
+ createRecycleAdapter(recyclerView, emptyView);
+ emptyView.hide();
return root;
}
- private void onFragmentLoaded() {
- if (listAdapter == null) {
- MainActivity mainActivity = (MainActivity) getActivity();
- listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess,
- new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes());
- listAdapter.setHasStableIds(true);
- recyclerView.setAdapter(listAdapter);
- }
+ private void onFragmentLoaded(List<FeedItem> episodes) {
+ this.episodes = episodes;
listAdapter.notifyDataSetChanged();
+
+ if (episodes.size() == 0) {
+ createRecycleAdapter(recyclerView, emptyView);
+ }
+
restoreScrollPosition();
- getActivity().supportInvalidateOptionsMenu();
- updateShowOnlyEpisodesListViewState();
+ requireActivity().invalidateOptionsMenu();
+ }
+
+ /**
+ * Currently, we need to recreate the list adapter in order to be able to undo last item via the
+ * snackbar. See #3084 for details.
+ */
+ private void createRecycleAdapter(RecyclerView recyclerView, EmptyViewHandler emptyViewHandler) {
+ MainActivity mainActivity = (MainActivity) getActivity();
+ listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes());
+ listAdapter.setHasStableIds(true);
+ recyclerView.setAdapter(listAdapter);
+ emptyViewHandler.updateAdapter(listAdapter);
}
private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
@Override
public int getCount() {
- if (episodes != null) {
- return episodes.size();
- }
- return 0;
+ return episodes.size();
}
@Override
public FeedItem getItem(int position) {
- if (episodes != null && 0 <= position && position < episodes.size()) {
+ if (0 <= position && position < episodes.size()) {
return episodes.get(position);
}
return null;
@@ -368,11 +361,8 @@ public class AllEpisodesFragment extends Fragment {
@Override
public LongList getItemsIds() {
- if(episodes == null) {
- return new LongList(0);
- }
LongList ids = new LongList(episodes.size());
- for(FeedItem episode : episodes) {
+ for (FeedItem episode : episodes) {
ids.add(episode.getId());
}
return ids;
@@ -380,12 +370,11 @@ public class AllEpisodesFragment extends Fragment {
@Override
public int getItemDownloadProgressPercent(FeedItem item) {
- if (downloaderList != null) {
- for (Downloader downloader : downloaderList) {
- if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
- && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
- return downloader.getDownloadRequest().getProgressPercent();
- }
+ for (Downloader downloader : downloaderList) {
+ DownloadRequest downloadRequest = downloader.getDownloadRequest();
+ if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && downloadRequest.getFeedfileId() == item.getMedia().getId()) {
+ return downloadRequest.getProgressPercent();
}
}
return 0;
@@ -399,11 +388,8 @@ public class AllEpisodesFragment extends Fragment {
@Override
public LongList getQueueIds() {
LongList queueIds = new LongList();
- if(episodes == null) {
- return queueIds;
- }
- for(FeedItem item : episodes) {
- if(item.isTagged(FeedItem.TAG_QUEUE)) {
+ for (FeedItem item : episodes) {
+ if (item.isTagged(FeedItem.TAG_QUEUE)) {
queueIds.add(item.getId());
}
}
@@ -412,34 +398,39 @@ public class AllEpisodesFragment extends Fragment {
};
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if(episodes == null || listAdapter == null) {
- return;
- }
- for(int i=0, size = event.items.size(); i < size; i++) {
- FeedItem item = event.items.get(i);
+ for (FeedItem item : event.items) {
int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
- if(pos >= 0) {
+ if (pos >= 0) {
episodes.remove(pos);
- episodes.add(pos, item);
- listAdapter.notifyItemChanged(pos);
+ if (shouldUpdatedItemRemainInList(item)) {
+ episodes.add(pos, item);
+ listAdapter.notifyItemChanged(pos);
+ } else {
+ listAdapter.notifyItemRemoved(pos);
+ }
}
}
}
+ protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
+ return true;
+ }
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
- getActivity().supportInvalidateOptionsMenu();
+ requireActivity().invalidateOptionsMenu();
}
- if(listAdapter != null && update.mediaIds.length > 0) {
- for(long mediaId : update.mediaIds) {
+ if (update.mediaIds.length > 0) {
+ for (long mediaId : update.mediaIds) {
int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
- if(pos >= 0) {
+ if (pos >= 0) {
listAdapter.notifyItemChanged(pos);
}
}
@@ -452,49 +443,36 @@ public class AllEpisodesFragment extends Fragment {
if ((arg & EVENTS) != 0) {
loadItems();
if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- getActivity().supportInvalidateOptionsMenu();
+ requireActivity().invalidateOptionsMenu();
}
}
}
};
- private void updateShowOnlyEpisodesListViewState() {
- }
-
void loadItems() {
if (disposable != null) {
disposable.dispose();
}
- if (viewsCreated && !itemsLoaded) {
- recyclerView.setVisibility(View.GONE);
- progLoading.setVisibility(View.VISIBLE);
- }
disposable = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
- recyclerView.setVisibility(View.VISIBLE);
progLoading.setVisibility(View.GONE);
- if (data != null) {
- episodes = data;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
- }
+ onFragmentLoaded(data);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @NonNull
List<FeedItem> loadData() {
return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT);
}
- void markItemAsSeenWithUndo(FeedItem item) {
+ void removeNewFlagWithUndo(FeedItem item) {
if (item == null) {
return;
}
- Log.d(TAG, "markItemAsSeenWithUndo(" + item.getId() + ")");
+ Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")");
if (disposable != null) {
disposable.dispose();
}
@@ -503,14 +481,14 @@ public class AllEpisodesFragment extends Fragment {
DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
final Handler h = new Handler(getActivity().getMainLooper());
- final Runnable r = () -> {
+ final Runnable r = () -> {
FeedMedia media = item.getMedia();
if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
}
};
- Snackbar snackbar = Snackbar.make(getView(), getString(R.string.marked_as_seen_label),
+ Snackbar snackbar = Snackbar.make(getView(), getString(R.string.removed_new_flag_label),
Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> {
DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
@@ -518,7 +496,6 @@ public class AllEpisodesFragment extends Fragment {
h.removeCallbacks(r);
});
snackbar.show();
- h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f));
+ h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f));
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
index 4d34d076d..4bebfe4c9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -6,28 +6,28 @@ import android.util.Log;
import android.view.View;
import android.widget.ListView;
+import java.util.List;
+import java.util.ListIterator;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment;
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.view.EmptyViewHandler;
+import io.reactivex.Maybe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
-public class ChaptersFragment extends ListFragment implements MediaplayerInfoContentFragment {
-
+public class ChaptersFragment extends ListFragment {
private static final String TAG = "ChaptersFragment";
-
- private Playable media;
- private PlaybackController controller;
-
private ChaptersListAdapter adapter;
+ private PlaybackController controller;
+ private Disposable disposable;
+ private EmptyViewHandler emptyView;
- public static ChaptersFragment newInstance(Playable media) {
- ChaptersFragment f = new ChaptersFragment();
- f.media = media;
- return f;
- }
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -38,11 +38,13 @@ public class ChaptersFragment extends ListFragment implements MediaplayerInfoCon
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.attachToListView(lv);
+ emptyView.setIcon(R.attr.ic_bookmark);
+ emptyView.setTitle(R.string.no_chapters_head_label);
+ emptyView.setMessage(R.string.no_chapters_label);
+
adapter = new ChaptersListAdapter(getActivity(), 0, pos -> {
- if(controller == null) {
- Log.d(TAG, "controller is null");
- return;
- }
Chapter chapter = (Chapter) getListAdapter().getItem(pos);
controller.seekToChapter(chapter);
});
@@ -50,42 +52,83 @@ public class ChaptersFragment extends ListFragment implements MediaplayerInfoCon
}
@Override
- public void onResume() {
- super.onResume();
- adapter.setMedia(media);
- adapter.notifyDataSetChanged();
- if(media == null || media.getChapters() == null) {
- setEmptyText(getString(R.string.no_chapters_label));
- } else {
- setEmptyText(null);
+ public void onStart() {
+ super.onStart();
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public boolean loadMediaInfo() {
+ ChaptersFragment.this.loadMediaInfo();
+ return true;
+ }
+
+ @Override
+ public void onPositionObserverUpdate() {
+ adapter.notifyDataSetChanged();
+ }
+ };
+ controller.init();
+
+ loadMediaInfo();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ if (disposable != null) {
+ disposable.dispose();
}
}
- public void onDestroy() {
- super.onDestroy();
- adapter = null;
+ @Override
+ public void onStop() {
+ super.onStop();
+ controller.release();
controller = null;
}
- @Override
- public void onMediaChanged(Playable media) {
- if(this.media == media) {
- return;
+ private void scrollTo(int position) {
+ getListView().setSelection(position);
+ }
+
+ private int getCurrentChapter(Playable media) {
+ int currentPosition = controller.getPosition();
+
+ List<Chapter> chapters = media.getChapters();
+ for (final ListIterator<Chapter> it = chapters.listIterator(); it.hasNext(); ) {
+ Chapter chapter = it.next();
+ if (chapter.getStart() > currentPosition) {
+ return it.previousIndex() - 1;
+ }
}
- this.media = media;
+ return chapters.size() - 1;
+ }
+
+ private void loadMediaInfo() {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Maybe.create(emitter -> {
+ Playable media = controller.getMedia();
+ if (media != null) {
+ emitter.onSuccess(media);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> onMediaChanged((Playable) media),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+
+ private void onMediaChanged(Playable media) {
if (adapter != null) {
adapter.setMedia(media);
adapter.notifyDataSetChanged();
- if(media == null || media.getChapters() == null || media.getChapters().size() == 0) {
- setEmptyText(getString(R.string.no_items_label));
- } else {
- setEmptyText(null);
+ if (media != null && media.getChapters() != null && media.getChapters().size() != 0) {
+ scrollTo(getCurrentChapter(media));
}
}
}
-
- public void setController(PlaybackController controller) {
- this.controller = controller;
- }
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
new file mode 100644
index 000000000..1d9020f0d
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
@@ -0,0 +1,173 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.widget.SearchView;
+import android.util.Log;
+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 android.widget.Button;
+import android.widget.GridView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
+import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
+import de.danoeh.antennapod.discovery.CombinedSearcher;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
+import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import io.reactivex.disposables.Disposable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CombinedSearchFragment extends Fragment {
+
+ private static final String TAG = "CombinedSearchFragment";
+ public static final String ARGUMENT_QUERY = "query";
+
+ /**
+ * Adapter responsible with the search results
+ */
+ private ItunesAdapter adapter;
+ private GridView gridView;
+ private ProgressBar progressBar;
+ private TextView txtvError;
+ private Button butRetry;
+ private TextView txtvEmpty;
+
+ /**
+ * List of podcasts retreived from the search
+ */
+ private List<PodcastSearchResult> searchResults = new ArrayList<>();
+ private Disposable disposable;
+
+ /**
+ * Constructor
+ */
+ public CombinedSearchFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
+ gridView = root.findViewById(R.id.gridView);
+ adapter = new ItunesAdapter(getActivity(), new ArrayList<>());
+ gridView.setAdapter(adapter);
+
+ //Show information about the podcast when the list item is clicked
+ gridView.setOnItemClickListener((parent, view1, position, id) -> {
+ PodcastSearchResult podcast = searchResults.get(position);
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl);
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title);
+ startActivity(intent);
+ });
+ progressBar = root.findViewById(R.id.progressBar);
+ txtvError = root.findViewById(R.id.txtvError);
+ butRetry = root.findViewById(R.id.butRetry);
+ txtvEmpty = root.findViewById(android.R.id.empty);
+
+ return root;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ adapter = null;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.itunes_search, menu);
+ MenuItem searchItem = menu.findItem(R.id.action_search);
+ final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
+ MenuItemUtils.adjustTextColor(getActivity(), sv);
+ sv.setQueryHint(getString(R.string.search_label));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ search(s);
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ getActivity().getSupportFragmentManager().popBackStack();
+ return true;
+ }
+ });
+ MenuItemCompat.expandActionView(searchItem);
+
+ if (getArguments() != null && getArguments().getString(ARGUMENT_QUERY, null) != null) {
+ sv.setQuery(getArguments().getString(ARGUMENT_QUERY, null), true);
+ }
+ }
+
+ private void search(String query) {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+
+ showOnlyProgressBar();
+
+ CombinedSearcher searcher = new CombinedSearcher(getContext());
+ disposable = searcher.search(query).subscribe(result -> {
+ searchResults = result;
+ progressBar.setVisibility(View.GONE);
+
+ adapter.clear();
+ adapter.addAll(searchResults);
+ adapter.notifyDataSetInvalidated();
+ gridView.setVisibility(!searchResults.isEmpty() ? View.VISIBLE : View.GONE);
+ txtvEmpty.setVisibility(searchResults.isEmpty() ? View.VISIBLE : View.GONE);
+
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(error.toString());
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setOnClickListener(v -> search(query));
+ butRetry.setVisibility(View.VISIBLE);
+ });
+ }
+
+ private void showOnlyProgressBar() {
+ gridView.setVisibility(View.GONE);
+ txtvError.setVisibility(View.GONE);
+ butRetry.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.GONE);
+ progressBar.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index 4bba9b255..705151062 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.Menu;
@@ -10,6 +10,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -21,11 +22,15 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_ADD_TO_QUEUE;
+import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
+
/**
* Displays all running downloads and provides a button to delete them
*/
@@ -37,24 +42,27 @@ public class CompletedDownloadsFragment extends ListFragment {
EventDistributor.DOWNLOADLOG_UPDATE |
EventDistributor.UNREAD_ITEMS_UPDATE;
- private List<FeedItem> items;
+ private List<FeedItem> items = new ArrayList<>();
private DownloadedEpisodesListAdapter listAdapter;
-
- private boolean viewCreated = false;
-
private Disposable disposable;
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
setHasOptionsMenu(true);
- loadItems();
+ addVerticalPadding();
+ addEmptyView();
+
+ listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
+ setListAdapter(listAdapter);
+ setListShown(false);
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
+ loadItems();
}
@Override
@@ -67,99 +75,54 @@ public class CompletedDownloadsFragment extends ListFragment {
}
@Override
- public void onDetach() {
- super.onDetach();
- if (disposable != null) {
- disposable.dispose();
- }
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- listAdapter = null;
- viewCreated = false;
- if (disposable != null) {
- disposable.dispose();
- }
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- if (viewCreated && items != null) {
- onFragmentLoaded();
- }
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- // add padding
- final ListView lv = getListView();
- lv.setClipToPadding(false);
- final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
- lv.setPadding(0, vertPadding, 0, vertPadding);
-
- viewCreated = true;
- if (items != null && getActivity() != null) {
- onFragmentLoaded();
- }
- }
-
- @Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(items);
- ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
- }
-
- private void onFragmentLoaded() {
- if (listAdapter == null) {
- listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
- setListAdapter(listAdapter);
- }
- setListShown(true);
- listAdapter.notifyDataSetChanged();
- getActivity().supportInvalidateOptionsMenu();
+ ((MainActivity) requireActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
- return;
- }
super.onCreateOptionsMenu(menu, inflater);
- if(items != null) {
- inflater.inflate(R.menu.downloads_completed, menu);
- menu.findItem(R.id.episode_actions).setVisible(items.size() > 0);
- }
+ inflater.inflate(R.menu.downloads_completed, menu);
+ menu.findItem(R.id.episode_actions).setVisible(items.size() > 0);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.episode_actions:
- EpisodesApplyActionFragment fragment = EpisodesApplyActionFragment
- .newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE | EpisodesApplyActionFragment.ACTION_QUEUE);
- ((MainActivity) getActivity()).loadChildFragment(fragment);
- return true;
- default:
- return false;
+ if (item.getItemId() == R.id.episode_actions) {
+ ((MainActivity) requireActivity())
+ .loadChildFragment(EpisodesApplyActionFragment.newInstance(items, ACTION_DELETE | ACTION_ADD_TO_QUEUE));
+ return true;
}
+ return false;
+ }
+
+ private void addEmptyView() {
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setIcon(R.attr.av_download);
+ emptyView.setTitle(R.string.no_comp_downloads_head_label);
+ emptyView.setMessage(R.string.no_comp_downloads_label);
+ emptyView.attachToListView(getListView());
+ }
+
+ private void addVerticalPadding() {
+ final ListView lv = getListView();
+ lv.setClipToPadding(false);
+ final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
+ lv.setPadding(0, vertPadding, 0, vertPadding);
}
private final DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() {
@Override
public int getCount() {
- return (items != null) ? items.size() : 0;
+ return items.size();
}
@Override
public FeedItem getItem(int position) {
- if (items != null && 0 <= position && position < items.size()) {
+ if (0 <= position && position < items.size()) {
return items.get(position);
} else {
return null;
@@ -168,7 +131,7 @@ public class CompletedDownloadsFragment extends ListFragment {
@Override
public void onFeedItemSecondaryAction(FeedItem item) {
- DBWriter.deleteFeedMediaOfItem(getActivity(), item.getMedia().getId());
+ DBWriter.deleteFeedMediaOfItem(requireActivity(), item.getMedia().getId());
}
};
@@ -185,18 +148,18 @@ public class CompletedDownloadsFragment extends ListFragment {
if (disposable != null) {
disposable.dispose();
}
- if (items == null && viewCreated) {
- setListShown(false);
- }
disposable = Observable.fromCallable(DBReader::getDownloadedItems)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
items = result;
- if (viewCreated && getActivity() != null) {
- onFragmentLoaded();
- }
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ onItemsLoaded();
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ private void onItemsLoaded() {
+ setListShown(true);
+ listAdapter.notifyDataSetChanged();
+ requireActivity().invalidateOptionsMenu();
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index 5a061c7e6..db9dd9530 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -13,74 +14,68 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import io.reactivex.Maybe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays the cover and the title of a FeedItem.
*/
-public class CoverFragment extends Fragment implements MediaplayerInfoContentFragment {
+public class CoverFragment extends Fragment {
private static final String TAG = "CoverFragment";
- private Playable media;
-
private View root;
private TextView txtvPodcastTitle;
private TextView txtvEpisodeTitle;
private ImageView imgvCover;
-
- public static CoverFragment newInstance(Playable item) {
- CoverFragment f = new CoverFragment();
- f.media = item;
- return f;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (media == null) {
- Log.e(TAG, TAG + " was called without media");
- }
- }
+ private PlaybackController controller;
+ private Disposable disposable;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
+ setRetainInstance(true);
root = inflater.inflate(R.layout.cover_fragment, container, false);
txtvPodcastTitle = root.findViewById(R.id.txtvPodcastTitle);
txtvEpisodeTitle = root.findViewById(R.id.txtvEpisodeTitle);
imgvCover = root.findViewById(R.id.imgvCover);
+ imgvCover.setOnClickListener(v -> onPlayPause());
return root;
}
private void loadMediaInfo() {
- if (media != null) {
- txtvPodcastTitle.setText(media.getFeedTitle());
- txtvEpisodeTitle.setText(media.getEpisodeTitle());
- Glide.with(this)
- .load(media.getImageLocation())
- .apply(new RequestOptions()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .dontAnimate()
- .fitCenter())
- .into(imgvCover);
- } else {
- Log.w(TAG, "loadMediaInfo was called while media was null");
+ if (disposable != null) {
+ disposable.dispose();
}
+ disposable = Maybe.create(emitter -> {
+ Playable media = controller.getMedia();
+ if (media != null) {
+ emitter.onSuccess(media);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> displayMediaInfo((Playable) media),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- @Override
- public void onStart() {
- Log.d(TAG, "On Start");
- super.onStart();
- if (media != null) {
- Log.d(TAG, "Loading media info");
- loadMediaInfo();
- } else {
- Log.w(TAG, "Unable to load media info: media was null");
- }
+ private void displayMediaInfo(@NonNull Playable media) {
+ txtvPodcastTitle.setText(media.getFeedTitle());
+ txtvEpisodeTitle.setText(media.getEpisodeTitle());
+ Glide.with(this)
+ .load(media.getImageLocation())
+ .apply(new RequestOptions()
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .dontAnimate()
+ .fitCenter())
+ .into(imgvCover);
}
@Override
@@ -91,14 +86,40 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
}
@Override
- public void onMediaChanged(Playable media) {
- if(this.media == media) {
- return;
- }
- this.media = media;
- if (isAdded()) {
- loadMediaInfo();
+ public void onStart() {
+ super.onStart();
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public boolean loadMediaInfo() {
+ CoverFragment.this.loadMediaInfo();
+ return true;
+ }
+
+ };
+ controller.init();
+ loadMediaInfo();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ controller.release();
+ controller = null;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ if (disposable != null) {
+ disposable.dispose();
}
}
+ void onPlayPause() {
+ if (controller == null) {
+ return;
+ }
+ controller.playPause();
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index 5ab6bac63..26b115b4b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -14,15 +14,18 @@ import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -35,18 +38,13 @@ public class DownloadLogFragment extends ListFragment {
private static final String TAG = "DownloadLogFragment";
- private List<DownloadStatus> downloadLog;
+ private List<DownloadStatus> downloadLog = new ArrayList<>();
private DownloadLogAdapter adapter;
-
- private boolean viewsCreated = false;
- private boolean itemsLoaded = false;
-
private Disposable disposable;
@Override
public void onStart() {
super.onStart();
- setHasOptionsMenu(true);
EventDistributor.getInstance().register(contentUpdate);
loadItems();
}
@@ -55,7 +53,7 @@ public class DownloadLogFragment extends ListFragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
}
@@ -63,6 +61,7 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
+ setHasOptionsMenu(true);
// add padding
final ListView lv = getListView();
@@ -70,17 +69,17 @@ public class DownloadLogFragment extends ListFragment {
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
- viewsCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
- }
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setIcon(R.attr.av_download);
+ emptyView.setTitle(R.string.no_log_downloads_head_label);
+ emptyView.setMessage(R.string.no_log_downloads_label);
+ emptyView.attachToListView(getListView());
+
+ adapter = new DownloadLogAdapter(getActivity(), itemAccess);
+ setListAdapter(adapter);
}
private void onFragmentLoaded() {
- if (adapter == null) {
- adapter = new DownloadLogAdapter(getActivity(), itemAccess);
- setListAdapter(adapter);
- }
setListShown(true);
adapter.notifyDataSetChanged();
getActivity().supportInvalidateOptionsMenu();
@@ -93,10 +92,18 @@ public class DownloadLogFragment extends ListFragment {
DownloadStatus status = adapter.getItem(position);
String url = "unknown";
String message = getString(R.string.download_successful);
- FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
- if (media != null) {
- url = media.getDownload_url();
+ if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
+ if (media != null) {
+ url = media.getDownload_url();
+ }
+ } else if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
+ Feed feed = DBReader.getFeed(status.getFeedfileId());
+ if (feed != null) {
+ url = feed.getDownload_url();
+ }
}
+
if (!status.isSuccessful()) {
message = status.getReasonDetailed();
}
@@ -113,12 +120,12 @@ public class DownloadLogFragment extends ListFragment {
@Override
public int getCount() {
- return (downloadLog != null) ? downloadLog.size() : 0;
+ return downloadLog.size();
}
@Override
public DownloadStatus getItem(int position) {
- if (downloadLog != null && 0 <= position && position < downloadLog.size()) {
+ if (0 <= position && position < downloadLog.size()) {
return downloadLog.get(position);
} else {
return null;
@@ -138,27 +145,23 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
+ if (!isAdded()) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
- MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
- clearHistory.setIcon(drawables.getDrawable(0));
- drawables.recycle();
- }
+ MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
+ MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
+ clearHistory.setIcon(drawables.getDrawable(0));
+ drawables.recycle();
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- if (itemsLoaded) {
- MenuItem menuItem = menu.findItem(R.id.clear_history_item);
- if(menuItem != null) {
- menuItem.setVisible(downloadLog != null && !downloadLog.isEmpty());
- }
+ MenuItem menuItem = menu.findItem(R.id.clear_history_item);
+ if (menuItem != null) {
+ menuItem.setVisible(!downloadLog.isEmpty());
}
}
@@ -178,7 +181,7 @@ public class DownloadLogFragment extends ListFragment {
}
private void loadItems() {
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
disposable = Observable.fromCallable(DBReader::getDownloadLog)
@@ -187,12 +190,8 @@ public class DownloadLogFragment extends ListFragment {
.subscribe(result -> {
if (result != null) {
downloadLog = result;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
+ onFragmentLoaded();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index de2f04590..348c73b92 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -18,6 +18,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
@@ -90,10 +91,6 @@ public class ExternalPlayerFragment extends Fragment {
loadMediaInfo();
}
- public void connectToPlaybackService() {
- controller.init();
- }
-
private PlaybackController setupPlaybackController() {
return new PlaybackController(getActivity(), true) {
@@ -109,12 +106,12 @@ public class ExternalPlayerFragment extends Fragment {
@Override
public boolean loadMediaInfo() {
- ExternalPlayerFragment fragment = ExternalPlayerFragment.this;
- if (fragment != null) {
- return fragment.loadMediaInfo();
- } else {
- return false;
- }
+ return ExternalPlayerFragment.this.loadMediaInfo();
+ }
+
+ @Override
+ public void setupGUI() {
+ ExternalPlayerFragment.this.loadMediaInfo();
}
@Override
@@ -133,17 +130,29 @@ public class ExternalPlayerFragment extends Fragment {
public void onResume() {
super.onResume();
onPositionObserverUpdate();
+ }
+ @Override
+ public void onStart() {
+ super.onStart();
+ controller = setupPlaybackController();
controller.init();
+ loadMediaInfo();
}
@Override
- public void onDestroy() {
- super.onDestroy();
- Log.d(TAG, "Fragment is about to be destroyed");
+ public void onStop() {
+ super.onStop();
if (controller != null) {
controller.release();
+ controller = null;
}
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.d(TAG, "Fragment is about to be destroyed");
if (disposable != null) {
disposable.dispose();
}
@@ -196,7 +205,8 @@ public class ExternalPlayerFragment extends Fragment {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(media -> updateUi((Playable) media),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
+ error -> Log.e(TAG, Log.getStackTraceString(error)),
+ () -> fragmentLayout.setVisibility(View.GONE));
return true;
}
@@ -217,7 +227,7 @@ public class ExternalPlayerFragment extends Fragment {
.into(imgvCover);
fragmentLayout.setVisibility(View.VISIBLE);
- if (controller.isPlayingVideoLocally()) {
+ if (controller != null && controller.isPlayingVideoLocally()) {
butPlay.setVisibility(View.GONE);
} else {
butPlay.setVisibility(View.VISIBLE);
@@ -232,7 +242,9 @@ public class ExternalPlayerFragment extends Fragment {
}
private void onPositionObserverUpdate() {
- if (controller.getPosition() == PlaybackService.INVALID_TIME
+ if (controller == null) {
+ return;
+ } else if (controller.getPosition() == PlaybackService.INVALID_TIME
|| controller.getDuration() == PlaybackService.INVALID_TIME) {
return;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index 70f82c2ec..bb029b731 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
@@ -9,6 +10,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import org.greenrobot.eventbus.Subscribe;
+
import java.util.List;
import de.danoeh.antennapod.R;
@@ -18,38 +21,38 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
-
/**
* Like 'EpisodesFragment' except that it only shows favorite episodes and
* supports swiping to remove from favorites.
*/
-
public class FavoriteEpisodesFragment extends AllEpisodesFragment {
private static final String TAG = "FavoriteEpisodesFrag";
-
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";
@Override
- protected boolean showOnlyNewEpisodes() { return true; }
+ protected boolean showOnlyNewEpisodes() {
+ return true;
+ }
@Override
- protected String getPrefName() { return PREF_NAME; }
+ protected String getPrefName() {
+ return PREF_NAME;
+ }
+ @Subscribe
public void onEvent(FavoritesEvent event) {
- Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ Log.d(TAG, String.format("onEvent() called with: event = [%s]", event));
loadItems();
}
+ @NonNull
@Override
- protected void resetViewState() {
- super.resetViewState();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
- R.layout.all_episodes_fragment);
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, container, savedInstanceState);
+ emptyView.setIcon(R.attr.ic_unfav);
+ emptyView.setTitle(R.string.no_fav_episodes_head_label);
+ emptyView.setMessage(R.string.no_fav_episodes_label);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
@@ -59,8 +62,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
- AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder;
- Log.d(TAG, "remove(" + holder.getItemId() + ")");
+ AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder;
+ Log.d(TAG, String.format("remove(%s)", holder.getItemId()));
if (disposable != null) {
disposable.dispose();
@@ -69,8 +72,7 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
if (item != null) {
DBWriter.removeFavoriteItem(item);
- Snackbar snackbar = Snackbar.make(root, getString(R.string.removed_item),
- Snackbar.LENGTH_LONG);
+ Snackbar snackbar = Snackbar.make(root, getString(R.string.removed_item), Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> DBWriter.addFavoriteItem(item));
snackbar.show();
}
@@ -82,6 +84,7 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
return root;
}
+ @NonNull
@Override
protected List<FeedItem> loadData() {
return DBReader.getFavoriteItemsList();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
new file mode 100644
index 000000000..4fb3d90f5
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
@@ -0,0 +1,177 @@
+package de.danoeh.antennapod.fragment;
+
+import android.arch.lifecycle.ViewModelProviders;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedFilter;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import de.danoeh.antennapod.dialog.EpisodeFilterDialog;
+import de.danoeh.antennapod.viewmodel.FeedSettingsViewModel;
+
+import static de.danoeh.antennapod.activity.FeedSettingsActivity.EXTRA_FEED_ID;
+
+public class FeedSettingsFragment extends PreferenceFragmentCompat {
+ private static final CharSequence PREF_EPISODE_FILTER = "episodeFilter";
+ private Feed feed;
+ private FeedPreferences feedPreferences;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.feed_settings);
+
+ long feedId = getArguments().getLong(EXTRA_FEED_ID);
+ ViewModelProviders.of(getActivity()).get(FeedSettingsViewModel.class).getFeed(feedId)
+ .subscribe(result -> {
+ feed = result;
+ feedPreferences = feed.getPreferences();
+
+ setupAutoDownloadPreference();
+ setupKeepUpdatedPreference();
+ setupAutoDeletePreference();
+ setupAuthentificationPreference();
+ setupEpisodeFilterPreference();
+
+ updateAutoDeleteSummary();
+ updateAutoDownloadEnabled();
+ }).dispose();
+ }
+
+ private void setupEpisodeFilterPreference() {
+ findPreference(PREF_EPISODE_FILTER).setOnPreferenceClickListener(preference -> {
+ new EpisodeFilterDialog(getContext(), feedPreferences.getFilter()) {
+ @Override
+ protected void onConfirmed(FeedFilter filter) {
+ feedPreferences.setFilter(filter);
+ feed.savePreferences();
+ }
+ }.show();
+ return false;
+ });
+ }
+
+ private void setupAuthentificationPreference() {
+ findPreference("authentication").setOnPreferenceClickListener(preference -> {
+ new AuthenticationDialog(getContext(),
+ R.string.authentication_label, true, false,
+ feedPreferences.getUsername(), feedPreferences.getPassword()) {
+ @Override
+ protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
+ feedPreferences.setUsername(username);
+ feedPreferences.setPassword(password);
+ feed.savePreferences();
+ }
+ }.show();
+ return false;
+ });
+ }
+
+ private void setupAutoDeletePreference() {
+ ListPreference autoDeletePreference = (ListPreference) findPreference("autoDelete");
+ autoDeletePreference.setOnPreferenceChangeListener((preference, newValue) -> {
+ switch ((String) newValue) {
+ case "global":
+ feedPreferences.setAutoDeleteAction(FeedPreferences.AutoDeleteAction.GLOBAL);
+ break;
+ case "always":
+ feedPreferences.setAutoDeleteAction(FeedPreferences.AutoDeleteAction.YES);
+ break;
+ case "never":
+ feedPreferences.setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NO);
+ break;
+ }
+ feed.savePreferences();
+ updateAutoDeleteSummary();
+ return false;
+ });
+ }
+
+ private void updateAutoDeleteSummary() {
+ ListPreference autoDeletePreference = (ListPreference) findPreference("autoDelete");
+
+ switch (feedPreferences.getAutoDeleteAction()) {
+ case GLOBAL:
+ autoDeletePreference.setSummary(R.string.feed_auto_download_global);
+ autoDeletePreference.setValue("global");
+ break;
+ case YES:
+ autoDeletePreference.setSummary(R.string.feed_auto_download_always);
+ autoDeletePreference.setValue("always");
+ break;
+ case NO:
+ autoDeletePreference.setSummary(R.string.feed_auto_download_never);
+ autoDeletePreference.setValue("never");
+ break;
+ }
+ }
+
+ private void setupKeepUpdatedPreference() {
+ SwitchPreference pref = (SwitchPreference) findPreference("keepUpdated");
+
+ pref.setChecked(feedPreferences.getKeepUpdated());
+ pref.setOnPreferenceChangeListener((preference, newValue) -> {
+ boolean checked = newValue == Boolean.TRUE;
+ feedPreferences.setKeepUpdated(checked);
+ feed.savePreferences();
+ pref.setChecked(checked);
+ return false;
+ });
+ }
+
+ private void setupAutoDownloadPreference() {
+ SwitchPreference pref = (SwitchPreference) findPreference("autoDownload");
+
+ pref.setEnabled(UserPreferences.isEnableAutodownload());
+ if (UserPreferences.isEnableAutodownload()) {
+ pref.setChecked(feedPreferences.getAutoDownload());
+ } else {
+ pref.setChecked(false);
+ pref.setSummary(R.string.auto_download_disabled_globally);
+ }
+
+ pref.setOnPreferenceChangeListener((preference, newValue) -> {
+ boolean checked = newValue == Boolean.TRUE;
+
+ feedPreferences.setAutoDownload(checked);
+ feed.savePreferences();
+ updateAutoDownloadEnabled();
+ ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(getActivity(), checked);
+ dialog.createNewDialog().show();
+ pref.setChecked(checked);
+ return false;
+ });
+ }
+
+ private void updateAutoDownloadEnabled() {
+ if (feed != null && feed.getPreferences() != null) {
+ boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload();
+ findPreference(PREF_EPISODE_FILTER).setEnabled(enabled);
+ }
+ }
+
+ private class ApplyToEpisodesDialog extends ConfirmationDialog {
+ private final boolean autoDownload;
+
+ ApplyToEpisodesDialog(Context context, boolean autoDownload) {
+ super(context, R.string.auto_download_apply_to_items_title,
+ R.string.auto_download_apply_to_items_message);
+ this.autoDownload = autoDownload;
+ setPositiveText(R.string.yes);
+ setNegativeText(R.string.no);
+ }
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ DBWriter.setFeedsItemsAutoDownload(feed, autoDownload);
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
index dadc596e2..9c16cfe56 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
@@ -16,24 +16,16 @@ import android.widget.Button;
import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
-import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.danoeh.antennapod.discovery.FyydPodcastSearcher;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.mfietz.fyydlin.FyydClient;
-import de.mfietz.fyydlin.FyydResponse;
-import de.mfietz.fyydlin.SearchHit;
-import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
-import static java.util.Collections.emptyList;
+import java.util.ArrayList;
+import java.util.List;
public class FyydSearchFragment extends Fragment {
@@ -49,12 +41,10 @@ public class FyydSearchFragment extends Fragment {
private Button butRetry;
private TextView txtvEmpty;
- private final FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient());
-
/**
* List of podcasts retreived from the search
*/
- private List<Podcast> searchResults;
+ private List<PodcastSearchResult> searchResults;
private Disposable disposable;
/**
@@ -81,7 +71,7 @@ public class FyydSearchFragment extends Fragment {
//Show information about the podcast when the list item is clicked
gridView.setOnItemClickListener((parent, view1, position, id) -> {
- Podcast podcast = searchResults.get(position);
+ PodcastSearchResult podcast = searchResults.get(position);
Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl);
intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title);
@@ -145,20 +135,26 @@ public class FyydSearchFragment extends Fragment {
disposable.dispose();
}
showOnlyProgressBar();
- disposable = client.searchPodcasts(query, 10)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- progressBar.setVisibility(View.GONE);
- processSearchResult(result);
- }, error -> {
- Log.e(TAG, Log.getStackTraceString(error));
- progressBar.setVisibility(View.GONE);
- txtvError.setText(error.toString());
- txtvError.setVisibility(View.VISIBLE);
- butRetry.setOnClickListener(v -> search(query));
- butRetry.setVisibility(View.VISIBLE);
- });
+
+ FyydPodcastSearcher searcher = new FyydPodcastSearcher();
+ disposable = searcher.search(query).subscribe(result -> {
+ searchResults = result;
+ progressBar.setVisibility(View.GONE);
+
+ adapter.clear();
+ adapter.addAll(searchResults);
+ adapter.notifyDataSetInvalidated();
+ gridView.setVisibility(!searchResults.isEmpty() ? View.VISIBLE : View.GONE);
+ txtvEmpty.setVisibility(searchResults.isEmpty() ? View.VISIBLE : View.GONE);
+
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(error.toString());
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setOnClickListener(v -> search(query));
+ butRetry.setVisibility(View.VISIBLE);
+ });
}
private void showOnlyProgressBar() {
@@ -168,25 +164,4 @@ public class FyydSearchFragment extends Fragment {
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
}
-
- private void processSearchResult(FyydResponse response) {
- adapter.clear();
- if (!response.getData().isEmpty()) {
- adapter.clear();
- searchResults = new ArrayList<>();
- for (SearchHit searchHit : response.getData()) {
- Podcast podcast = Podcast.fromSearch(searchHit);
- searchResults.add(podcast);
- }
- } else {
- searchResults = emptyList();
- }
- for(Podcast podcast : searchResults) {
- adapter.add(podcast);
- }
- adapter.notifyDataSetInvalidated();
- gridView.setVisibility(!searchResults.isEmpty() ? View.VISIBLE : View.GONE);
- txtvEmpty.setVisibility(searchResults.isEmpty() ? View.VISIBLE : View.GONE);
- }
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index 4ee7a06ad..5cf2c5eeb 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -10,7 +10,9 @@ import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.ContextMenu;
@@ -27,16 +29,11 @@ import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MediaplayerInfoActivity;
-import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment;
-import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Converter;
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.ShownotesProvider;
-import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
import io.reactivex.Observable;
@@ -47,7 +44,7 @@ import io.reactivex.schedulers.Schedulers;
/**
* Displays the description of a Playable object in a Webview.
*/
-public class ItemDescriptionFragment extends Fragment implements MediaplayerInfoContentFragment {
+public class ItemDescriptionFragment extends Fragment {
private static final String TAG = "ItemDescriptionFragment";
@@ -55,58 +52,16 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
private static final String PREF_SCROLL_Y = "prefScrollY";
private static final String PREF_PLAYABLE_ID = "prefPlayableId";
- private static final String ARG_PLAYABLE = "arg.playable";
- private static final String ARG_FEEDITEM_ID = "arg.feeditem";
-
- private static final String ARG_SAVE_STATE = "arg.saveState";
- private static final String ARG_HIGHLIGHT_TIMECODES = "arg.highlightTimecodes";
-
private WebView webvDescription;
-
- private ShownotesProvider shownotesProvider;
- private Playable media;
-
private Disposable webViewLoader;
+ private PlaybackController controller;
/**
* URL that was selected via long-press.
*/
private String selectedURL;
- /**
- * True if Fragment should save its state (e.g. scrolling position) in a
- * shared preference.
- */
- private boolean saveState;
-
- /**
- * True if Fragment should highlight timecodes (e.g. time codes in the HH:MM:SS format).
- */
- private boolean highlightTimecodes;
-
- public static ItemDescriptionFragment newInstance(Playable media,
- boolean saveState,
- boolean highlightTimecodes) {
- ItemDescriptionFragment f = new ItemDescriptionFragment();
- Bundle args = new Bundle();
- args.putParcelable(ARG_PLAYABLE, media);
- args.putBoolean(ARG_SAVE_STATE, saveState);
- args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes);
- f.setArguments(args);
- return f;
- }
-
- public static ItemDescriptionFragment newInstance(FeedItem item, boolean saveState, boolean highlightTimecodes) {
- ItemDescriptionFragment f = new ItemDescriptionFragment();
- Bundle args = new Bundle();
- args.putLong(ARG_FEEDITEM_ID, item.getId());
- args.putBoolean(ARG_SAVE_STATE, saveState);
- args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes);
- f.setArguments(args);
- return f;
- }
- @SuppressLint("NewApi")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -126,6 +81,10 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
// Use cached resources, even if they have expired
}
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ webvDescription.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+ }
+
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webvDescription.getSettings().setLoadWithOverviewMode(true);
@@ -175,39 +134,6 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
}
}
- @SuppressLint("NewApi")
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log.d(TAG, "Creating fragment");
- Bundle args = getArguments();
- saveState = args.getBoolean(ARG_SAVE_STATE, false);
- highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- Bundle args = getArguments();
- if (args.containsKey(ARG_PLAYABLE)) {
- if (media == null) {
- media = args.getParcelable(ARG_PLAYABLE);
- shownotesProvider = media;
- }
- load();
- } else if (args.containsKey(ARG_FEEDITEM_ID)) {
- long id = getArguments().getLong(ARG_FEEDITEM_ID);
- Observable.defer(() -> Observable.just(DBReader.getFeedItem(id)))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(feedItem -> {
- shownotesProvider = feedItem;
- load();
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
- }
- }
-
-
private final View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
@Override
@@ -300,22 +226,20 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
if(webViewLoader != null) {
webViewLoader.dispose();
}
- if(shownotesProvider == null) {
- return;
- }
webViewLoader = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
- webvDescription.loadDataWithBaseURL(null, data, "text/html",
+ webvDescription.loadDataWithBaseURL("https://127.0.0.1", data, "text/html",
"utf-8", "about:blank");
Log.d(TAG, "Webview loaded");
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @NonNull
private String loadData() {
- Timeline timeline = new Timeline(getActivity(), shownotesProvider);
- return timeline.processShownotes(highlightTimecodes);
+ Timeline timeline = new Timeline(getActivity(), controller.getMedia());
+ return timeline.processShownotes(true);
}
@Override
@@ -325,42 +249,38 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
}
private void savePreference() {
- if (saveState) {
- Log.d(TAG, "Saving preferences");
- SharedPreferences prefs = getActivity().getSharedPreferences(PREF,
- Activity.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- if (media != null && webvDescription != null) {
- Log.d(TAG, "Saving scroll position: " + webvDescription.getScrollY());
- editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
- editor.putString(PREF_PLAYABLE_ID, media.getIdentifier()
- .toString());
- } else {
- Log.d(TAG, "savePreferences was called while media or webview was null");
- editor.putInt(PREF_SCROLL_Y, -1);
- editor.putString(PREF_PLAYABLE_ID, "");
- }
- editor.commit();
+ Log.d(TAG, "Saving preferences");
+ SharedPreferences prefs = getActivity().getSharedPreferences(PREF,
+ Activity.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ if (controller != null && controller.getMedia() != null && webvDescription != null) {
+ Log.d(TAG, "Saving scroll position: " + webvDescription.getScrollY());
+ editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
+ editor.putString(PREF_PLAYABLE_ID, controller.getMedia().getIdentifier()
+ .toString());
+ } else {
+ Log.d(TAG, "savePreferences was called while media or webview was null");
+ editor.putInt(PREF_SCROLL_Y, -1);
+ editor.putString(PREF_PLAYABLE_ID, "");
}
+ editor.commit();
}
private boolean restoreFromPreference() {
- if (saveState) {
- Log.d(TAG, "Restoring from preferences");
- Activity activity = getActivity();
- if (activity != null) {
- SharedPreferences prefs = activity.getSharedPreferences(
- PREF, Activity.MODE_PRIVATE);
- String id = prefs.getString(PREF_PLAYABLE_ID, "");
- int scrollY = prefs.getInt(PREF_SCROLL_Y, -1);
- if (scrollY != -1 && media != null
- && id.equals(media.getIdentifier().toString())
- && webvDescription != null) {
- Log.d(TAG, "Restored scroll Position: " + scrollY);
- webvDescription.scrollTo(webvDescription.getScrollX(),
- scrollY);
- return true;
- }
+ Log.d(TAG, "Restoring from preferences");
+ Activity activity = getActivity();
+ if (activity != null) {
+ SharedPreferences prefs = activity.getSharedPreferences(
+ PREF, Activity.MODE_PRIVATE);
+ String id = prefs.getString(PREF_PLAYABLE_ID, "");
+ int scrollY = prefs.getInt(PREF_SCROLL_Y, -1);
+ if (controller != null && scrollY != -1 && controller.getMedia() != null
+ && id.equals(controller.getMedia().getIdentifier().toString())
+ && webvDescription != null) {
+ Log.d(TAG, "Restored scroll Position: " + scrollY);
+ webvDescription.scrollTo(webvDescription.getScrollX(),
+ scrollY);
+ return true;
}
}
return false;
@@ -377,15 +297,27 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
}
@Override
- public void onMediaChanged(Playable media) {
- if(this.media == media) {
- return;
- }
- this.media = media;
- this.shownotesProvider = media;
- if (webvDescription != null) {
- load();
- }
+ public void onStart() {
+ super.onStart();
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public boolean loadMediaInfo() {
+ if (getMedia() == null) {
+ return false;
+ }
+ load();
+ return true;
+ }
+
+ };
+ controller.init();
+ load();
}
+ @Override
+ public void onStop() {
+ super.onStop();
+ controller.release();
+ controller = null;
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index bcca281d4..432ada44e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -36,13 +36,16 @@ import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton;
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.DefaultActionButtonCallback;
+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;
@@ -61,14 +64,12 @@ 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.util.IntentUtils;
-import de.danoeh.antennapod.core.util.LongList;
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 de.greenrobot.event.EventBus;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -196,6 +197,9 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
// Use cached resources, even if they have expired
}
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ webvDescription.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+ }
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
@@ -227,9 +231,9 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
if (item == null) {
return;
}
- DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getActivity());
- actionButtonCallback.onActionButtonPressed(item, item.isTagged(FeedItem.TAG_QUEUE) ?
- LongList.of(item.getId()) : new LongList(0));
+ ItemActionButton actionButton = ItemActionButton.forItem(item, item.isTagged(FeedItem.TAG_QUEUE));
+ actionButton.onClick(getActivity());
+
FeedMedia media = item.getMedia();
if (media != null && media.isDownloaded()) {
// playback was started, dialog should close itself
@@ -262,14 +266,19 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
load();
}
@Override
public void onResume() {
super.onResume();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().registerSticky(this);
if(itemsLoaded) {
progbarLoading.setVisibility(View.GONE);
updateAppearance();
@@ -277,8 +286,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
@Override
- public void onPause() {
- super.onPause();
+ public void onStop() {
+ super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
}
@@ -297,19 +306,20 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public boolean onSwipeLeftToRight() {
- Log.d(TAG, "onSwipeLeftToRight()");
- feedItemPos = feedItemPos - 1;
- if(feedItemPos < 0) {
- feedItemPos = feedItems.length - 1;
- }
- load();
- return true;
+ return swipeFeedItem(-1);
}
@Override
public boolean onSwipeRightToLeft() {
- Log.d(TAG, "onSwipeRightToLeft()");
- feedItemPos = (feedItemPos + 1) % feedItems.length;
+ 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;
}
@@ -358,7 +368,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private void onFragmentLoaded() {
if (webviewData != null) {
- webvDescription.loadDataWithBaseURL(null, webviewData, "text/html", "utf-8", "about:blank");
+ webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank");
}
updateAppearance();
}
@@ -537,6 +547,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
((MainActivity)getActivity()).loadChildFragment(fragment);
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
for(FeedItem item : event.items) {
@@ -547,6 +558,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
@@ -588,10 +600,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @Nullable
private FeedItem loadInBackground() {
FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
- if (feedItem != null) {
- Timeline t = new Timeline(getActivity(), feedItem);
+ Context context = getContext();
+ if (feedItem != null && context != null) {
+ Timeline t = new Timeline(context, feedItem);
webviewData = t.processShownotes(false);
}
return feedItem;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
index d9e318069..0c75af986 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.LightingColorFilter;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
@@ -25,11 +26,13 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
-import com.joanzapata.iconify.IconDrawable;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
import org.apache.commons.lang3.Validate;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
@@ -37,7 +40,6 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.FeedInfoActivity;
import de.danoeh.antennapod.activity.FeedSettingsActivity;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
@@ -61,13 +63,13 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
+import de.danoeh.antennapod.core.util.Optional;
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.greenrobot.event.EventBus;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -94,8 +96,6 @@ public class ItemlistFragment extends ListFragment {
private long feedID;
private Feed feed;
- private boolean itemsLoaded = false;
- private boolean viewsCreated = false;
private boolean headerCreated = false;
private List<Downloader> downloaderList;
@@ -103,7 +103,7 @@ public class ItemlistFragment extends ListFragment {
private MoreContentListFooterUtil listFooter;
private boolean isUpdatingFeed;
-
+
private TextView txtvTitle;
private IconTextView txtvFailure;
private ImageView imgvBackground;
@@ -142,24 +142,21 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- if (viewsCreated && itemsLoaded) {
- onFragmentLoaded();
- }
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
+ loadItems();
}
@Override
public void onResume() {
super.onResume();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().registerSticky(this);
((MainActivity)getActivity()).getSupportActionBar().setTitle("");
updateProgressBarVisibility();
- loadItems();
}
@Override
- public void onPause() {
- super.onPause();
+ public void onStop() {
+ super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if(disposable != null) {
@@ -175,7 +172,6 @@ public class ItemlistFragment extends ListFragment {
private void resetViewState() {
adapter = null;
- viewsCreated = false;
listFooter = null;
}
@@ -188,45 +184,43 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
+ if (!isAdded()) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- FeedMenuHandler.onCreateOptionsMenu(inflater, menu);
-
- MenuItem searchItem = menu.findItem(R.id.action_search);
- final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- if (itemsLoaded) {
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s, feed.getId()));
- }
- return true;
- }
+ FeedMenuHandler.onCreateOptionsMenu(inflater, menu);
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
+ MenuItem searchItem = menu.findItem(R.id.action_search);
+ final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
+ MenuItemUtils.adjustTextColor(getActivity(), sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ if (feed != null) {
+ ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s, feed.getId()));
}
- });
- if(feed == null || feed.getLink() == null) {
- menu.findItem(R.id.share_link_item).setVisible(false);
- menu.findItem(R.id.visit_website_item).setVisible(false);
+ return true;
}
- isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ if (feed == null || feed.getLink() == null) {
+ menu.findItem(R.id.share_link_item).setVisible(false);
+ menu.findItem(R.id.visit_website_item).setVisible(false);
}
+
+ isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
- if (itemsLoaded) {
+ if (feed != null) {
FeedMenuHandler.onPrepareOptionsMenu(menu, feed);
}
}
@@ -339,11 +333,6 @@ public class ItemlistFragment extends ListFragment {
super.onViewCreated(view, savedInstanceState);
registerForContextMenu(getListView());
-
- viewsCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
- }
}
@Override
@@ -358,6 +347,7 @@ public class ItemlistFragment extends ListFragment {
activity.getSupportActionBar().setTitle(feed.getTitle());
}
+ @Subscribe
public void onEvent(FeedEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
if(event.feedId == feedID) {
@@ -365,6 +355,7 @@ public class ItemlistFragment extends ListFragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
if(feed == null || feed.getItems() == null || adapter == null) {
@@ -379,6 +370,7 @@ public class ItemlistFragment extends ListFragment {
}
}
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
@@ -422,7 +414,7 @@ public class ItemlistFragment extends ListFragment {
setListAdapter(null);
setupHeaderView();
setupFooterView();
- adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false, true);
+ adapter = new FeedItemlistAdapter(getActivity(), itemAccess, false, true);
setListAdapter(adapter);
}
refreshHeaderView();
@@ -498,7 +490,7 @@ public class ItemlistFragment extends ListFragment {
butShowInfo.setOnClickListener(v -> showFeedInfo());
imgvCover.setOnClickListener(v -> showFeedInfo());
butShowSettings.setOnClickListener(v -> {
- if (viewsCreated && itemsLoaded) {
+ if (feed != null) {
Intent startIntent = new Intent(getActivity(), FeedSettingsActivity.class);
startIntent.putExtra(FeedSettingsActivity.EXTRA_FEED_ID,
feed.getId());
@@ -509,7 +501,7 @@ public class ItemlistFragment extends ListFragment {
}
private void showFeedInfo() {
- if (viewsCreated && itemsLoaded) {
+ if (feed != null) {
Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class);
startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID,
feed.getId());
@@ -618,24 +610,20 @@ public class ItemlistFragment extends ListFragment {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
- if (result != null) {
- feed = result;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
- }
+ feed = result.orElse(null);
+ onFragmentLoaded();
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- private Feed loadData() {
+ @NonNull
+ private Optional<Feed> loadData() {
Feed feed = DBReader.getFeed(feedID);
- DBReader.loadAdditionalFeedItemListData(feed.getItems());
- if(feed != null && feed.getItemFilter() != null) {
+ if (feed != null && feed.getItemFilter() != null) {
+ DBReader.loadAdditionalFeedItemListData(feed.getItems());
FeedItemFilter filter = feed.getItemFilter();
feed.setItems(filter.filter(feed.getItems()));
}
- return feed;
+ return Optional.ofNullable(feed);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
index a0e2ca22a..80767bef2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
@@ -5,6 +5,7 @@ import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
+import android.support.annotation.NonNull;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -19,13 +20,14 @@ import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.discovery.ItunesPodcastSearcher;
+import de.danoeh.antennapod.discovery.ItunesTopListLoader;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -45,15 +47,11 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
-import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
-
//Searches iTunes store for given string and displays results in a list
public class ItunesSearchFragment extends Fragment {
private static final String TAG = "ItunesSearchFragment";
- private static final String API_URL = "https://itunes.apple.com/search?media=podcast&term=%s";
-
/**
* Adapter responsible with the search results
@@ -68,21 +66,21 @@ public class ItunesSearchFragment extends Fragment {
/**
* List of podcasts retreived from the search
*/
- private List<Podcast> searchResults;
- private List<Podcast> topList;
+ private List<PodcastSearchResult> searchResults;
+ private List<PodcastSearchResult> topList;
private Disposable disposable;
/**
* Replace adapter data with provided search results from SearchTask.
* @param result List of Podcast objects containing search results
*/
- private void updateData(List<Podcast> result) {
+ private void updateData(List<PodcastSearchResult> result) {
this.searchResults = result;
adapter.clear();
if (result != null && result.size() > 0) {
gridView.setVisibility(View.VISIBLE);
txtvEmpty.setVisibility(View.GONE);
- for (Podcast p : result) {
+ for (PodcastSearchResult p : result) {
adapter.add(p);
}
adapter.notifyDataSetInvalidated();
@@ -106,7 +104,7 @@ public class ItunesSearchFragment extends Fragment {
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
@@ -116,59 +114,31 @@ public class ItunesSearchFragment extends Fragment {
//Show information about the podcast when the list item is clicked
gridView.setOnItemClickListener((parent, view1, position, id) -> {
- Podcast podcast = searchResults.get(position);
- if(podcast.feedUrl == null) {
+ PodcastSearchResult podcast = searchResults.get(position);
+ if (podcast.feedUrl == null) {
return;
}
- if (!podcast.feedUrl.contains("itunes.apple.com")) {
- Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
- intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl);
- intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
- startActivity(intent);
- } else {
- gridView.setVisibility(View.GONE);
- progressBar.setVisibility(View.VISIBLE);
- disposable = Single.create((SingleOnSubscribe<String>) emitter -> {
- OkHttpClient client = AntennapodHttpClient.getHttpClient();
- Request.Builder httpReq = new Request.Builder()
- .url(podcast.feedUrl)
- .header("User-Agent", ClientConfig.USER_AGENT);
- try {
- Response response = client.newCall(httpReq.build()).execute();
- if (response.isSuccessful()) {
- String resultString = response.body().string();
- JSONObject result = new JSONObject(resultString);
- JSONObject results = result.getJSONArray("results").getJSONObject(0);
- String feedUrl = results.getString("feedUrl");
- emitter.onSuccess(feedUrl);
- } else {
- String prefix = getString(R.string.error_msg_prefix);
- emitter.onError(new IOException(prefix + response));
- }
- } catch (IOException | JSONException e) {
- emitter.onError(e);
- }
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(feedUrl -> {
- progressBar.setVisibility(View.GONE);
- gridView.setVisibility(View.VISIBLE);
- Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
- intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl);
- intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
- startActivity(intent);
- }, error -> {
- Log.e(TAG, Log.getStackTraceString(error));
- progressBar.setVisibility(View.GONE);
- gridView.setVisibility(View.VISIBLE);
- String prefix = getString(R.string.error_msg_prefix);
- new MaterialDialog.Builder(getActivity())
- .content(prefix + " " + error.getMessage())
- .neutralText(android.R.string.ok)
- .show();
- });
- }
+ gridView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.VISIBLE);
+ ItunesTopListLoader loader = new ItunesTopListLoader(getContext());
+ disposable = loader.getFeedUrl(podcast)
+ .subscribe(feedUrl -> {
+ progressBar.setVisibility(View.GONE);
+ gridView.setVisibility(View.VISIBLE);
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl);
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
+ startActivity(intent);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ gridView.setVisibility(View.VISIBLE);
+ String prefix = getString(R.string.error_msg_prefix);
+ new MaterialDialog.Builder(getActivity())
+ .content(prefix + " " + error.getMessage())
+ .neutralText(android.R.string.ok)
+ .show();
+ });
});
progressBar = root.findViewById(R.id.progressBar);
txtvError = root.findViewById(R.id.txtvError);
@@ -236,47 +206,9 @@ public class ItunesSearchFragment extends Fragment {
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
- disposable = Single.create((SingleOnSubscribe<List<Podcast>>) emitter -> {
- String lang = Locale.getDefault().getLanguage();
- String url = "https://itunes.apple.com/" + lang + "/rss/toppodcasts/limit=25/explicit=true/json";
- OkHttpClient client = AntennapodHttpClient.getHttpClient();
- Request.Builder httpReq = new Request.Builder()
- .url(url)
- .header("User-Agent", ClientConfig.USER_AGENT);
- List<Podcast> results = new ArrayList<>();
- try {
- Response response = client.newCall(httpReq.build()).execute();
- if(!response.isSuccessful()) {
- // toplist for language does not exist, fall back to united states
- url = "https://itunes.apple.com/us/rss/toppodcasts/limit=25/explicit=true/json";
- httpReq = new Request.Builder()
- .url(url)
- .header("User-Agent", ClientConfig.USER_AGENT);
- response = client.newCall(httpReq.build()).execute();
- }
- if(response.isSuccessful()) {
- String resultString = response.body().string();
- JSONObject result = new JSONObject(resultString);
- JSONObject feed = result.getJSONObject("feed");
- JSONArray entries = feed.getJSONArray("entry");
- for(int i=0; i < entries.length(); i++) {
- JSONObject json = entries.getJSONObject(i);
- Podcast podcast = Podcast.fromToplist(json);
- results.add(podcast);
- }
- }
- else {
- String prefix = getString(R.string.error_msg_prefix);
- emitter.onError(new IOException(prefix + response));
- }
- } catch (IOException | JSONException e) {
- emitter.onError(e);
- }
- emitter.onSuccess(results);
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ ItunesTopListLoader loader = new ItunesTopListLoader(getContext());
+ disposable = loader.loadToplist(25)
.subscribe(podcasts -> {
progressBar.setVisibility(View.GONE);
topList = podcasts;
@@ -300,61 +232,19 @@ public class ItunesSearchFragment extends Fragment {
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
- disposable = Single.create((SingleOnSubscribe<List<Podcast>>) subscriber -> {
- String encodedQuery = null;
- try {
- encodedQuery = URLEncoder.encode(query, "UTF-8");
- } catch (UnsupportedEncodingException e) {
- // this won't ever be thrown
- }
- if (encodedQuery == null) {
- encodedQuery = query; // failsafe
- }
-
- //Spaces in the query need to be replaced with '+' character.
- String formattedUrl = String.format(API_URL, query).replace(' ', '+');
- OkHttpClient client = AntennapodHttpClient.getHttpClient();
- Request.Builder httpReq = new Request.Builder()
- .url(formattedUrl)
- .header("User-Agent", ClientConfig.USER_AGENT);
- List<Podcast> podcasts = new ArrayList<>();
- try {
- Response response = client.newCall(httpReq.build()).execute();
-
- if(response.isSuccessful()) {
- String resultString = response.body().string();
- JSONObject result = new JSONObject(resultString);
- JSONArray j = result.getJSONArray("results");
-
- for (int i = 0; i < j.length(); i++) {
- JSONObject podcastJson = j.getJSONObject(i);
- Podcast podcast = Podcast.fromSearch(podcastJson);
- podcasts.add(podcast);
- }
- }
- else {
- String prefix = getString(R.string.error_msg_prefix);
- subscriber.onError(new IOException(prefix + response));
- }
- } catch (IOException | JSONException e) {
- subscriber.onError(e);
- }
- subscriber.onSuccess(podcasts);
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(podcasts -> {
- progressBar.setVisibility(View.GONE);
- updateData(podcasts);
- }, error -> {
- Log.e(TAG, Log.getStackTraceString(error));
- progressBar.setVisibility(View.GONE);
- txtvError.setText(error.toString());
- txtvError.setVisibility(View.VISIBLE);
- butRetry.setOnClickListener(v -> search(query));
- butRetry.setVisibility(View.VISIBLE);
- });
+ ItunesPodcastSearcher searcher = new ItunesPodcastSearcher(getContext());
+ disposable = searcher.search(query).subscribe(podcasts -> {
+ progressBar.setVisibility(View.GONE);
+ updateData(podcasts);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(error.toString());
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setOnClickListener(v -> search(query));
+ butRetry.setVisibility(View.VISIBLE);
+ });
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index 6695ba427..1bf907aee 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -12,53 +12,39 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.util.FeedItemUtil;
-
/**
* Like 'EpisodesFragment' except that it only shows new episodes and
* supports swiping to mark as read.
*/
-
public class NewEpisodesFragment extends AllEpisodesFragment {
public static final String TAG = "NewEpisodesFragment";
-
private static final String PREF_NAME = "PrefNewEpisodesFragment";
@Override
- protected boolean showOnlyNewEpisodes() { return true; }
-
- @Override
- protected String getPrefName() { return PREF_NAME; }
+ protected boolean showOnlyNewEpisodes() {
+ return true;
+ }
@Override
- protected void resetViewState() {
- super.resetViewState();
+ protected String getPrefName() {
+ return PREF_NAME;
}
@Override
- public void onEventMainThread(FeedItemEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if(episodes == null) {
- return;
- }
- for(FeedItem item : event.items) {
- int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
- if(pos >= 0 && item.isTagged(FeedItem.TAG_QUEUE)) {
- episodes.remove(pos);
- listAdapter.notifyItemRemoved(pos);
- }
- }
+ protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
+ return item.isNew();
}
+ @NonNull
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
- R.layout.all_episodes_fragment);
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, container, savedInstanceState);
+ emptyView.setTitle(R.string.no_new_episodes_head_label);
+ emptyView.setMessage(R.string.no_new_episodes_label);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
@@ -68,8 +54,8 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
- AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder;
- markItemAsSeenWithUndo(holder.getFeedItem());
+ AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder;
+ removeNewFlagWithUndo(holder.getFeedItem());
}
@Override
@@ -86,6 +72,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
super.onSelectedChanged(viewHolder, actionState);
}
+
@Override
public void clearView(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder) {
@@ -105,9 +92,9 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
return root;
}
+ @NonNull
@Override
protected List<FeedItem> loadData() {
return DBReader.getNewItemsList();
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index c2a9200c8..e2060481f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
import android.util.Log;
@@ -12,11 +12,14 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+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.MainActivity;
-import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
@@ -29,7 +32,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
-import de.greenrobot.event.EventBus;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -44,23 +47,10 @@ public class PlaybackHistoryFragment extends ListFragment {
private List<FeedItem> playbackHistory;
private FeedItemlistAdapter adapter;
-
- private boolean itemsLoaded = false;
- private boolean viewsCreated = false;
-
private List<Downloader> downloaderList;
-
private Disposable disposable;
@Override
- public void onAttach(Context context) {
- super.onAttach(context);
- if (viewsCreated && itemsLoaded) {
- onFragmentLoaded();
- }
- }
-
- @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
@@ -77,63 +67,43 @@ public class PlaybackHistoryFragment extends ListFragment {
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
- viewsCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
- }
- }
-
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setIcon(R.attr.ic_history);
+ emptyView.setTitle(R.string.no_history_head_label);
+ emptyView.setMessage(R.string.no_history_label);
+ emptyView.attachToListView(getListView());
- @Override
- public void onResume() {
- super.onResume();
- EventBus.getDefault().registerSticky(this);
- loadItems();
+ // played items shoudln't be transparent for this fragment since, *all* items
+ // in this fragment will, by definition, be played. So it serves no purpose and can make
+ // it harder to read.
+ adapter = new FeedItemlistAdapter(getActivity(), itemAccess, true, false);
+ setListAdapter(adapter);
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- EventBus.getDefault().unregister(this);
+ EventBus.getDefault().register(this);
+ loadItems();
}
@Override
public void onStop() {
super.onStop();
+ EventBus.getDefault().unregister(this);
EventDistributor.getInstance().unregister(contentUpdate);
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
}
- @Override
- public void onDetach() {
- super.onDetach();
- if(disposable != null) {
- disposable.dispose();
- }
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- adapter = null;
- viewsCreated = false;
- }
-
+ @Subscribe(sticky = true)
public void onEvent(DownloadEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
+ adapter.notifyDataSetChanged();
}
@Override
@@ -146,27 +116,23 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded()) {
+ if (!isAdded()) {
return;
}
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
- MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
- clearHistory.setIcon(drawables.getDrawable(0));
- drawables.recycle();
- }
+ MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
+ MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
+ clearHistory.setIcon(drawables.getDrawable(0));
+ drawables.recycle();
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- if (itemsLoaded) {
- MenuItem menuItem = menu.findItem(R.id.clear_history_item);
- if (menuItem != null) {
- menuItem.setVisible(playbackHistory != null && !playbackHistory.isEmpty());
- }
+ MenuItem menuItem = menu.findItem(R.id.clear_history_item);
+ if (menuItem != null) {
+ menuItem.setVisible(playbackHistory != null && !playbackHistory.isEmpty());
}
}
@@ -185,6 +151,7 @@ public class PlaybackHistoryFragment extends ListFragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
if(playbackHistory == null) {
@@ -211,15 +178,6 @@ public class PlaybackHistoryFragment extends ListFragment {
};
private void onFragmentLoaded() {
- if (adapter == null) {
- // played items shoudln't be transparent for this fragment since, *all* items
- // in this fragment will, by definition, be played. So it serves no purpose and can make
- // it harder to read.
- adapter = new FeedItemlistAdapter(getActivity(), itemAccess,
- new DefaultActionButtonCallback(getActivity()), true, false);
- setListAdapter(adapter);
- }
- setListShown(true);
adapter.notifyDataSetChanged();
getActivity().supportInvalidateOptionsMenu();
}
@@ -278,18 +236,15 @@ public class PlaybackHistoryFragment extends ListFragment {
.subscribe(result -> {
if (result != null) {
playbackHistory = result;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
+ onFragmentLoaded();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @NonNull
private List<FeedItem> loadData() {
List<FeedItem> history = DBReader.getPlaybackHistory();
DBReader.loadAdditionalFeedItemListData(history);
return history;
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index faeabf75c..0fe413954 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -28,7 +28,6 @@ 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.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
@@ -50,13 +49,22 @@ import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.QueueSorter;
+import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-import de.greenrobot.event.EventBus;
+
+import de.danoeh.antennapod.view.EmptyViewHandler;
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 static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
+import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REMOVE_FROM_QUEUE;
/**
* Shows all items in the queue
@@ -72,7 +80,7 @@ public class QueueFragment extends Fragment {
private TextView infoBar;
private RecyclerView recyclerView;
private QueueRecyclerAdapter recyclerAdapter;
- private TextView txtvEmpty;
+ private EmptyViewHandler emptyView;
private ProgressBar progLoading;
private List<FeedItem> queue;
@@ -102,20 +110,20 @@ public class QueueFragment extends Fragment {
if (queue != null) {
onFragmentLoaded(true);
}
- }
-
- @Override
- public void onResume() {
- super.onResume();
loadItems(true);
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().registerSticky(this);
+ EventBus.getDefault().register(this);
}
@Override
public void onPause() {
super.onPause();
saveScrollPosition();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if(disposable != null) {
@@ -123,9 +131,13 @@ public class QueueFragment extends Fragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(QueueEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if(queue == null || recyclerAdapter == null) {
+ if (queue == null) {
+ return;
+ } else if (recyclerAdapter == null) {
+ loadItems(true);
return;
}
switch(event.action) {
@@ -158,9 +170,13 @@ public class QueueFragment extends Fragment {
onFragmentLoaded(false);
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- if(queue == null || recyclerAdapter == null) {
+ if (queue == null) {
+ return;
+ } else if (recyclerAdapter == null) {
+ loadItems(true);
return;
}
for(int i=0, size = event.items.size(); i < size; i++) {
@@ -170,10 +186,12 @@ public class QueueFragment extends Fragment {
queue.remove(pos);
queue.add(pos, item);
recyclerAdapter.notifyItemChanged(pos);
+ refreshInfoBar();
}
}
}
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
@@ -259,6 +277,19 @@ public class QueueFragment extends Fragment {
MenuItemUtils.refreshLockItem(getActivity(), menu);
+ // Show Lock Item only if queue is sorted manually
+ boolean keepSorted = UserPreferences.isQueueKeepSorted();
+ MenuItem lockItem = menu.findItem(R.id.queue_lock);
+ lockItem.setVisible(!keepSorted);
+
+ // Random sort is not supported in keep sorted mode
+ MenuItem sortRandomItem = menu.findItem(R.id.queue_sort_random);
+ sortRandomItem.setVisible(!keepSorted);
+
+ // Set keep sorted checkbox
+ MenuItem keepSortedItem = menu.findItem(R.id.queue_keep_sorted);
+ keepSortedItem.setChecked(keepSorted);
+
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
}
@@ -271,7 +302,9 @@ public class QueueFragment extends Fragment {
boolean newLockState = !UserPreferences.isQueueLocked();
UserPreferences.setQueueLocked(newLockState);
getActivity().supportInvalidateOptionsMenu();
- recyclerAdapter.setLocked(newLockState);
+ if (recyclerAdapter != null) {
+ recyclerAdapter.setLocked(newLockState);
+ }
if (newLockState) {
Snackbar.make(getActivity().findViewById(R.id.content), R.string
.queue_locked, Snackbar.LENGTH_SHORT).show();
@@ -301,38 +334,55 @@ public class QueueFragment extends Fragment {
};
conDialog.createNewDialog().show();
return true;
+ case R.id.episode_actions:
+ ((MainActivity) requireActivity()) .loadChildFragment(
+ EpisodesApplyActionFragment.newInstance(queue, ACTION_DELETE | ACTION_REMOVE_FROM_QUEUE));
+ return true;
case R.id.queue_sort_episode_title_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.EPISODE_TITLE_ASC, true);
+ setSortOrder(SortOrder.EPISODE_TITLE_A_Z);
return true;
case R.id.queue_sort_episode_title_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.EPISODE_TITLE_DESC, true);
+ setSortOrder(SortOrder.EPISODE_TITLE_Z_A);
return true;
case R.id.queue_sort_date_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.DATE_ASC, true);
+ setSortOrder(SortOrder.DATE_OLD_NEW);
return true;
case R.id.queue_sort_date_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.DATE_DESC, true);
+ setSortOrder(SortOrder.DATE_NEW_OLD);
return true;
case R.id.queue_sort_duration_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.DURATION_ASC, true);
+ setSortOrder(SortOrder.DURATION_SHORT_LONG);
return true;
case R.id.queue_sort_duration_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.DURATION_DESC, true);
+ setSortOrder(SortOrder.DURATION_LONG_SHORT);
return true;
case R.id.queue_sort_feed_title_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_ASC, true);
+ setSortOrder(SortOrder.FEED_TITLE_A_Z);
return true;
case R.id.queue_sort_feed_title_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_DESC, true);
+ setSortOrder(SortOrder.FEED_TITLE_Z_A);
return true;
case R.id.queue_sort_random:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.RANDOM, true);
+ setSortOrder(SortOrder.RANDOM);
return true;
case R.id.queue_sort_smart_shuffle_asc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_ASC, true);
+ setSortOrder(SortOrder.SMART_SHUFFLE_OLD_NEW);
return true;
case R.id.queue_sort_smart_shuffle_desc:
- QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_DESC, true);
+ setSortOrder(SortOrder.SMART_SHUFFLE_NEW_OLD);
+ return true;
+ case R.id.queue_keep_sorted:
+ boolean keepSortedOld = UserPreferences.isQueueKeepSorted();
+ boolean keepSortedNew = !keepSortedOld;
+ UserPreferences.setQueueKeepSorted(keepSortedNew);
+ if (keepSortedNew) {
+ SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder();
+ QueueSorter.sort(sortOrder, true);
+ recyclerAdapter.setLocked(true);
+ } else {
+ recyclerAdapter.setLocked(UserPreferences.isQueueLocked());
+ }
+ getActivity().invalidateOptionsMenu();
return true;
default:
return false;
@@ -342,6 +392,15 @@ public class QueueFragment extends Fragment {
}
}
+ /**
+ * This method is called if the user clicks on a sort order menu item.
+ *
+ * @param sortOrder New sort order.
+ */
+ private void setSortOrder(SortOrder sortOrder) {
+ UserPreferences.setQueueKeepSortedOrder(sortOrder);
+ QueueSorter.sort(sortOrder, true);
+ }
@Override
public boolean onContextItemSelected(MenuItem item) {
@@ -431,7 +490,7 @@ public class QueueFragment extends Fragment {
final FeedItem item = queue.get(position);
final boolean isRead = item.isPlayed();
DBWriter.markItemPlayed(FeedItem.PLAYED, false, item.getId());
- DBWriter.removeQueueItem(getActivity(), item, true);
+ DBWriter.removeQueueItem(getActivity(), true, item);
Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_read_label), Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> {
DBWriter.addQueueItemAt(getActivity(), item.getId(), position, false);
@@ -494,8 +553,12 @@ public class QueueFragment extends Fragment {
);
itemTouchHelper.attachToRecyclerView(recyclerView);
- txtvEmpty = root.findViewById(android.R.id.empty);
- txtvEmpty.setVisibility(View.GONE);
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.attachToRecyclerView(recyclerView);
+ emptyView.setIcon(R.attr.stat_playlist);
+ emptyView.setTitle(R.string.no_items_header_label);
+ emptyView.setMessage(R.string.no_items_label);
+
progLoading = root.findViewById(R.id.progLoading);
progLoading.setVisibility(View.VISIBLE);
@@ -503,19 +566,19 @@ public class QueueFragment extends Fragment {
}
private void onFragmentLoaded(final boolean restoreScrollPosition) {
- if (recyclerAdapter == null) {
- MainActivity activity = (MainActivity) getActivity();
- recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess,
- new DefaultActionButtonCallback(activity), itemTouchHelper);
- recyclerAdapter.setHasStableIds(true);
- recyclerView.setAdapter(recyclerAdapter);
- }
- if(queue == null || queue.size() == 0) {
- recyclerView.setVisibility(View.GONE);
- txtvEmpty.setVisibility(View.VISIBLE);
- } else {
- txtvEmpty.setVisibility(View.GONE);
+ if (queue != null && queue.size() > 0) {
+ if (recyclerAdapter == null) {
+ MainActivity activity = (MainActivity) getActivity();
+ recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess, itemTouchHelper);
+ recyclerAdapter.setHasStableIds(true);
+ recyclerView.setAdapter(recyclerAdapter);
+ emptyView.updateAdapter(recyclerAdapter);
+ }
recyclerView.setVisibility(View.VISIBLE);
+ } else {
+ recyclerAdapter = null;
+ recyclerView.setVisibility(View.GONE);
+ emptyView.updateAdapter(recyclerAdapter);
}
if (restoreScrollPosition) {
@@ -628,22 +691,19 @@ public class QueueFragment extends Fragment {
}
if (queue == null) {
recyclerView.setVisibility(View.GONE);
- txtvEmpty.setVisibility(View.GONE);
+ emptyView.hide();
progLoading.setVisibility(View.VISIBLE);
}
disposable = Observable.fromCallable(DBReader::getQueue)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> {
- if(items != null) {
- progLoading.setVisibility(View.GONE);
- queue = items;
- onFragmentLoaded(restoreScrollPosition);
- if(recyclerAdapter != null) {
- recyclerAdapter.notifyDataSetChanged();
- }
+ progLoading.setVisibility(View.GONE);
+ queue = items;
+ onFragmentLoaded(restoreScrollPosition);
+ if(recyclerAdapter != null) {
+ recyclerAdapter.notifyDataSetChanged();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
new file mode 100644
index 000000000..e4213cc6b
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
@@ -0,0 +1,119 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.GridView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
+import de.danoeh.antennapod.adapter.FeedDiscoverAdapter;
+import de.danoeh.antennapod.discovery.ItunesTopListLoader;
+import de.danoeh.antennapod.discovery.PodcastSearchResult;
+import io.reactivex.disposables.Disposable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView.OnItemClickListener {
+ private static final String TAG = "FeedDiscoveryFragment";
+
+ private ProgressBar progressBar;
+ private Disposable disposable;
+ private FeedDiscoverAdapter adapter;
+ private GridView subscriptionGridLayout;
+ private TextView errorTextView;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View root = inflater.inflate(R.layout.quick_feed_discovery, container, false);
+ View discoverMore = root.findViewById(R.id.discover_more);
+ discoverMore.setOnClickListener(v ->
+ ((MainActivity) getActivity()).loadChildFragment(new ItunesSearchFragment()));
+
+ subscriptionGridLayout = root.findViewById(R.id.discover_grid);
+ progressBar = root.findViewById(R.id.discover_progress_bar);
+ errorTextView = root.findViewById(R.id.discover_error);
+
+ adapter = new FeedDiscoverAdapter((MainActivity) getActivity());
+ subscriptionGridLayout.setAdapter(adapter);
+ subscriptionGridLayout.setOnItemClickListener(this);
+
+ // Fill with dummy elements to have a fixed height and
+ // prevent the UI elements below from jumping on slow connections
+ List<PodcastSearchResult> dummies = new ArrayList<>();
+ for (int i = 0; i < 8; i++) {
+ dummies.add(PodcastSearchResult.dummy());
+ }
+ adapter.updateData(dummies);
+
+ loadToplist();
+
+ return root;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ private void loadToplist() {
+ progressBar.setVisibility(View.VISIBLE);
+ subscriptionGridLayout.setVisibility(View.INVISIBLE);
+ errorTextView.setVisibility(View.GONE);
+
+ ItunesTopListLoader loader = new ItunesTopListLoader(getContext());
+ disposable = loader.loadToplist(8)
+ .subscribe(podcasts -> {
+ errorTextView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.GONE);
+ subscriptionGridLayout.setVisibility(View.VISIBLE);
+ adapter.updateData(podcasts);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ errorTextView.setText(error.getLocalizedMessage());
+ errorTextView.setVisibility(View.VISIBLE);
+ progressBar.setVisibility(View.GONE);
+ subscriptionGridLayout.setVisibility(View.INVISIBLE);
+ });
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
+ PodcastSearchResult podcast = adapter.getItem(position);
+ if (podcast.feedUrl == null) {
+ return;
+ }
+ view.setAlpha(0.5f);
+ ItunesTopListLoader loader = new ItunesTopListLoader(getContext());
+ disposable = loader.getFeedUrl(podcast)
+ .subscribe(feedUrl -> {
+ view.setAlpha(1f);
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl);
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label));
+ startActivity(intent);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ view.setAlpha(1f);
+ String prefix = getString(R.string.error_msg_prefix);
+ new MaterialDialog.Builder(getActivity())
+ .content(prefix + " " + error.getMessage())
+ .neutralText(android.R.string.ok)
+ .show();
+ });
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
index 66c59b7f7..2a7f7d12b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -7,6 +7,10 @@ import android.view.View;
import android.widget.ListView;
import android.widget.Toast;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -20,7 +24,7 @@ import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.greenrobot.event.EventBus;
+import de.danoeh.antennapod.view.EmptyViewHandler;
/**
* Displays all running downloads and provides actions to cancel them
@@ -30,7 +34,7 @@ public class RunningDownloadsFragment extends ListFragment {
private static final String TAG = "RunningDownloadsFrag";
private DownloadlistAdapter adapter;
- private List<Downloader> downloaderList;
+ private List<Downloader> downloaderList = new ArrayList<>();
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -44,17 +48,24 @@ public class RunningDownloadsFragment extends ListFragment {
adapter = new DownloadlistAdapter(getActivity(), itemAccess);
setListAdapter(adapter);
+
+ EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
+ emptyView.setIcon(R.attr.av_download);
+ emptyView.setTitle(R.string.no_run_downloads_head_label);
+ emptyView.setMessage(R.string.no_run_downloads_label);
+ emptyView.attachToListView(getListView());
+
}
@Override
- public void onResume() {
- super.onResume();
- EventBus.getDefault().registerSticky(this);
+ public void onStart() {
+ super.onStart();
+ EventBus.getDefault().register(this);
}
@Override
- public void onPause() {
- super.onPause();
+ public void onStop() {
+ super.onStop();
EventBus.getDefault().unregister(this);
}
@@ -62,28 +73,25 @@ public class RunningDownloadsFragment extends ListFragment {
public void onDestroy() {
super.onDestroy();
setListAdapter(null);
- adapter = null;
}
+ @Subscribe(sticky = true)
public void onEvent(DownloadEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
+ adapter.notifyDataSetChanged();
}
-
private final DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() {
@Override
public int getCount() {
- return (downloaderList != null) ? downloaderList.size() : 0;
+ return downloaderList.size();
}
@Override
public Downloader getItem(int position) {
- if (downloaderList != null && 0 <= position && position < downloaderList.size()) {
+ if (0 <= position && position < downloaderList.size()) {
return downloaderList.get(position);
} else {
return null;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index 8322a5573..0892bce0a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment;
import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
@@ -13,6 +14,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -39,11 +41,7 @@ public class SearchFragment extends ListFragment {
private static final String ARG_FEED = "feed";
private SearchlistAdapter searchAdapter;
- private List<SearchResult> searchResults;
-
- private boolean viewCreated = false;
- private boolean itemsLoaded = false;
-
+ private List<SearchResult> searchResults = new ArrayList<>();
private Disposable disposable;
/**
@@ -73,13 +71,13 @@ public class SearchFragment extends ListFragment {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
- search();
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
+ search();
}
@Override
@@ -92,21 +90,6 @@ public class SearchFragment extends ListFragment {
}
@Override
- public void onDetach() {
- super.onDetach();
- if(disposable != null) {
- disposable.dispose();
- }
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- searchAdapter = null;
- viewCreated = false;
- }
-
- @Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -117,10 +100,9 @@ public class SearchFragment extends ListFragment {
lv.setPadding(0, vertPadding, 0, vertPadding);
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.search_label);
- viewCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
- }
+
+ searchAdapter = new SearchlistAdapter(getActivity(), itemAccess);
+ setListAdapter(searchAdapter);
}
@Override
@@ -141,28 +123,26 @@ public class SearchFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
- MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
- MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- final SearchView sv = new SearchView(getActivity());
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setQuery(getArguments().getString(ARG_QUERY), false);
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- getArguments().putString(ARG_QUERY, s);
- itemsLoaded = false;
- search();
- return true;
- }
-
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
- MenuItemCompat.setActionView(item, sv);
- }
+ MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
+ MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ final SearchView sv = new SearchView(getActivity());
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setQuery(getArguments().getString(ARG_QUERY), false);
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ getArguments().putString(ARG_QUERY, s);
+ search();
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ MenuItemCompat.setActionView(item, sv);
}
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@@ -175,14 +155,9 @@ public class SearchFragment extends ListFragment {
}
};
- private void onFragmentLoaded() {
- if (searchAdapter == null) {
- searchAdapter = new SearchlistAdapter(getActivity(), itemAccess);
- setListAdapter(searchAdapter);
- }
+ private void onSearchResults(List<SearchResult> results) {
+ searchResults = results;
searchAdapter.notifyDataSetChanged();
- setListShown(true);
-
String query = getArguments().getString(ARG_QUERY);
setEmptyText(getString(R.string.no_results_for_query, query));
}
@@ -190,12 +165,12 @@ public class SearchFragment extends ListFragment {
private final SearchlistAdapter.ItemAccess itemAccess = new SearchlistAdapter.ItemAccess() {
@Override
public int getCount() {
- return (searchResults != null) ? searchResults.size() : 0;
+ return searchResults.size();
}
@Override
public SearchResult getItem(int position) {
- if (searchResults != null && 0 <= position && position < searchResults.size()) {
+ if (0 <= position && position < searchResults.size()) {
return searchResults.get(position);
} else {
return null;
@@ -203,28 +178,17 @@ public class SearchFragment extends ListFragment {
}
};
-
private void search() {
if(disposable != null) {
disposable.dispose();
}
- if (viewCreated && !itemsLoaded) {
- setListShown(false);
- }
disposable = Observable.fromCallable(this::performSearch)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- if (result != null) {
- itemsLoaded = true;
- searchResults = result;
- if (viewCreated) {
- onFragmentLoaded();
- }
- }
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ .subscribe(this::onSearchResults, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+ @NonNull
private List<SearchResult> performSearch() {
Bundle args = getArguments();
String query = args.getString(ARG_QUERY);
@@ -232,5 +196,4 @@ public class SearchFragment extends ListFragment {
Context context = getActivity();
return FeedSearcher.performSearch(context, query, feed);
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
index 5f09be8ce..15c6052a9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -1,11 +1,16 @@
package de.danoeh.antennapod.fragment;
+import android.annotation.SuppressLint;
+import android.content.Context;
import android.content.DialogInterface;
+import android.content.SharedPreferences;
import android.os.Bundle;
+import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
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.View;
@@ -13,6 +18,8 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridView;
+import java.util.concurrent.Callable;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SubscriptionsAdapter;
@@ -41,6 +48,8 @@ public class SubscriptionFragment extends Fragment {
private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
| EventDistributor.UNREAD_ITEMS_UPDATE;
+ private static final String PREFS = "SubscriptionFragment";
+ private static final String PREF_NUM_COLUMNS = "columns";
private GridView subscriptionGridLayout;
private DBReader.NavDrawerData navDrawerData;
@@ -49,19 +58,15 @@ public class SubscriptionFragment extends Fragment {
private int mPosition = -1;
private Disposable disposable;
-
- public SubscriptionFragment() {
- }
+ private SharedPreferences prefs;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
-
- // So, we certainly *don't* have an options menu,
- // but unless we say we do, old options menus sometimes
- // persist. mfietz thinks this causes the ActionBar to be invalidated
setHasOptionsMenu(true);
+
+ prefs = requireActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
}
@Override
@@ -69,31 +74,75 @@ public class SubscriptionFragment extends Fragment {
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_subscriptions, container, false);
subscriptionGridLayout = root.findViewById(R.id.subscriptions_grid);
+ subscriptionGridLayout.setNumColumns(prefs.getInt(PREF_NUM_COLUMNS, 3));
registerForContextMenu(subscriptionGridLayout);
return root;
}
@Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.subscriptions, menu);
+
+ int columns = prefs.getInt(PREF_NUM_COLUMNS, 3);
+ menu.findItem(R.id.subscription_num_columns_2).setChecked(columns == 2);
+ menu.findItem(R.id.subscription_num_columns_3).setChecked(columns == 3);
+ menu.findItem(R.id.subscription_num_columns_4).setChecked(columns == 4);
+ menu.findItem(R.id.subscription_num_columns_5).setChecked(columns == 5);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (super.onOptionsItemSelected(item)) {
+ return true;
+ }
+ switch (item.getItemId()) {
+ case R.id.subscription_num_columns_2:
+ setColumnNumber(2);
+ return true;
+ case R.id.subscription_num_columns_3:
+ setColumnNumber(3);
+ return true;
+ case R.id.subscription_num_columns_4:
+ setColumnNumber(4);
+ return true;
+ case R.id.subscription_num_columns_5:
+ setColumnNumber(5);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void setColumnNumber(int columns) {
+ subscriptionGridLayout.setNumColumns(columns);
+ prefs.edit().putInt(PREF_NUM_COLUMNS, columns).apply();
+ getActivity().invalidateOptionsMenu();
+ }
+
+ @Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
subscriptionAdapter = new SubscriptionsAdapter((MainActivity)getActivity(), itemAccess);
-
subscriptionGridLayout.setAdapter(subscriptionAdapter);
-
- loadSubscriptions();
-
subscriptionGridLayout.setOnItemClickListener(subscriptionAdapter);
if (getActivity() instanceof MainActivity) {
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.subscriptions_label);
}
+ }
+ @Override
+ public void onStart() {
+ super.onStart();
EventDistributor.getInstance().register(contentUpdate);
+ loadSubscriptions();
}
@Override
- public void onDestroy() {
- super.onDestroy();
+ public void onStop() {
+ super.onStop();
+ EventDistributor.getInstance().unregister(contentUpdate);
if(disposable != null) {
disposable.dispose();
}
@@ -126,7 +175,7 @@ public class SubscriptionFragment extends Fragment {
Feed feed = (Feed)selectedObject;
- MenuInflater inflater = getActivity().getMenuInflater();
+ MenuInflater inflater = requireActivity().getMenuInflater();
inflater.inflate(R.menu.nav_feed_context, menu);
menu.setHeaderTitle(feed.getTitle());
@@ -136,7 +185,6 @@ public class SubscriptionFragment extends Fragment {
@Override
public boolean onContextItemSelected(MenuItem item) {
-
final int position = mPosition;
mPosition = -1; // reset
if(position < 0) {
@@ -151,84 +199,73 @@ public class SubscriptionFragment extends Fragment {
Feed feed = (Feed)selectedObject;
switch(item.getItemId()) {
- case R.id.mark_all_seen_item:
- ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(getActivity(),
- R.string.mark_all_seen_label,
- R.string.mark_all_seen_confirmation_msg) {
-
- @Override
- public void onConfirmButtonPressed(DialogInterface dialog) {
- dialog.dismiss();
-
- Observable.fromCallable(() -> DBWriter.markFeedSeen(feed.getId()))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> loadSubscriptions(),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
- }
- };
- markAllSeenConfirmationDialog.createNewDialog().show();
+ case R.id.remove_all_new_flags_item:
+ displayConfirmationDialog(
+ R.string.remove_all_new_flags_label,
+ R.string.remove_all_new_flags_confirmation_msg,
+ () -> DBWriter.removeFeedNewFlag(feed.getId()));
return true;
case R.id.mark_all_read_item:
- ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
+ displayConfirmationDialog(
R.string.mark_all_read_label,
- R.string.mark_all_read_confirmation_msg) {
-
- @Override
- public void onConfirmButtonPressed(DialogInterface dialog) {
- dialog.dismiss();
- Observable.fromCallable(() -> DBWriter.markFeedRead(feed.getId()))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> loadSubscriptions(),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
- }
- };
- markAllReadConfirmationDialog.createNewDialog().show();
+ R.string.mark_all_read_confirmation_msg,
+ () -> DBWriter.markFeedRead(feed.getId()));
return true;
case R.id.rename_item:
new RenameFeedDialog(getActivity(), feed).show();
return true;
case R.id.remove_item:
- final FeedRemover remover = new FeedRemover(getContext(), feed) {
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- loadSubscriptions();
- }
- };
- ConfirmationDialog conDialog = new ConfirmationDialog(getContext(),
- R.string.remove_feed_label,
- getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) {
- @Override
- public void onConfirmButtonPressed(
- DialogInterface dialog) {
- dialog.dismiss();
- long mediaId = PlaybackPreferences.getCurrentlyPlayingFeedMediaId();
- if (mediaId > 0 &&
- FeedItemUtil.indexOfItemWithMediaId(feed.getItems(), mediaId) >= 0) {
- Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
- remover.skipOnCompletion = true;
- int playerStatus = PlaybackPreferences.getCurrentPlayerStatus();
- if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) {
- IntentUtils.sendLocalBroadcast(getContext(), PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
-
- }
- }
- remover.executeAsync();
- }
- };
- conDialog.createNewDialog().show();
+ displayRemoveFeedDialog(feed);
return true;
default:
return super.onContextItemSelected(item);
}
}
- @Override
- public void onResume() {
- super.onResume();
- loadSubscriptions();
+ private void displayRemoveFeedDialog(Feed feed) {
+ final FeedRemover remover = new FeedRemover(getContext(), feed) {
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ loadSubscriptions();
+ }
+ };
+
+ String message = getString(R.string.feed_delete_confirmation_msg, feed.getTitle());
+ ConfirmationDialog dialog = new ConfirmationDialog(getContext(), R.string.remove_feed_label, message) {
+ @Override
+ public void onConfirmButtonPressed(DialogInterface clickedDialog) {
+ clickedDialog.dismiss();
+ long mediaId = PlaybackPreferences.getCurrentlyPlayingFeedMediaId();
+ if (mediaId > 0 && FeedItemUtil.indexOfItemWithMediaId(feed.getItems(), mediaId) >= 0) {
+ Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
+ remover.skipOnCompletion = true;
+ int playerStatus = PlaybackPreferences.getCurrentPlayerStatus();
+ if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) {
+ IntentUtils.sendLocalBroadcast(getContext(), PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+
+ }
+ }
+ remover.executeAsync();
+ }
+ };
+ dialog.createNewDialog().show();
+ }
+
+ private <T> void displayConfirmationDialog(@StringRes int title, @StringRes int message, Callable<? extends T> task) {
+ ConfirmationDialog dialog = new ConfirmationDialog(getActivity(), title, message) {
+ @Override
+ @SuppressLint("CheckResult")
+ public void onConfirmButtonPressed(DialogInterface clickedDialog) {
+ clickedDialog.dismiss();
+ Observable.fromCallable(task)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> loadSubscriptions(),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+ };
+ dialog.createNewDialog().show();
}
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
new file mode 100644
index 000000000..6cba798ba
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
@@ -0,0 +1,184 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String TAG = "AutoDnldPrefFragment";
+ private CheckBoxPreference[] selectedNetworks;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_autodownload);
+
+ setupAutoDownloadScreen();
+ buildAutodownloadSelectedNetworksPreference();
+ setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter());
+ buildEpisodeCleanupPreference();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ checkAutodownloadItemVisibility(UserPreferences.isEnableAutodownload());
+ }
+
+ private void setupAutoDownloadScreen() {
+ findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ checkAutodownloadItemVisibility((Boolean) newValue);
+ }
+ return true;
+ });
+ findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
+ .setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ setSelectedNetworksEnabled((Boolean) newValue);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ );
+ }
+
+ private void checkAutodownloadItemVisibility(boolean autoDownload) {
+ findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setEnabled(autoDownload);
+ findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled(autoDownload);
+ findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled(autoDownload);
+ findPreference(UserPreferences.PREF_EPISODE_CLEANUP).setEnabled(autoDownload);
+ setSelectedNetworksEnabled(autoDownload && UserPreferences.isEnableAutodownloadWifiFilter());
+ }
+
+ private static String blankIfNull(String val) {
+ return val == null ? "" : val;
+ }
+
+ private void buildAutodownloadSelectedNetworksPreference() {
+ final Activity activity = getActivity();
+
+ if (selectedNetworks != null) {
+ clearAutodownloadSelectedNetworsPreference();
+ }
+ // get configured networks
+ WifiManager wifiservice = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ List<WifiConfiguration> networks = wifiservice.getConfiguredNetworks();
+
+ if (networks == null) {
+ Log.e(TAG, "Couldn't get list of configure Wi-Fi networks");
+ return;
+ }
+ Collections.sort(networks, (x, y) ->
+ blankIfNull(x.SSID).compareTo(blankIfNull(y.SSID)));
+ selectedNetworks = new CheckBoxPreference[networks.size()];
+ List<String> prefValues = Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks());
+ PreferenceScreen prefScreen = getPreferenceScreen();
+ Preference.OnPreferenceClickListener clickListener = preference -> {
+ if (preference instanceof CheckBoxPreference) {
+ String key = preference.getKey();
+ List<String> prefValuesList = new ArrayList<>(
+ Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks())
+ );
+ boolean newValue = ((CheckBoxPreference) preference)
+ .isChecked();
+ Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
+
+ int index = prefValuesList.indexOf(key);
+ if (index >= 0 && !newValue) {
+ // remove network
+ prefValuesList.remove(index);
+ } else if (index < 0 && newValue) {
+ prefValuesList.add(key);
+ }
+
+ UserPreferences.setAutodownloadSelectedNetworks(
+ prefValuesList.toArray(new String[prefValuesList.size()])
+ );
+ return true;
+ } else {
+ return false;
+ }
+ };
+ // create preference for each known network. attach listener and set
+ // value
+ for (int i = 0; i < networks.size(); i++) {
+ WifiConfiguration config = networks.get(i);
+
+ CheckBoxPreference pref = new CheckBoxPreference(activity);
+ String key = Integer.toString(config.networkId);
+ pref.setTitle(config.SSID);
+ pref.setKey(key);
+ pref.setOnPreferenceClickListener(clickListener);
+ pref.setPersistent(false);
+ pref.setChecked(prefValues.contains(key));
+ selectedNetworks[i] = pref;
+ prefScreen.addPreference(pref);
+ }
+ }
+
+ private void clearAutodownloadSelectedNetworsPreference() {
+ if (selectedNetworks != null) {
+ PreferenceScreen prefScreen = getPreferenceScreen();
+
+ for (CheckBoxPreference network : selectedNetworks) {
+ if (network != null) {
+ prefScreen.removePreference(network);
+ }
+ }
+ }
+ }
+
+ private void buildEpisodeCleanupPreference() {
+ final Resources res = getActivity().getResources();
+
+ ListPreference pref = (ListPreference) findPreference(UserPreferences.PREF_EPISODE_CLEANUP);
+ String[] values = res.getStringArray(
+ R.array.episode_cleanup_values);
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ int v = Integer.parseInt(values[x]);
+ if (v == UserPreferences.EPISODE_CLEANUP_QUEUE) {
+ entries[x] = res.getString(R.string.episode_cleanup_queue_removal);
+ } else if (v == UserPreferences.EPISODE_CLEANUP_NULL){
+ entries[x] = res.getString(R.string.episode_cleanup_never);
+ } else if (v == 0) {
+ entries[x] = res.getString(R.string.episode_cleanup_after_listening);
+ } else if (v > 0 && v < 24) {
+ entries[x] = res.getQuantityString(R.plurals.episode_cleanup_hours_after_listening, v, v);
+ } else {
+ int numDays = v / 24; // assume underlying value will be NOT fraction of days, e.g., 36 (hours)
+ entries[x] = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, numDays, numDays);
+ }
+ }
+ pref.setEntries(entries);
+ }
+
+ private void setSelectedNetworksEnabled(boolean b) {
+ if (selectedNetworks != null) {
+ for (Preference p : selectedNetworks) {
+ p.setEnabled(b);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
new file mode 100644
index 000000000..491922056
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
@@ -0,0 +1,144 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.text.Html;
+import android.text.format.DateUtils;
+import android.widget.Toast;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.service.GpodnetSyncService;
+import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
+
+public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
+ private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
+ private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
+ private static final String PREF_GPODNET_FORCE_FULL_SYNC = "pref_gpodnet_force_full_sync";
+ private static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
+ private static final String PREF_GPODNET_HOSTNAME = "pref_gpodnet_hostname";
+ private static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_gpodder);
+ setupGpodderScreen();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener);
+ updateGpodnetPreferenceScreen();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ GpodnetPreferences.unregisterOnSharedPreferenceChangeListener(gpoddernetListener);
+ }
+
+ private final SharedPreferences.OnSharedPreferenceChangeListener gpoddernetListener =
+ (sharedPreferences, key) -> {
+ if (GpodnetPreferences.PREF_LAST_SYNC_ATTEMPT_TIMESTAMP.equals(key)) {
+ updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
+ GpodnetPreferences.getLastSyncAttemptTimestamp());
+ }
+ };
+
+ private void setupGpodderScreen() {
+ final Activity activity = getActivity();
+
+ findPreference(PREF_GPODNET_SETLOGIN_INFORMATION)
+ .setOnPreferenceClickListener(preference -> {
+ AuthenticationDialog dialog = new AuthenticationDialog(activity,
+ R.string.pref_gpodnet_setlogin_information_title, false, false, GpodnetPreferences.getUsername(),
+ null) {
+
+ @Override
+ protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
+ GpodnetPreferences.setPassword(password);
+ }
+ };
+ dialog.show();
+ return true;
+ });
+ findPreference(PREF_GPODNET_SYNC).
+ setOnPreferenceClickListener(preference -> {
+ GpodnetSyncService.sendSyncIntent(getActivity().getApplicationContext());
+ Toast toast = Toast.makeText(getActivity(), R.string.pref_gpodnet_sync_started,
+ Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
+ });
+ findPreference(PREF_GPODNET_FORCE_FULL_SYNC).
+ setOnPreferenceClickListener(preference -> {
+ GpodnetPreferences.setLastSubscriptionSyncTimestamp(0L);
+ GpodnetPreferences.setLastEpisodeActionsSyncTimestamp(0L);
+ GpodnetPreferences.setLastSyncAttempt(false, 0);
+ updateLastGpodnetSyncReport(false, 0);
+ GpodnetSyncService.sendSyncIntent(getActivity().getApplicationContext());
+ Toast toast = Toast.makeText(getActivity(), R.string.pref_gpodnet_sync_started,
+ Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
+ });
+ findPreference(PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(
+ preference -> {
+ GpodnetPreferences.logout();
+ Toast toast = Toast.makeText(activity, R.string.pref_gpodnet_logout_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ updateGpodnetPreferenceScreen();
+ return true;
+ });
+ findPreference(PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(
+ preference -> {
+ GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(dialog -> updateGpodnetPreferenceScreen());
+ return true;
+ });
+ }
+
+ private void updateGpodnetPreferenceScreen() {
+ final boolean loggedIn = GpodnetPreferences.loggedIn();
+ findPreference(PREF_GPODNET_LOGIN).setEnabled(!loggedIn);
+ findPreference(PREF_GPODNET_SETLOGIN_INFORMATION).setEnabled(loggedIn);
+ findPreference(PREF_GPODNET_SYNC).setEnabled(loggedIn);
+ findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setEnabled(loggedIn);
+ findPreference(PREF_GPODNET_LOGOUT).setEnabled(loggedIn);
+ findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(loggedIn);
+ if(loggedIn) {
+ String format = getActivity().getString(R.string.pref_gpodnet_login_status);
+ String summary = String.format(format, GpodnetPreferences.getUsername(),
+ GpodnetPreferences.getDeviceID());
+ findPreference(PREF_GPODNET_LOGOUT).setSummary(Html.fromHtml(summary));
+ updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
+ GpodnetPreferences.getLastSyncAttemptTimestamp());
+ } else {
+ findPreference(PREF_GPODNET_LOGOUT).setSummary(null);
+ updateLastGpodnetSyncReport(false, 0);
+ }
+ findPreference(PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname());
+ }
+
+ private void updateLastGpodnetSyncReport(boolean successful, long lastTime) {
+ Preference sync = findPreference(PREF_GPODNET_SYNC);
+ if (lastTime != 0) {
+ sync.setSummary(getActivity().getString(R.string.pref_gpodnet_sync_changes_sum) + "\n" +
+ getActivity().getString(R.string.pref_gpodnet_sync_sum_last_sync_line,
+ getActivity().getString(successful ?
+ R.string.gpodnetsync_pref_report_successful :
+ R.string.gpodnetsync_pref_report_failed),
+ DateUtils.getRelativeDateTimeString(getActivity(),
+ lastTime,
+ DateUtils.MINUTE_IN_MILLIS,
+ DateUtils.WEEK_IN_MILLIS,
+ DateUtils.FORMAT_SHOW_TIME)));
+ } else {
+ sync.setSummary(getActivity().getString(R.string.pref_gpodnet_sync_changes_sum));
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
new file mode 100644
index 000000000..229274b76
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
@@ -0,0 +1,23 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+
+public class IntegrationsPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_SCREEN_GPODDER = "prefGpodderSettings";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_integrations);
+ setupIntegrationsScreen();
+ }
+
+ private void setupIntegrationsScreen() {
+ findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_gpodder);
+ return true;
+ });
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
new file mode 100644
index 000000000..701d21ce0
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
@@ -0,0 +1,147 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.content.FileProvider;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.util.Log;
+import android.widget.Toast;
+import com.bytehamster.lib.preferencesearch.SearchConfiguration;
+import com.bytehamster.lib.preferencesearch.SearchPreference;
+import de.danoeh.antennapod.CrashReportWriter;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.AboutActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.activity.StatisticsActivity;
+
+import java.util.List;
+
+public class MainPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String TAG = "MainPreferencesFragment";
+
+ private static final String PREF_SCREEN_USER_INTERFACE = "prefScreenInterface";
+ private static final String PREF_SCREEN_PLAYBACK = "prefScreenPlayback";
+ private static final String PREF_SCREEN_NETWORK = "prefScreenNetwork";
+ private static final String PREF_SCREEN_INTEGRATIONS = "prefScreenIntegrations";
+ private static final String PREF_SCREEN_STORAGE = "prefScreenStorage";
+ private static final String PREF_KNOWN_ISSUES = "prefKnownIssues";
+ private static final String PREF_FAQ = "prefFaq";
+ private static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport";
+ private static final String STATISTICS = "statistics";
+ private static final String PREF_ABOUT = "prefAbout";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences);
+ setupMainScreen();
+ setupSearch();
+ }
+
+ private void setupMainScreen() {
+ findPreference(PREF_SCREEN_USER_INTERFACE).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_user_interface);
+ return true;
+ });
+ findPreference(PREF_SCREEN_PLAYBACK).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_playback);
+ return true;
+ });
+ findPreference(PREF_SCREEN_NETWORK).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_network);
+ return true;
+ });
+ findPreference(PREF_SCREEN_INTEGRATIONS).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_integrations);
+ return true;
+ });
+ findPreference(PREF_SCREEN_STORAGE).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_storage);
+ return true;
+ });
+
+ findPreference(PREF_ABOUT).setOnPreferenceClickListener(
+ preference -> {
+ startActivity(new Intent(getActivity(), AboutActivity.class));
+ return true;
+ }
+ );
+ findPreference(STATISTICS).setOnPreferenceClickListener(
+ preference -> {
+ startActivity(new Intent(getActivity(), StatisticsActivity.class));
+ return true;
+ }
+ );
+ findPreference(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> {
+ openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug");
+ return true;
+ });
+ findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
+ openInBrowser("http://antennapod.org/faq.html");
+ return true;
+ });
+ findPreference(PREF_SEND_CRASH_REPORT).setOnPreferenceClickListener(preference -> {
+ Context context = getActivity().getApplicationContext();
+ Intent emailIntent = new Intent(Intent.ACTION_SEND);
+ emailIntent.setType("text/plain");
+ emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"});
+ emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report");
+ emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed");
+ // the attachment
+ Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority),
+ CrashReportWriter.getFile());
+ emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ emailIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ String intentTitle = getActivity().getString(R.string.send_email);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
+ return true;
+ });
+ }
+
+ private void openInBrowser(String url) {
+ try {
+ Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ startActivity(myIntent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(getActivity(), R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+
+ private void setupSearch() {
+ SearchPreference searchPreference = (SearchPreference) findPreference("searchPreference");
+ SearchConfiguration config = searchPreference.getSearchConfiguration();
+ config.setActivity((AppCompatActivity) getActivity());
+ config.setFragmentContainerViewId(R.id.content);
+ config.setBreadcrumbsEnabled(true);
+
+ config.index(R.xml.preferences_user_interface)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_user_interface));
+ config.index(R.xml.preferences_playback)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_playback));
+ config.index(R.xml.preferences_network)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_network));
+ config.index(R.xml.preferences_storage)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_storage));
+ config.index(R.xml.preferences_autodownload)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_network))
+ .addBreadcrumb(R.string.automation)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_autodownload));
+ config.index(R.xml.preferences_gpodder)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_integrations))
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_gpodder));
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
new file mode 100644
index 000000000..ac2436e25
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
@@ -0,0 +1,175 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.TimePickerDialog;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.text.format.DateFormat;
+import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.dialog.ProxyDialog;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.concurrent.TimeUnit;
+
+public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_SCREEN_AUTODL = "prefAutoDownloadSettings";
+ private static final String PREF_PROXY = "prefProxy";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_network);
+ setupNetworkScreen();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ setUpdateIntervalText();
+ setParallelDownloadsText(UserPreferences.getParallelDownloads());
+ }
+
+ private void setupNetworkScreen() {
+ findPreference(PREF_SCREEN_AUTODL).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_autodownload);
+ return true;
+ });
+ findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
+ .setOnPreferenceClickListener(preference -> {
+ showUpdateIntervalTimePreferencesDialog();
+ return true;
+ });
+ findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)
+ .setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof Integer) {
+ setParallelDownloadsText((Integer) o);
+ }
+ return true;
+ }
+ );
+ // validate and set correct value: number of downloads between 1 and 50 (inclusive)
+ findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> {
+ ProxyDialog dialog = new ProxyDialog(getActivity());
+ dialog.createDialog().show();
+ return true;
+ });
+ }
+
+ private void setUpdateIntervalText() {
+ Context context = getActivity().getApplicationContext();
+ String val;
+ long interval = UserPreferences.getUpdateInterval();
+ if(interval > 0) {
+ int hours = (int) TimeUnit.MILLISECONDS.toHours(interval);
+ String hoursStr = context.getResources().getQuantityString(R.plurals.time_hours_quantified, hours, hours);
+ val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_every), hoursStr);
+ } else {
+ int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
+ if(timeOfDay.length == 2) {
+ Calendar cal = new GregorianCalendar();
+ cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]);
+ cal.set(Calendar.MINUTE, timeOfDay[1]);
+ String timeOfDayStr = DateFormat.getTimeFormat(context).format(cal.getTime());
+ val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_at),
+ timeOfDayStr);
+ } else {
+ val = context.getString(R.string.pref_smart_mark_as_played_disabled); // TODO: Is this a bug? Otherwise document why is this related to smart mark???
+ }
+ }
+ String summary = context.getString(R.string.pref_autoUpdateIntervallOrTime_sum) + "\n"
+ + String.format(context.getString(R.string.pref_current_value), val);
+ findPreference(UserPreferences.PREF_UPDATE_INTERVAL).setSummary(summary);
+ }
+
+ private void setParallelDownloadsText(int downloads) {
+ final Resources res = getActivity().getResources();
+
+ String s = Integer.toString(downloads)
+ + res.getString(R.string.parallel_downloads_suffix);
+ findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS).setSummary(s);
+ }
+
+ private void showUpdateIntervalTimePreferencesDialog() {
+ final Context context = getActivity();
+
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
+ builder.title(R.string.pref_autoUpdateIntervallOrTime_title);
+ builder.content(R.string.pref_autoUpdateIntervallOrTime_message);
+ builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
+ builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
+ builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
+ builder.onPositive((dialog, which) -> {
+ AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
+ builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
+ final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
+ final String[] entries = getUpdateIntervalEntries(values);
+ long currInterval = UserPreferences.getUpdateInterval();
+ int checkedItem = -1;
+ if(currInterval > 0) {
+ String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
+ checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
+ }
+ builder1.setSingleChoiceItems(entries, checkedItem, (dialog1, which1) -> {
+ int hours = Integer.parseInt(values[which1]);
+ UserPreferences.setUpdateInterval(hours);
+ dialog1.dismiss();
+ setUpdateIntervalText();
+ });
+ builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
+ builder1.show();
+ });
+ builder.onNegative((dialog, which) -> {
+ int hourOfDay = 7, minute = 0;
+ int[] updateTime = UserPreferences.getUpdateTimeOfDay();
+ if (updateTime.length == 2) {
+ hourOfDay = updateTime[0];
+ minute = updateTime[1];
+ }
+ TimePickerDialog timePickerDialog = new TimePickerDialog(context,
+ (view, selectedHourOfDay, selectedMinute) -> {
+ if (view.getTag() == null) { // onTimeSet() may get called twice!
+ view.setTag("TAGGED");
+ UserPreferences.setUpdateTimeOfDay(selectedHourOfDay, selectedMinute);
+ setUpdateIntervalText();
+ }
+ }, hourOfDay, minute, DateFormat.is24HourFormat(context));
+ timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
+ timePickerDialog.show();
+ });
+ builder.onNeutral((dialog, which) -> {
+ UserPreferences.disableAutoUpdate();
+ setUpdateIntervalText();
+ });
+ builder.show();
+ }
+
+ private String[] getUpdateIntervalEntries(final String[] values) {
+ final Resources res = getActivity().getResources();
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ Integer v = Integer.parseInt(values[x]);
+ switch (v) {
+ case 0:
+ entries[x] = res.getString(R.string.pref_update_interval_hours_manual);
+ break;
+ case 1:
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_singular);
+ break;
+ default:
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_plural);
+ break;
+
+ }
+ }
+ return entries;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
new file mode 100644
index 000000000..e1714d4bd
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
@@ -0,0 +1,92 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MediaplayerActivity;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
+import de.danoeh.antennapod.dialog.VariableSpeedDialog;
+import de.danoeh.antennapod.preferences.PreferenceControllerFlavorHelper;
+
+public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher";
+ private static final String PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher";
+ private static final String PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_playback);
+
+ setupPlaybackScreen();
+ PreferenceControllerFlavorHelper.setupFlavoredUI(this);
+ buildSmartMarkAsPlayedPreference();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ checkSonicItemVisibility();
+ }
+
+ private void setupPlaybackScreen() {
+ final Activity activity = getActivity();
+
+ findPreference(PREF_PLAYBACK_SPEED_LAUNCHER)
+ .setOnPreferenceClickListener(preference -> {
+ VariableSpeedDialog.showDialog(activity);
+ return true;
+ });
+ findPreference(PREF_PLAYBACK_REWIND_DELTA_LAUNCHER)
+ .setOnPreferenceClickListener(preference -> {
+ MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_REWIND);
+ return true;
+ });
+ findPreference(PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER)
+ .setOnPreferenceClickListener(preference -> {
+ MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_FORWARD);
+ return true;
+ });
+ if (!PictureInPictureUtil.supportsPictureInPicture(activity)) {
+ ListPreference behaviour = (ListPreference) findPreference(UserPreferences.PREF_VIDEO_BEHAVIOR);
+ behaviour.setEntries(R.array.video_background_behavior_options_without_pip);
+ behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip);
+ }
+ }
+
+ private void buildSmartMarkAsPlayedPreference() {
+ final Resources res = getActivity().getResources();
+
+ ListPreference pref = (ListPreference) findPreference(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS);
+ String[] values = res.getStringArray(R.array.smart_mark_as_played_values);
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ if(x == 0) {
+ entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled);
+ } else {
+ Integer v = Integer.parseInt(values[x]);
+ if(v < 60) {
+ entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v, v);
+ } else {
+ v /= 60;
+ entries[x] = res.getQuantityString(R.plurals.time_minutes_quantified, v, v);
+ }
+ }
+ }
+ pref.setEntries(entries);
+ }
+
+
+
+ private void checkSonicItemVisibility() {
+ if (Build.VERSION.SDK_INT < 16) {
+ ListPreference p = (ListPreference) findPreference(UserPreferences.PREF_MEDIA_PLAYER);
+ p.setEntries(R.array.media_player_options_no_sonic);
+ p.setEntryValues(R.array.media_player_values_no_sonic);
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
new file mode 100644
index 000000000..b4226b546
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
@@ -0,0 +1,244 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.FileProvider;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.util.Log;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.DirectoryChooserActivity;
+import de.danoeh.antennapod.activity.ImportExportActivity;
+import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
+import de.danoeh.antennapod.asynctask.ExportWorker;
+import de.danoeh.antennapod.core.export.ExportWriter;
+import de.danoeh.antennapod.core.export.html.HtmlWriter;
+import de.danoeh.antennapod.core.export.opml.OpmlWriter;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.File;
+import java.util.List;
+
+public class StoragePreferencesFragment extends PreferenceFragmentCompat {
+ private static final String TAG = "StoragePrefFragment";
+ private static final String PREF_OPML_EXPORT = "prefOpmlExport";
+ private static final String PREF_OPML_IMPORT = "prefOpmlImport";
+ private static final String PREF_HTML_EXPORT = "prefHtmlExport";
+ private static final String IMPORT_EXPORT = "importExport";
+ private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
+ private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE };
+ private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
+ private Disposable disposable;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_storage);
+ setupStorageScreen();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ setDataFolderText();
+ }
+
+ private void setupStorageScreen() {
+ final Activity activity = getActivity();
+
+ findPreference(IMPORT_EXPORT).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, ImportExportActivity.class));
+ return true;
+ }
+ );
+ findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
+ preference -> export(new OpmlWriter()));
+ findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
+ preference -> export(new HtmlWriter()));
+ findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
+ return true;
+ });
+ findPreference(PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
+ preference -> {
+ if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ showChooseDataFolderDialog();
+ } else {
+ int readPermission = ActivityCompat.checkSelfPermission(
+ activity, Manifest.permission.READ_EXTERNAL_STORAGE);
+ int writePermission = ActivityCompat.checkSelfPermission(
+ activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (readPermission == PackageManager.PERMISSION_GRANTED &&
+ writePermission == PackageManager.PERMISSION_GRANTED) {
+ openDirectoryChooser();
+ } else {
+ requestPermission();
+ }
+ }
+ return true;
+ }
+ );
+ findPreference(PREF_CHOOSE_DATA_DIR)
+ .setOnPreferenceClickListener(
+ preference -> {
+ if (Build.VERSION.SDK_INT >= 19) {
+ showChooseDataFolderDialog();
+ } else {
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent,
+ DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+ return true;
+ }
+ );
+ findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE).setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof String) {
+ int newValue = Integer.parseInt((String) o) * 1024 * 1024;
+ if (newValue != UserPreferences.getImageCacheSize()) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
+ }
+ return true;
+ }
+ return false;
+ }
+ );
+ }
+
+ private boolean export(ExportWriter exportWriter) {
+ Context context = getActivity();
+ final ProgressDialog progressDialog = new ProgressDialog(context);
+ progressDialog.setMessage(context.getString(R.string.exporting_label));
+ progressDialog.setIndeterminate(true);
+ progressDialog.show();
+ final AlertDialog.Builder alert = new AlertDialog.Builder(context)
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
+ disposable = observable.subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output -> {
+ alert.setTitle(R.string.export_success_title);
+ String message = context.getString(R.string.export_success_sum, output.toString());
+ alert.setMessage(message);
+ alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
+ Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
+ context.getString(R.string.provider_authority), output);
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT,
+ context.getResources().getText(R.string.opml_export_label));
+ sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ sendIntent.setType("text/plain");
+ sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ context.startActivity(Intent.createChooser(sendIntent,
+ context.getResources().getText(R.string.send_label)));
+ });
+ alert.create().show();
+ }, error -> {
+ alert.setTitle(R.string.export_error_label);
+ alert.setMessage(error.getMessage());
+ alert.show();
+ }, progressDialog::dismiss);
+ return true;
+ }
+
+ public void unsubscribeExportSubscription() {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ @SuppressLint("NewApi")
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
+
+ File path;
+ if(dir != null) {
+ path = new File(dir);
+ } else {
+ path = getActivity().getExternalFilesDir(null);
+ }
+ String message = null;
+ final Context context= getActivity().getApplicationContext();
+ if(!path.exists()) {
+ message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(context.getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(context.getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ setDataFolderText();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(getActivity());
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
+ }
+ }
+
+ private void setDataFolderText() {
+ File f = UserPreferences.getDataFolder(null);
+ if (f != null) {
+ findPreference(PREF_CHOOSE_DATA_DIR)
+ .setSummary(f.getAbsolutePath());
+ }
+ }
+
+ private void requestPermission() {
+ ActivityCompat.requestPermissions(getActivity(), EXTERNAL_STORAGE_PERMISSIONS,
+ PERMISSION_REQUEST_EXTERNAL_STORAGE);
+ }
+
+ private void openDirectoryChooser() {
+ Activity activity = getActivity();
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+
+ private void showChooseDataFolderDialog() {
+ ChooseDataFolderDialog.showDialog(
+ getActivity(), new ChooseDataFolderDialog.RunnableWithString() {
+ @Override
+ public void run(final String folder) {
+ UserPreferences.setDataFolder(folder);
+ setDataFolderText();
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
new file mode 100644
index 000000000..e1d44f7d3
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
@@ -0,0 +1,165 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.widget.ListView;
+import android.widget.Toast;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.List;
+
+public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_user_interface);
+ setupInterfaceScreen();
+ }
+
+ private void setupInterfaceScreen() {
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ // disable expanded notification option on unsupported android versions
+ findPreference(PREF_EXPANDED_NOTIFICATION).setEnabled(false);
+ findPreference(PREF_EXPANDED_NOTIFICATION).setOnPreferenceClickListener(
+ preference -> {
+ Toast toast = Toast.makeText(getActivity(),
+ R.string.pref_expand_notify_unsupport_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
+ }
+ );
+ }
+ findPreference(UserPreferences.PREF_THEME)
+ .setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ Intent i = new Intent(getActivity(), MainActivity.class);
+ i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ getActivity().finish();
+ startActivity(i);
+ return true;
+ }
+ );
+ findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
+ .setOnPreferenceClickListener(preference -> {
+ showDrawerPreferencesDialog();
+ return true;
+ });
+
+ findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS)
+ .setOnPreferenceClickListener(preference -> {
+ showNotificationButtonsDialog();
+ return true;
+ });
+
+ findPreference(UserPreferences.PREF_BACK_BUTTON_BEHAVIOR)
+ .setOnPreferenceChangeListener((preference, newValue) -> {
+ if (newValue.equals("page")) {
+ final Context context = getActivity();
+ final String[] navTitles = context.getResources().getStringArray(R.array.back_button_go_to_pages);
+ final String[] navTags = context.getResources().getStringArray(R.array.back_button_go_to_pages_tags);
+ final String choice[] = { UserPreferences.getBackButtonGoToPage() };
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.back_button_go_to_page_title);
+ builder.setSingleChoiceItems(navTitles, ArrayUtils.indexOf(navTags, UserPreferences.getBackButtonGoToPage()), (dialogInterface, i) -> {
+ if (i >= 0) {
+ choice[0] = navTags[i];
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialogInterface, i) -> UserPreferences.setBackButtonGoToPage(choice[0]));
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ return true;
+ } else {
+ return true;
+ }
+ });
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ findPreference(UserPreferences.PREF_EXPANDED_NOTIFICATION).setVisible(false);
+ }
+ }
+
+ private void showDrawerPreferencesDialog() {
+ final Context context = getActivity();
+ final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
+ final String[] navTitles = context.getResources().getStringArray(R.array.nav_drawer_titles);
+ final String[] NAV_DRAWER_TAGS = MainActivity.NAV_DRAWER_TAGS;
+ boolean[] checked = new boolean[MainActivity.NAV_DRAWER_TAGS.length];
+ for(int i=0; i < NAV_DRAWER_TAGS.length; i++) {
+ String tag = NAV_DRAWER_TAGS[i];
+ if(!hiddenDrawerItems.contains(tag)) {
+ checked[i] = true;
+ }
+ }
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.drawer_preferences);
+ builder.setMultiChoiceItems(navTitles, checked, (dialog, which, isChecked) -> {
+ if (isChecked) {
+ hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]);
+ } else {
+ hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]);
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) ->
+ UserPreferences.setHiddenDrawerItems(hiddenDrawerItems));
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
+
+ private void showNotificationButtonsDialog() {
+ final Context context = getActivity();
+ final List<Integer> preferredButtons = UserPreferences.getCompactNotificationButtons();
+ final String[] allButtonNames = context.getResources().getStringArray(
+ R.array.compact_notification_buttons_options);
+ boolean[] checked = new boolean[allButtonNames.length]; // booleans default to false in java
+
+ for(int i=0; i < checked.length; i++) {
+ if(preferredButtons.contains(i)) {
+ checked[i] = true;
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(String.format(context.getResources().getString(
+ R.string.pref_compact_notification_buttons_dialog_title), 2));
+ builder.setMultiChoiceItems(allButtonNames, checked, (dialog, which, isChecked) -> {
+ checked[which] = isChecked;
+
+ if (isChecked) {
+ if (preferredButtons.size() < 2) {
+ preferredButtons.add(which);
+ } else {
+ // Only allow a maximum of two selections. This is because the notification
+ // on the lock screen can only display 3 buttons, and the play/pause button
+ // is always included.
+ checked[which] = false;
+ ListView selectionView = ((AlertDialog) dialog).getListView();
+ selectionView.setItemChecked(which, false);
+ Snackbar.make(
+ selectionView,
+ String.format(context.getResources().getString(
+ R.string.pref_compact_notification_buttons_dialog_error), 2),
+ Snackbar.LENGTH_SHORT).show();
+ }
+ } else {
+ preferredButtons.remove((Integer) which);
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) ->
+ UserPreferences.setCompactNotificationButtons(preferredButtons));
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
+}