Do not hold state in fragments

This commit is contained in:
ByteHamster 2019-04-10 00:52:49 +02:00
parent 3fdf6af1a3
commit 0e3cabb86d
6 changed files with 155 additions and 315 deletions

View File

@ -239,7 +239,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
getWindow().setFormat(PixelFormat.TRANSPARENT);
setupGUI();
loadMediaInfo();
}
@Override
@ -283,6 +282,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.release();
}
controller = newPlaybackController();
controller.init();
loadMediaInfo();
onPositionObserverUpdate();
}
@ -661,9 +662,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
super.onResume();
Log.d(TAG, "onResume()");
StorageUtils.checkStorageAvailability(this);
if (controller != null) {
controller.init();
}
}
public void onEventMainThread(ServiceEvent event) {

View File

@ -102,7 +102,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
private ActionBarDrawerToggle drawerToggle;
private int mPosition = -1;
private Playable media;
private ViewPager pager;
private MediaplayerInfoPagerAdapter pagerAdapter;
@ -124,9 +123,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop()");
if(pagerAdapter != null) {
pagerAdapter.setController(null);
}
if (disposable != null) {
disposable.dispose();
}
@ -182,11 +178,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
protected void onResume() {
super.onResume();
if(pagerAdapter != null && controller != null && controller.getMedia() != media) {
media = controller.getMedia();
pagerAdapter.onMediaChanged(media);
pagerAdapter.setController(controller);
}
AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext());
EventDistributor.getInstance().register(contentUpdate);
@ -279,8 +270,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
pager = findViewById(R.id.pager);
pager.setOffscreenPageLimit(3);
pagerAdapter = new MediaplayerInfoPagerAdapter(getSupportFragmentManager(), media);
pagerAdapter.setController(controller);
pagerAdapter = new MediaplayerInfoPagerAdapter(getSupportFragmentManager());
pager.setAdapter(pagerAdapter);
CirclePageIndicator pageIndicator = findViewById(R.id.page_indicator);
pageIndicator.setViewPager(pager);
@ -290,37 +280,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
navList.post(this::supportStartPostponedEnterTransition);
}
@Override
protected void onPositionObserverUpdate() {
super.onPositionObserverUpdate();
notifyMediaPositionChanged();
}
@Override
protected boolean loadMediaInfo() {
if (!super.loadMediaInfo()) {
return false;
}
if(controller != null && controller.getMedia() != media) {
media = controller.getMedia();
pagerAdapter.onMediaChanged(media);
}
return true;
}
private void notifyMediaPositionChanged() {
if(pagerAdapter == null) {
return;
}
ChaptersFragment chaptersFragment = pagerAdapter.getChaptersFragment();
if(chaptersFragment != null) {
ChaptersListAdapter adapter = (ChaptersListAdapter) chaptersFragment.getListAdapter();
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
}
@Override
protected void onReloadNotification(int notificationCode) {
if (notificationCode == PlaybackService.EXTRA_CODE_VIDEO) {
@ -567,50 +526,11 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
}
};
public interface MediaplayerInfoContentFragment {
void onMediaChanged(Playable media);
}
private static class MediaplayerInfoPagerAdapter extends FragmentStatePagerAdapter {
private static final String TAG = "MPInfoPagerAdapter";
private Playable media;
private PlaybackController controller;
public MediaplayerInfoPagerAdapter(FragmentManager fm, Playable media) {
public MediaplayerInfoPagerAdapter(FragmentManager fm) {
super(fm);
this.media = media;
}
private CoverFragment coverFragment;
private ItemDescriptionFragment itemDescriptionFragment;
private ChaptersFragment chaptersFragment;
public void onMediaChanged(Playable media) {
Log.d(TAG, "media changing to " + ((media != null) ? media.getEpisodeTitle() : "null"));
this.media = media;
if(coverFragment != null) {
coverFragment.onMediaChanged(media);
}
if(itemDescriptionFragment != null) {
itemDescriptionFragment.onMediaChanged(media);
}
if(chaptersFragment != null) {
chaptersFragment.onMediaChanged(media);
}
}
public void setController(PlaybackController controller) {
this.controller = controller;
if(chaptersFragment != null) {
chaptersFragment.setController(controller);
}
}
@Nullable
public ChaptersFragment getChaptersFragment() {
return chaptersFragment;
}
@Override
@ -618,21 +538,11 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
Log.d(TAG, "getItem(" + position + ")");
switch (position) {
case POS_COVER:
if(coverFragment == null) {
coverFragment = CoverFragment.newInstance(media);
}
return coverFragment;
return new CoverFragment();
case POS_DESCR:
if(itemDescriptionFragment == null) {
itemDescriptionFragment = ItemDescriptionFragment.newInstance(media, true, true);
}
return itemDescriptionFragment;
return new ItemDescriptionFragment();
case POS_CHAPTERS:
if(chaptersFragment == null) {
chaptersFragment = ChaptersFragment.newInstance(media);
chaptersFragment.setController(controller);
}
return chaptersFragment;
return new ChaptersFragment();
default:
return null;
}

View File

@ -2,32 +2,23 @@ package de.danoeh.antennapod.fragment;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment;
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
import de.danoeh.antennapod.core.event.ServiceEvent;
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.greenrobot.event.EventBus;
public class ChaptersFragment extends ListFragment implements MediaplayerInfoContentFragment {
public class ChaptersFragment extends ListFragment {
private static final String TAG = "ChaptersFragment";
private Playable media;
private ChaptersListAdapter adapter;
private PlaybackController controller;
private ChaptersListAdapter adapter;
public static ChaptersFragment newInstance(Playable media) {
ChaptersFragment f = new ChaptersFragment();
f.media = media;
return f;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@ -39,10 +30,6 @@ public class ChaptersFragment extends ListFragment implements MediaplayerInfoCon
lv.setPadding(0, vertPadding, 0, vertPadding);
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 +37,51 @@ 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() {
if (getMedia() == null) {
return false;
}
onMediaChanged(getMedia());
return true;
}
public void onDestroy() {
super.onDestroy();
adapter = null;
controller = null;
@Override
public void onPositionObserverUpdate() {
adapter.notifyDataSetChanged();
}
};
controller.init();
onMediaChanged(controller.getMedia());
EventBus.getDefault().register(this);
}
@Override
public void onMediaChanged(Playable media) {
if(this.media == media) {
return;
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
controller.release();
controller = null;
}
public void onEventMainThread(ServiceEvent event) {
if (event.action == ServiceEvent.Action.SERVICE_STARTED && controller != null) {
controller.init();
}
this.media = media;
}
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));
if (media == null || media.getChapters() == null || media.getChapters().size() == 0) {
setEmptyText(getString(R.string.no_chapters_label));
} else {
setEmptyText(null);
}
}
}
public void setController(PlaybackController controller) {
this.controller = controller;
}
}

View File

@ -13,37 +13,24 @@ 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.event.ServiceEvent;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.greenrobot.event.EventBus;
/**
* 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;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -57,31 +44,20 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
}
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 {
Playable media = controller.getMedia();
if (media == null) {
Log.w(TAG, "loadMediaInfo was called while media was null");
return;
}
}
@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");
}
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
@ -92,14 +68,35 @@ 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() {
if (getMedia() == null) {
return false;
}
CoverFragment.this.loadMediaInfo();
return true;
}
};
controller.init();
loadMediaInfo();
EventBus.getDefault().register(this);
}
public void onEventMainThread(ServiceEvent event) {
if (event.action == ServiceEvent.Action.SERVICE_STARTED && controller != null) {
controller.init();
}
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
controller.release();
controller = null;
}
}

View File

@ -28,18 +28,15 @@ 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.event.ServiceEvent;
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 de.greenrobot.event.EventBus;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@ -48,7 +45,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";
@ -56,56 +53,15 @@ 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
@ -176,39 +132,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
@ -301,9 +224,6 @@ 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())
@ -316,8 +236,8 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
@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
@ -327,42 +247,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.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 (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;
@ -379,15 +295,35 @@ 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();
EventBus.getDefault().register(this);
}
public void onEventMainThread(ServiceEvent event) {
if (event.action == ServiceEvent.Action.SERVICE_STARTED && controller != null) {
controller.init();
}
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
controller.release();
controller = null;
}
}

View File

@ -781,6 +781,9 @@ public abstract class PlaybackController {
}
private void initServiceNotRunning() {
if (getPlayButton() == null) {
return;
}
Log.v(TAG, "initServiceNotRunning()");
mediaLoader = Maybe.create((MaybeOnSubscribe<Playable>) emitter -> {
Playable media = getMedia();