Reorganize downloads fragments lifecycle

Unregistering from the EventDistributor on stop will prevent downloads
from updating when an episode finishes playing while the screen was off
(#2747), so this registers/unregisters on view create/destroy.

Disposing of the request to load items on stop could potentially cause
the same issue. Since we're disposing of this request on destroy,
there's no need to keep checking and disposing of it in the several
lifecycle methods.

There's no need to call `onFragmentLoaded()` on attach, since this is
the first lifecycle method to be called [[1]], meaning the items will
always be null by the time this method is called.

Finally, since `loadItems` depends on the view being created, it is now
only called on view create to avoid having to store state in the class
about whether the view has been created, taking advantage of the native
fragment lifecycle.

[1]: https://developer.android.com/guide/components/fragments

Closes: #2747
This commit is contained in:
Anderson Mesquita 2019-05-19 15:59:19 -04:00
parent a556183d60
commit 486ceed0ef
2 changed files with 42 additions and 94 deletions

View File

@ -1,6 +1,5 @@
package de.danoeh.antennapod.fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ListFragment;
@ -11,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;
@ -42,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
@ -72,41 +75,28 @@ public class CompletedDownloadsFragment extends ListFragment {
}
@Override
public void onDetach() {
super.onDetach();
if (disposable != null) {
disposable.dispose();
}
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) requireActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
}
@Override
public void onDestroyView() {
super.onDestroyView();
listAdapter = null;
viewCreated = false;
if (disposable != null) {
disposable.dispose();
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.downloads_completed, menu);
menu.findItem(R.id.episode_actions).setVisible(items.size() > 0);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (viewCreated && items != null) {
onFragmentLoaded();
}
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
addVerticalPadding();
addEmptyView();
viewCreated = true;
if (items != null && getActivity() != null) {
onFragmentLoaded();
public boolean onOptionsItemSelected(MenuItem item) {
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() {
@ -123,55 +113,15 @@ public class CompletedDownloadsFragment extends ListFragment {
lv.setPadding(0, vertPadding, 0, vertPadding);
}
@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) requireActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
}
private void onFragmentLoaded() {
if (listAdapter == null) {
listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
setListAdapter(listAdapter);
}
setListShown(true);
listAdapter.notifyDataSetChanged();
requireActivity().invalidateOptionsMenu();
}
@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);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.episode_actions) {
((MainActivity) requireActivity())
.loadChildFragment(EpisodesApplyActionFragment.newInstance(items, ACTION_DELETE | ACTION_ADD_TO_QUEUE));
return true;
}
return false;
}
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;
@ -197,18 +147,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();
}
onItemsLoaded();
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private void onItemsLoaded() {
setListShown(true);
listAdapter.notifyDataSetChanged();
requireActivity().invalidateOptionsMenu();
}
}

View File

@ -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;
@ -21,8 +25,6 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.view.EmptyViewHandler;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
/**
* Displays all running downloads and provides actions to cancel them
@ -32,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) {
@ -70,7 +72,6 @@ public class RunningDownloadsFragment extends ListFragment {
public void onDestroy() {
super.onDestroy();
setListAdapter(null);
adapter = null;
}
@Subscribe(sticky = true)
@ -78,21 +79,18 @@ public class RunningDownloadsFragment extends ListFragment {
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;