Reorganize Episode Fragments lifecycle

This uses the existing android lifecycle methods to avoid having to do
null checks and state saving in various places.
This commit is contained in:
Anderson Mesquita 2019-05-26 07:44:57 -04:00
parent aabe370db3
commit bbcec5d0aa
4 changed files with 83 additions and 147 deletions

View File

@ -29,6 +29,7 @@ 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;
@ -43,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;
@ -80,11 +82,10 @@ public class AllEpisodesFragment extends Fragment {
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;
@ -100,26 +101,18 @@ public class AllEpisodesFragment extends Fragment {
return DEFAULT_PREF_NAME;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@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();
loadItems();
registerForContextMenu(recyclerView);
}
@ -140,12 +133,6 @@ public class AllEpisodesFragment extends Fragment {
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
resetViewState();
}
private void saveScrollPosition() {
int firstItem = layoutManager.findFirstVisibleItemPosition();
View firstItemView = layoutManager.findViewByPosition(firstItem);
@ -177,12 +164,6 @@ public class AllEpisodesFragment extends Fragment {
}
}
void resetViewState() {
viewsCreated = false;
listAdapter = null;
}
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
@ -192,28 +173,26 @@ public class AllEpisodesFragment extends Fragment {
return;
}
super.onCreateOptionsMenu(menu, inflater);
if (itemsLoaded) {
inflater.inflate(R.menu.episodes, menu);
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;
}
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
@ -221,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());
markAllSeen.setVisible(showOnlyNewEpisodes() && !episodes.isEmpty());
}
}
@ -286,7 +265,7 @@ public class AllEpisodesFragment extends Fragment {
return true; // avoids that the position is reset when we need it in the submenu
}
if (listAdapter == null || listAdapter.getSelectedItem() == null) {
if (listAdapter.getSelectedItem() == null) {
Log.i(TAG, "Selected item or listAdapter was null, ignoring selection");
return super.onContextItemSelected(item);
}
@ -305,85 +284,74 @@ public class AllEpisodesFragment extends Fragment {
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);
if (!itemsLoaded) {
progLoading.setVisibility(View.VISIBLE);
}
viewsCreated = true;
if (itemsLoaded) {
onFragmentLoaded();
}
progLoading.setVisibility(View.VISIBLE);
emptyView = new EmptyViewHandler(getContext());
emptyView.attachToRecyclerView(recyclerView);
emptyView.setTitle(R.string.no_all_episodes_head_label);
emptyView.setMessage(R.string.no_all_episodes_label);
emptyView.hide();
createRecycleAdapter();
return root;
}
private void onFragmentLoaded() {
if (episodes != null && episodes.size() > 0) {
if (listAdapter == null) {
MainActivity mainActivity = (MainActivity) getActivity();
listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes());
listAdapter.setHasStableIds(true);
recyclerView.setAdapter(listAdapter);
emptyView.updateAdapter(listAdapter);
}
if (episodes.size() > 0) {
recyclerView.setVisibility(View.VISIBLE);
listAdapter.notifyDataSetChanged();
} else {
listAdapter = null;
createRecycleAdapter();
recyclerView.setVisibility(View.GONE);
emptyView.updateAdapter(listAdapter);
}
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() {
MainActivity mainActivity = (MainActivity) getActivity();
listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes());
listAdapter.setHasStableIds(true);
recyclerView.setAdapter(listAdapter);
emptyView.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;
@ -391,9 +359,6 @@ 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) {
ids.add(episode.getId());
@ -403,12 +368,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;
@ -422,9 +386,6 @@ 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)) {
queueIds.add(item.getId());
@ -438,12 +399,6 @@ 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) {
return;
} else if (listAdapter == null) {
loadItems();
return;
}
for (FeedItem item : event.items) {
int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
if (pos >= 0) {
@ -468,11 +423,7 @@ public class AllEpisodesFragment extends Fragment {
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
getActivity().supportInvalidateOptionsMenu();
}
if (listAdapter == null) {
loadItems();
return;
requireActivity().invalidateOptionsMenu();
}
if (update.mediaIds.length > 0) {
for (long mediaId : update.mediaIds) {
@ -490,24 +441,16 @@ 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);
emptyView.hide();
progLoading.setVisibility(View.VISIBLE);
}
disposable = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@ -515,10 +458,7 @@ public class AllEpisodesFragment extends Fragment {
recyclerView.setVisibility(View.VISIBLE);
progLoading.setVisibility(View.GONE);
episodes = data;
itemsLoaded = true;
if (viewsCreated) {
onFragmentLoaded();
}
onFragmentLoaded();
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}

View File

@ -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;
@ -27,7 +28,6 @@ import de.danoeh.antennapod.core.storage.DBWriter;
public class FavoriteEpisodesFragment extends AllEpisodesFragment {
private static final String TAG = "FavoriteEpisodesFrag";
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";
@Override
@ -42,19 +42,14 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
@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.setTitle(R.string.no_fav_episodes_head_label);
emptyView.setMessage(R.string.no_fav_episodes_label);
@ -67,7 +62,7 @@ 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() + ")");
Log.d(TAG, String.format("remove(%s)", holder.getItemId()));
if (disposable != null) {
disposable.dispose();
@ -88,6 +83,7 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
return root;
}
@NonNull
@Override
protected List<FeedItem> loadData() {
return DBReader.getFavoriteItemsList();

View File

@ -1,6 +1,7 @@
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.view.LayoutInflater;
@ -33,20 +34,15 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
return PREF_NAME;
}
@Override
protected void resetViewState() {
super.resetViewState();
}
@Override
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);
@ -96,6 +92,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
return root;
}
@NonNull
@Override
protected List<FeedItem> loadData() {
return DBReader.getNewItemsList();

View File

@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.event;
import android.support.annotation.NonNull;
import java.util.Arrays;
import java.util.List;
@ -11,6 +13,7 @@ import de.danoeh.antennapod.core.util.LongList;
public class DownloaderUpdate {
/* Downloaders that are currently running */
@NonNull
public final List<Downloader> downloaders;
/**
@ -25,7 +28,7 @@ public class DownloaderUpdate {
*/
public final long[] mediaIds;
public DownloaderUpdate(List<Downloader> downloaders) {
DownloaderUpdate(@NonNull List<Downloader> downloaders) {
this.downloaders = downloaders;
LongList feedIds1 = new LongList(), mediaIds1 = new LongList();
for(Downloader d1 : downloaders) {