Handle destroied PlaybackService instances.

ShowQueueFragment used to keep a reference to PlaybackService - which may get stale at some point (if the service is re-created), causing the fragment to lose any functionality.

This commit gets rid of the long lived reference.
(Note that the Adapter still has a semi-long-lived reference. Thats not nice but okay as it only needs to access the SongTimeline)
This commit is contained in:
Adrian Ulrich 2018-05-05 12:06:33 +02:00
parent d2b751e841
commit 5ff7f8eddb

View File

@ -41,7 +41,7 @@ public class ShowQueueFragment extends Fragment
private DragSortListView mListView; private DragSortListView mListView;
private ShowQueueAdapter mListAdapter; private ShowQueueAdapter mListAdapter;
private PlaybackService mService; private boolean mIsPopulated;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -72,15 +72,14 @@ public class ShowQueueFragment extends Fragment
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
// Get playback service if we can and must // Update the song list if we are not populated and
// This happens eg. during a rotate where the view // have an usable PlaybackService instance.
// was destroyed // This is the case if the Application already fully
if (mService == null && PlaybackService.hasInstance()) // started up, but just lost this view (due to rotation).
mService = PlaybackService.get(getActivity()); if (!mIsPopulated && PlaybackService.hasInstance()) {
if (mService != null)
refreshSongQueueList(true); refreshSongQueueList(true);
} }
}
private final static int CTX_MENU_PLAY = 100; private final static int CTX_MENU_PLAY = 100;
@ -95,7 +94,7 @@ public class ShowQueueFragment extends Fragment
@Override @Override
public void onCreateContextMenu(ContextMenu menu, View listView, ContextMenu.ContextMenuInfo absInfo) { public void onCreateContextMenu(ContextMenu menu, View listView, ContextMenu.ContextMenuInfo absInfo) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)absInfo; AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)absInfo;
Song song = mService.getSongByQueuePosition(info.position); Song song = playbackService().getSongByQueuePosition(info.position);
Intent intent = new Intent(); Intent intent = new Intent();
intent.putExtra("id", song.id); intent.putExtra("id", song.id);
@ -121,19 +120,20 @@ public class ShowQueueFragment extends Fragment
int itemId = item.getItemId(); int itemId = item.getItemId();
int pos = intent.getIntExtra("position", -1); int pos = intent.getIntExtra("position", -1);
Song song = mService.getSongByQueuePosition(pos); PlaybackService service = playbackService();
Song song = service.getSongByQueuePosition(pos);
switch (item.getItemId()) { switch (item.getItemId()) {
case CTX_MENU_PLAY: case CTX_MENU_PLAY:
onItemClick(null, null, pos, -1); onItemClick(null, null, pos, -1);
break; break;
case CTX_MENU_ENQUEUE_ALBUM: case CTX_MENU_ENQUEUE_ALBUM:
mService.enqueueFromSong(song, MediaUtils.TYPE_ALBUM); service.enqueueFromSong(song, MediaUtils.TYPE_ALBUM);
break; break;
case CTX_MENU_ENQUEUE_ARTIST: case CTX_MENU_ENQUEUE_ARTIST:
mService.enqueueFromSong(song, MediaUtils.TYPE_ARTIST); service.enqueueFromSong(song, MediaUtils.TYPE_ARTIST);
break; break;
case CTX_MENU_ENQUEUE_GENRE: case CTX_MENU_ENQUEUE_GENRE:
mService.enqueueFromSong(song, MediaUtils.TYPE_GENRE); service.enqueueFromSong(song, MediaUtils.TYPE_GENRE);
break; break;
case CTX_MENU_REMOVE: case CTX_MENU_REMOVE:
remove(pos); remove(pos);
@ -154,7 +154,7 @@ public class ShowQueueFragment extends Fragment
@Override @Override
public void drop(int from, int to) { public void drop(int from, int to) {
if (from != to) { if (from != to) {
mService.moveSongPosition(from, to); playbackService().moveSongPosition(from, to);
} }
} }
@ -164,7 +164,7 @@ public class ShowQueueFragment extends Fragment
*/ */
@Override @Override
public void remove(int which) { public void remove(int which) {
mService.removeSongPosition(which); playbackService().removeSongPosition(which);
} }
/** /**
@ -172,7 +172,7 @@ public class ShowQueueFragment extends Fragment
*/ */
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mService.jumpToQueuePosition(position); playbackService().jumpToQueuePosition(position);
} }
/** /**
@ -180,15 +180,17 @@ public class ShowQueueFragment extends Fragment
* @param scroll enable or disable jumping to the currently playing item * @param scroll enable or disable jumping to the currently playing item
*/ */
private void refreshSongQueueList(final boolean scroll) { private void refreshSongQueueList(final boolean scroll) {
final PlaybackService service = playbackService();
final int pos = service.getTimelinePosition();
getActivity().runOnUiThread(new Runnable(){ getActivity().runOnUiThread(new Runnable(){
public void run() { public void run() {
int pos = mService.getTimelinePosition(); mListAdapter.setData(service, pos);
mListAdapter.setData(mService, pos);
if(scroll) if(scroll)
scrollToCurrentSong(pos); scrollToCurrentSong(pos);
} }
}); });
mIsPopulated = true;
} }
/** /**
@ -206,15 +208,26 @@ public class ShowQueueFragment extends Fragment
mListView.setSelectionFromTop(currentSongPosition, 0); /* scroll to currently playing song */ mListView.setSelectionFromTop(currentSongPosition, 0); /* scroll to currently playing song */
} }
/**
* Shortcut function to get the current playback service
* using our parent activity as context
*/
private PlaybackService playbackService() {
return PlaybackService.get(getActivity());
}
/** /**
* Called after a song has been set. * Called after a song has been set.
* We are only interested in this call if mService is null *
* as this signals that the playback service just became ready * We are generally not interested in such events as we do not display
* (and wasn't during onResume()) * any playback state - only the queue (which has its changes announced
* using `onTimelineChanged()'.
* However: We are still interested in `setSong()' if we are unpopulated:
* Such an event will then indicate that the PlaybackService just finished
* its startup and is ready to be queried.
*/ */
public void setSong(long uptime, Song song) { public void setSong(long uptime, Song song) {
if (mService == null) { if (!mIsPopulated) {
mService = PlaybackService.get(getActivity());
onTimelineChanged(); onTimelineChanged();
} }
} }
@ -223,9 +236,10 @@ public class ShowQueueFragment extends Fragment
* Called after the timeline changed * Called after the timeline changed
*/ */
public void onTimelineChanged() { public void onTimelineChanged() {
if (mService != null) if (PlaybackService.hasInstance()) {
refreshSongQueueList(false); refreshSongQueueList(false);
} }
}
// Unused Callbacks of TimelineCallback // Unused Callbacks of TimelineCallback
public void onPositionInfoChanged() { public void onPositionInfoChanged() {