From a12581051e8bce57c085a50082c47edbe5848036 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Mon, 13 Jul 2015 19:36:58 -0700 Subject: #527 Change from polling to using DownloadService events --- .../dsub/activity/SubsonicActivity.java | 22 +- .../dsub/activity/SubsonicFragmentActivity.java | 152 +++--- .../dsub/fragments/NowPlayingFragment.java | 539 ++++++++------------- .../daneren2005/dsub/service/DownloadService.java | 92 +++- 4 files changed, 369 insertions(+), 436 deletions(-) (limited to 'app') diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java index 33d978bf..1492c778 100644 --- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java +++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java @@ -29,6 +29,7 @@ import android.media.AudioManager; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.Handler; import android.support.design.widget.NavigationView; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v4.app.Fragment; @@ -82,6 +83,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte private static final int MENU_GROUP_SERVER = 10; private static final int MENU_ITEM_SERVER_BASE = 100; + private final List afterServiceAvailable = new ArrayList<>(); private boolean drawerIdle = true; private boolean destroyed = false; private boolean finished = false; @@ -91,6 +93,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte protected View secondaryContainer; protected boolean tv = false; protected boolean touchscreen = true; + protected Handler handler = new Handler(); Spinner actionBarSpinner; ArrayAdapter spinnerAdapter; ViewGroup rootView; @@ -871,13 +874,28 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte for (int i = 0; i < 5; i++) { DownloadService downloadService = DownloadService.getInstance(); if (downloadService != null) { - return downloadService; + break; } Log.w(TAG, "DownloadService not running. Attempting to start it."); startService(new Intent(this, DownloadService.class)); Util.sleepQuietly(50L); } - return DownloadService.getInstance(); + + final DownloadService downloadService = DownloadService.getInstance(); + if(downloadService != null && afterServiceAvailable.size() > 0) { + for(Runnable runnable: afterServiceAvailable) { + handler.post(runnable); + } + afterServiceAvailable.clear(); + } + return downloadService; + } + public void runWhenServiceAvailable(Runnable runnable) { + if(getDownloadService() != null) { + runnable.run(); + } else { + afterServiceAvailable.add(runnable); + } } public static String getThemeName() { diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java index f0685a5f..e5144f6f 100644 --- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java +++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java @@ -45,6 +45,7 @@ import com.sothree.slidinguppanel.SlidingUpPanelLayout; import java.io.File; import java.util.Date; +import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -82,13 +83,12 @@ import github.daneren2005.dsub.view.ChangeLog; /** * Created by Scott on 10/14/13. */ -public class SubsonicFragmentActivity extends SubsonicActivity { +public class SubsonicFragmentActivity extends SubsonicActivity implements DownloadService.OnSongChangedListener { private static String TAG = SubsonicFragmentActivity.class.getSimpleName(); private static boolean infoDialogDisplayed; private static boolean sessionInitialized = false; private static long ALLOWED_SKEW = 30000L; - private Handler handler = new Handler(); private SlidingUpPanelLayout slideUpPanel; private SlidingUpPanelLayout.PanelSlideListener panelSlideListener; private NowPlayingFragment nowPlayingFragment; @@ -96,7 +96,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity { private Toolbar mainToolbar; private Toolbar nowPlayingToolbar; - private ScheduledExecutorService executorService; private View bottomBar; private ImageView coverArtView; private TextView trackView; @@ -292,11 +291,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity { getDownloadService().previous(); return null; } - - @Override - protected void done(Void result) { - update(); - } }.execute(); } }); @@ -317,11 +311,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity { return null; } - - @Override - protected void done(Void result) { - update(); - } }.execute(); } }); @@ -340,11 +329,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity { getDownloadService().next(); return null; } - - @Override - protected void done(Void result) { - update(); - } }.execute(); } }); @@ -406,18 +390,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity { public void onResume() { super.onResume(); - Runnable runnable = new Runnable() { - @Override - public void run() { - handler.post(new Runnable() { - @Override - public void run() { - update(); - } - }); - } - }; - if(getIntent().hasExtra(Constants.INTENT_EXTRA_VIEW_ALBUM)) { SubsonicFragment fragment = new SelectDirectoryFragment(); Bundle args = new Bundle(); @@ -437,15 +409,21 @@ public class SubsonicFragmentActivity extends SubsonicActivity { } createAccount(); - - executorService = Executors.newSingleThreadScheduledExecutor(); - executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS); + runWhenServiceAvailable(new Runnable() { + @Override + public void run() { + getDownloadService().addOnSongChangedListener(SubsonicFragmentActivity.this, true); + } + }); } @Override public void onPause() { super.onPause(); - executorService.shutdown(); + DownloadService downloadService = getDownloadService(); + if(downloadService != null) { + downloadService.removeOnSongChangeListener(this); + } } @Override @@ -638,54 +616,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity { } } - private void update() { - DownloadService downloadService = getDownloadService(); - if (downloadService == null) { - return; - } - - DownloadFile current = downloadService.getCurrentPlaying(); - PlayerState state = downloadService.getPlayerState(); - if(current == currentPlaying && state == currentState) { - if(current == null && slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { - slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN); - } - return; - } else { - currentPlaying = current; - currentState = state; - } - - MusicDirectory.Entry song = null; - if (current != null) { - song = current.getSong(); - trackView.setText(song.getTitle()); - artistView.setText(song.getArtist()); - - if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.HIDDEN) { - slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); - } - } else if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { - slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN); - } - - if (coverArtView != null) { - int height = coverArtView.getHeight(); - if (height <= 0) { - int[] attrs = new int[]{R.attr.actionBarSize}; - TypedArray typedArray = this.obtainStyledAttributes(attrs); - height = typedArray.getDimensionPixelSize(0, 0); - typedArray.recycle(); - } - getImageLoader().loadImage(coverArtView, song, false, height, false); - } - - int[] attrs = new int[]{(state == PlayerState.STARTED) ? R.attr.actionbar_pause : R.attr.actionbar_start}; - TypedArray typedArray = this.obtainStyledAttributes(attrs); - startButton.setImageResource(typedArray.getResourceId(0, 0)); - typedArray.recycle(); - } - public void checkUpdates() { try { String version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName; @@ -892,4 +822,62 @@ public class SubsonicFragmentActivity extends SubsonicActivity { public Toolbar getActiveToolbar() { return slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED ? nowPlayingToolbar : mainToolbar; } + + @Override + public void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex) { + DownloadService downloadService = getDownloadService(); + PlayerState state = downloadService.getPlayerState(); + if(currentPlaying == this.currentPlaying && state == currentState) { + if(currentPlaying == null && slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { + slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN); + } + return; + } else { + this.currentPlaying = currentPlaying; + } + + MusicDirectory.Entry song = null; + if (currentPlaying != null) { + song = currentPlaying.getSong(); + trackView.setText(song.getTitle()); + artistView.setText(song.getArtist()); + + if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.HIDDEN) { + slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); + } + } else if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { + slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN); + } + + if (coverArtView != null) { + int height = coverArtView.getHeight(); + if (height <= 0) { + int[] attrs = new int[]{R.attr.actionBarSize}; + TypedArray typedArray = this.obtainStyledAttributes(attrs); + height = typedArray.getDimensionPixelSize(0, 0); + typedArray.recycle(); + } + getImageLoader().loadImage(coverArtView, song, false, height, false); + } + } + + @Override + public void onSongsChanged(List songs, DownloadFile currentPlaying, int currentPlayingIndex) { + if(this.currentPlaying != currentPlaying) { + onSongChanged(currentPlaying, currentPlayingIndex); + } + } + + @Override + public void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable) { + + } + + @Override + public void onStateUpdate(DownloadFile downloadFile, PlayerState playerState) { + int[] attrs = new int[]{(playerState == PlayerState.STARTED) ? R.attr.actionbar_pause : R.attr.actionbar_start}; + TypedArray typedArray = this.obtainStyledAttributes(attrs); + startButton.setImageResource(typedArray.getResourceId(0, 0)); + typedArray.recycle(); + } } diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java index 1b652f57..a75470e9 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java @@ -66,6 +66,7 @@ import github.daneren2005.dsub.domain.RepeatMode; import github.daneren2005.dsub.domain.ServerInfo; import github.daneren2005.dsub.service.DownloadFile; import github.daneren2005.dsub.service.DownloadService; +import github.daneren2005.dsub.service.DownloadService.OnSongChangedListener; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.service.MusicServiceFactory; import github.daneren2005.dsub.service.OfflineException; @@ -86,11 +87,10 @@ import java.util.ArrayList; import java.util.concurrent.ScheduledFuture; import github.daneren2005.dsub.activity.SubsonicActivity; -public class NowPlayingFragment extends SubsonicFragment implements OnGestureListener, SectionAdapter.OnItemClickedListener { +public class NowPlayingFragment extends SubsonicFragment implements OnGestureListener, SectionAdapter.OnItemClickedListener, OnSongChangedListener { private static final String TAG = NowPlayingFragment.class.getSimpleName(); private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 10; private static final int INCREMENT_TIME = 5000; - private static final int SERVICE_BACKOFF = 200; private static final int ACTION_PREVIOUS = 1; private static final int ACTION_NEXT = 2; @@ -126,13 +126,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis private ScheduledFuture hideControlsFuture; private List songList; private DownloadFileAdapter songListAdapter; - private SilentBackgroundTask onProgressChangedTask; - private SilentBackgroundTask onCurrentChangedTask; - private SilentBackgroundTask onDownloadListChangedTask; private boolean seekInProgress = false; private boolean startFlipped = false; private boolean scrollWhenLoaded = false; private int lastY = 0; + private int currentPlayingSize = 0; /** * Called when the activity is first created. @@ -280,12 +278,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis getDownloadService().previous(); return null; } - - @Override - protected void done(Void result) { - onCurrentChanged(); - onProgressChanged(); - } }.execute(); setControlsVisible(true); } @@ -306,14 +298,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis getDownloadService().next(); return true; } - - @Override - protected void done(Boolean result) { - if (result) { - onCurrentChanged(); - onProgressChanged(); - } - } }.execute(); setControlsVisible(true); } @@ -333,12 +317,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis getDownloadService().pause(); return null; } - - @Override - protected void done(Void result) { - onCurrentChanged(); - onProgressChanged(); - } }.execute(); } }); @@ -352,12 +330,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis getDownloadService().reset(); return null; } - - @Override - protected void done(Void result) { - onCurrentChanged(); - onProgressChanged(); - } }.execute(); } }); @@ -372,12 +344,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis start(); return null; } - - @Override - protected void done(Void result) { - onCurrentChanged(); - onProgressChanged(); - } }.execute(); } }); @@ -387,7 +353,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis public void onClick(View view) { RepeatMode repeatMode = getDownloadService().getRepeatMode().next(); getDownloadService().setRepeatMode(repeatMode); - onDownloadListChanged(); switch (repeatMode) { case OFF: Util.toast(context, R.string.download_repeat_off); @@ -523,7 +488,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis @Override protected void done(Void result) { seekInProgress = false; - NowPlayingFragment.this.onProgressChanged(); } }.execute(); } @@ -811,24 +775,10 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis } private void onResumeHandlers() { final Handler handler = new Handler(); - Runnable runnable = new Runnable() { - @Override - public void run() { - handler.post(new Runnable() { - @Override - public void run() { - update(); - } - }); - } - }; - executorService = Executors.newSingleThreadScheduledExecutor(); - executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS); - setControlsVisible(true); - DownloadService downloadService = getDownloadService(); + final DownloadService downloadService = getDownloadService(); if (downloadService == null || downloadService.getCurrentPlaying() == null || startFlipped) { playlistFlipper.setDisplayedChild(1); } @@ -843,24 +793,17 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis if(currentPlaying == null && downloadService != null && currentPlaying == downloadService.getCurrentPlaying()) { getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false); } - if(downloadService != null) { - downloadService.startRemoteScan(); - } else { - // Make sure to call remote scan once the service is ready - final Runnable waitForService = new Runnable() { - @Override - public void run() { - DownloadService service = getDownloadService(); - if(service != null) { - service.startRemoteScan(); - } else { - handler.postDelayed(this, SERVICE_BACKOFF); - } - } - }; - handler.postDelayed(waitForService, SERVICE_BACKOFF); - } + context.runWhenServiceAvailable(new Runnable() { + @Override + public void run() { + if(primaryFragment) { + DownloadService downloadService = getDownloadService(); + downloadService.startRemoteScan(); + downloadService.addOnSongChangedListener(NowPlayingFragment.this, true); + } + } + }); } @Override @@ -870,11 +813,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis } private void onPauseHandlers() { if(executorService != null) { - executorService.shutdown(); - if (getDownloadService() != null) { - getDownloadService().stopRemoteScan(); + DownloadService downloadService = getDownloadService(); + if (downloadService != null) { + downloadService.stopRemoteScan(); + downloadService.removeOnSongChangeListener(this); } - executorService = null; playlistFlipper.setDisplayedChild(0); } } @@ -983,24 +926,10 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis } private void update() { - if (getDownloadService() == null) { - return; - } - - if (currentRevision != getDownloadService().getDownloadListUpdateRevision()) { - onDownloadListChanged(); - } - - if (currentPlaying != getDownloadService().getCurrentPlaying()) { - onCurrentChanged(); - } - if(startFlipped) { startFlipped = false; scrollToCurrent(); } - - onProgressChanged(); } protected void startTimer() { @@ -1098,258 +1027,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis } } } - private void onDownloadListChanged() { - onDownloadListChanged(false); - } - private void onDownloadListChanged(final boolean refresh) { - final DownloadService downloadService = getDownloadService(); - if (downloadService == null || onDownloadListChangedTask != null) { - return; - } - - onDownloadListChangedTask = new SilentBackgroundTask(context) { - int currentPlayingIndex; - int size; - - @Override - protected Void doInBackground() throws Throwable { - currentPlayingIndex = downloadService.getCurrentPlayingIndex() + 1; - size = downloadService.size(); - - return null; - } - - @Override - protected void done(Void result) { - List list; - list = downloadService.getSongs(); - - if(downloadService.isShufflePlayEnabled()) { - emptyTextView.setText(R.string.download_shuffle_loading); - } - else { - emptyTextView.setText(R.string.download_empty); - } - - if(songListAdapter == null || refresh) { - songList = new ArrayList<>(); - songList.addAll(list); - playlistView.setAdapter(songListAdapter = new DownloadFileAdapter(context, songList, NowPlayingFragment.this)); - } else { - songList.clear(); - songList.addAll(list); - songListAdapter.notifyDataSetChanged(); - } - - emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE); - currentRevision = downloadService.getDownloadListUpdateRevision(); - - switch (downloadService.getRepeatMode()) { - case OFF: - if("light".equals(SubsonicActivity.getThemeName()) | "light_fullscreen".equals(SubsonicActivity.getThemeName())) { - repeatButton.setImageResource(R.drawable.media_repeat_off_light); - } else { - repeatButton.setImageResource(R.drawable.media_repeat_off); - } - break; - case ALL: - repeatButton.setImageResource(R.drawable.media_repeat_all); - break; - case SINGLE: - repeatButton.setImageResource(R.drawable.media_repeat_single); - break; - default: - break; - } - - if(scrollWhenLoaded) { - scrollToCurrent(); - scrollWhenLoaded = false; - } - - setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, size)); - onDownloadListChangedTask = null; - if(onCurrentChangedTask != null) { - onCurrentChangedTask.execute(); - } else if(onProgressChangedTask != null) { - onProgressChangedTask.execute(); - } - } - }; - onDownloadListChangedTask.execute(); - } - - private void onCurrentChanged() { - final DownloadService downloadService = getDownloadService(); - if (downloadService == null || onCurrentChangedTask != null) { - return; - } - - onCurrentChangedTask = new SilentBackgroundTask(context) { - int currentPlayingIndex; - int currentPlayingSize; - - @Override - protected Void doInBackground() throws Throwable { - currentPlaying = downloadService.getCurrentPlaying(); - currentPlayingIndex = downloadService.getCurrentPlayingIndex() + 1; - currentPlayingSize = downloadService.size(); - return null; - } - - @Override - protected void done(Void result) { - if (currentPlaying != null) { - Entry song = currentPlaying.getSong(); - songTitleTextView.setText(song.getTitle()); - getImageLoader().loadImage(albumArtImageView, song, true, true); - starButton.setImageResource(song.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off); - setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, currentPlayingSize)); - - int badRating, goodRating, bookmark; - if(song.getRating() == 1) { - badRating = R.drawable.ic_action_rating_bad_selected; - } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - badRating = R.drawable.ic_action_rating_bad_dark; - } else { - badRating = Util.getAttribute(context, R.attr.rating_bad); - } - rateBadButton.setImageResource(badRating); - - if(song.getRating() == 5) { - goodRating = R.drawable.ic_action_rating_good_selected; - } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - goodRating = R.drawable.ic_action_rating_good_dark; - } else { - goodRating = Util.getAttribute(context, R.attr.rating_good); - } - rateGoodButton.setImageResource(goodRating); - - if(song.getBookmark() != null) { - bookmark = R.drawable.ic_menu_bookmark_selected; - } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - bookmark = R.drawable.ic_menu_bookmark_dark; - } else { - bookmark = Util.getAttribute(context, R.attr.bookmark); - } - bookmarkButton.setImageResource(bookmark); - } else { - songTitleTextView.setText(null); - getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false); - starButton.setImageResource(android.R.drawable.btn_star_big_off); - setSubtitle(null); - } - onCurrentChangedTask = null; - if(onProgressChangedTask != null) { - onProgressChangedTask.execute(); - } - } - }; - - if(onDownloadListChangedTask == null) { - onCurrentChangedTask.execute(); - } - } - - private void onProgressChanged() { - // Make sure to only be trying to run one of these at a time - if (getDownloadService() == null || onProgressChangedTask != null) { - return; - } - - onProgressChangedTask = new SilentBackgroundTask(context) { - DownloadService downloadService; - int millisPlayed; - Integer duration; - PlayerState playerState; - boolean isSeekable; - - @Override - protected Void doInBackground() throws Throwable { - downloadService = getDownloadService(); - millisPlayed = Math.max(0, downloadService.getPlayerPosition()); - duration = downloadService.getPlayerDuration(); - playerState = getDownloadService().getPlayerState(); - isSeekable = downloadService.isSeekable(); - return null; - } - - @Override - protected void done(Void result) { - if (currentPlaying != null) { - int millisTotal = duration == null ? 0 : duration; - - positionTextView.setText(Util.formatDuration(millisPlayed / 1000)); - if(millisTotal > 0) { - durationTextView.setText(Util.formatDuration(millisTotal / 1000)); - } else { - durationTextView.setText("-:--"); - } - progressBar.setMax(millisTotal == 0 ? 100 : millisTotal); // Work-around for apparent bug. - if(!seekInProgress) { - progressBar.setProgress(millisPlayed); - } - progressBar.setEnabled(isSeekable); - } else { - positionTextView.setText("0:00"); - durationTextView.setText("-:--"); - progressBar.setProgress(0); - progressBar.setEnabled(false); - } - - switch (playerState) { - case DOWNLOADING: - if(currentPlaying != null) { - if(Util.isWifiRequiredForDownload(context)) { - statusTextView.setText(context.getResources().getString(R.string.download_playerstate_mobile_disabled)); - } else { - long bytes = currentPlaying.getPartialFile().length(); - statusTextView.setText(context.getResources().getString(R.string.download_playerstate_downloading, Util.formatLocalizedBytes(bytes, context))); - } - } - break; - case PREPARING: - statusTextView.setText(R.string.download_playerstate_buffering); - break; - default: - if(currentPlaying != null) { - String artist = ""; - if(currentPlaying.getSong().getArtist() != null) { - artist = currentPlaying.getSong().getArtist() + " - "; - } - statusTextView.setText(artist + currentPlaying.getSong().getAlbum()); - } else { - statusTextView.setText(null); - } - break; - } - - switch (playerState) { - case STARTED: - pauseButton.setVisibility(View.VISIBLE); - stopButton.setVisibility(View.INVISIBLE); - startButton.setVisibility(View.INVISIBLE); - break; - case DOWNLOADING: - case PREPARING: - pauseButton.setVisibility(View.INVISIBLE); - stopButton.setVisibility(View.VISIBLE); - startButton.setVisibility(View.INVISIBLE); - break; - default: - pauseButton.setVisibility(View.INVISIBLE); - stopButton.setVisibility(View.INVISIBLE); - startButton.setVisibility(View.VISIBLE); - break; - } - - onProgressChangedTask = null; - } - }; - if(onDownloadListChangedTask == null && onCurrentChangedTask == null) { - onProgressChangedTask.execute(); - } - } private void changeProgress(final int ms) { final DownloadService downloadService = getDownloadService(); @@ -1528,11 +1205,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis downloadService.seekTo(downloadService.getPlayerPosition() - DownloadService.REWIND); break; } - - onProgressChanged(); - if(performAction == ACTION_NEXT || performAction == ACTION_PREVIOUS) { - onCurrentChanged(); - } return null; } }.execute(); @@ -1568,11 +1240,180 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis @Override protected Void doInBackground() throws Throwable { getDownloadService().play(item); - - onCurrentChanged(); - onProgressChanged(); return null; } }.execute(); } + + @Override + public void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex) { + this.currentPlaying = currentPlaying; + if (currentPlaying != null) { + Entry song = currentPlaying.getSong(); + songTitleTextView.setText(song.getTitle()); + getImageLoader().loadImage(albumArtImageView, song, true, true); + starButton.setImageResource(song.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off); + setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, currentPlayingSize)); + + int badRating, goodRating, bookmark; + if(song.getRating() == 1) { + badRating = R.drawable.ic_action_rating_bad_selected; + } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + badRating = R.drawable.ic_action_rating_bad_dark; + } else { + badRating = Util.getAttribute(context, R.attr.rating_bad); + } + rateBadButton.setImageResource(badRating); + + if(song.getRating() == 5) { + goodRating = R.drawable.ic_action_rating_good_selected; + } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + goodRating = R.drawable.ic_action_rating_good_dark; + } else { + goodRating = Util.getAttribute(context, R.attr.rating_good); + } + rateGoodButton.setImageResource(goodRating); + + if(song.getBookmark() != null) { + bookmark = R.drawable.ic_menu_bookmark_selected; + } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + bookmark = R.drawable.ic_menu_bookmark_dark; + } else { + bookmark = Util.getAttribute(context, R.attr.bookmark); + } + bookmarkButton.setImageResource(bookmark); + } else { + songTitleTextView.setText(null); + getImageLoader().loadImage(albumArtImageView, null, true, false); + starButton.setImageResource(android.R.drawable.btn_star_big_off); + setSubtitle(null); + } + } + + @Override + public void onSongsChanged(List songs, DownloadFile currentPlaying, int currentPlayingIndex) { + currentPlayingSize = songs.size(); + + DownloadService downloadService = getDownloadService(); + if(downloadService.isShufflePlayEnabled()) { + emptyTextView.setText(R.string.download_shuffle_loading); + } + else { + emptyTextView.setText(R.string.download_empty); + } + + if(songListAdapter == null) { + songList = new ArrayList<>(); + songList.addAll(songs); + playlistView.setAdapter(songListAdapter = new DownloadFileAdapter(context, songList, NowPlayingFragment.this)); + } else { + songList.clear(); + songList.addAll(songs); + songListAdapter.notifyDataSetChanged(); + } + + emptyTextView.setVisibility(songs.isEmpty() ? View.VISIBLE : View.GONE); + currentRevision = downloadService.getDownloadListUpdateRevision(); + + switch (downloadService.getRepeatMode()) { + case OFF: + if("light".equals(SubsonicActivity.getThemeName()) | "light_fullscreen".equals(SubsonicActivity.getThemeName())) { + repeatButton.setImageResource(R.drawable.media_repeat_off_light); + } else { + repeatButton.setImageResource(R.drawable.media_repeat_off); + } + break; + case ALL: + repeatButton.setImageResource(R.drawable.media_repeat_all); + break; + case SINGLE: + repeatButton.setImageResource(R.drawable.media_repeat_single); + break; + default: + break; + } + + if(scrollWhenLoaded) { + scrollToCurrent(); + scrollWhenLoaded = false; + } + + setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, currentPlayingSize)); + if(this.currentPlaying != currentPlaying) { + onSongChanged(currentPlaying, currentPlayingIndex); + } + } + + @Override + public void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable) { + if (currentPlaying != null) { + int millisTotal = duration == null ? 0 : duration; + + positionTextView.setText(Util.formatDuration(millisPlayed / 1000)); + if(millisTotal > 0) { + durationTextView.setText(Util.formatDuration(millisTotal / 1000)); + } else { + durationTextView.setText("-:--"); + } + progressBar.setMax(millisTotal == 0 ? 100 : millisTotal); // Work-around for apparent bug. + if(!seekInProgress) { + progressBar.setProgress(millisPlayed); + } + progressBar.setEnabled(isSeekable); + } else { + positionTextView.setText("0:00"); + durationTextView.setText("-:--"); + progressBar.setProgress(0); + progressBar.setEnabled(false); + } + } + + @Override + public void onStateUpdate(DownloadFile downloadFile, PlayerState playerState) { + switch (playerState) { + case DOWNLOADING: + if(currentPlaying != null) { + if(Util.isWifiRequiredForDownload(context)) { + statusTextView.setText(context.getResources().getString(R.string.download_playerstate_mobile_disabled)); + } else { + long bytes = currentPlaying.getPartialFile().length(); + statusTextView.setText(context.getResources().getString(R.string.download_playerstate_downloading, Util.formatLocalizedBytes(bytes, context))); + } + } + break; + case PREPARING: + statusTextView.setText(R.string.download_playerstate_buffering); + break; + default: + if(currentPlaying != null) { + String artist = ""; + if(currentPlaying.getSong().getArtist() != null) { + artist = currentPlaying.getSong().getArtist() + " - "; + } + statusTextView.setText(artist + currentPlaying.getSong().getAlbum()); + } else { + statusTextView.setText(null); + } + break; + } + + switch (playerState) { + case STARTED: + pauseButton.setVisibility(View.VISIBLE); + stopButton.setVisibility(View.INVISIBLE); + startButton.setVisibility(View.INVISIBLE); + break; + case DOWNLOADING: + case PREPARING: + pauseButton.setVisibility(View.INVISIBLE); + stopButton.setVisibility(View.VISIBLE); + startButton.setVisibility(View.INVISIBLE); + break; + default: + pauseButton.setVisibility(View.INVISIBLE); + stopButton.setVisibility(View.INVISIBLE); + startButton.setVisibility(View.VISIBLE); + break; + } + } } diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java index 2e3217d2..cd22fb69 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java @@ -139,6 +139,7 @@ public class DownloadService extends Service { private boolean removePlayed; private boolean shufflePlay; private boolean artistRadio; + private final List onSongChangedListeners = new ArrayList<>(); private long revision; private static DownloadService instance; private String suggestedPlaylistName; @@ -362,7 +363,6 @@ public class DownloadService extends Service { } } setNextPlaying(); - revision++; } else { int size = size(); int index = getCurrentPlayingIndex(); @@ -378,8 +378,9 @@ public class DownloadService extends Service { if(!autoplay && (size - 1) == index) { setNextPlaying(); } - revision++; } + revision++; + onSongsChanged(); updateRemotePlaylist(); if(shuffle) { @@ -543,6 +544,7 @@ public class DownloadService extends Service { currentPlayingIndex = 0; } revision++; + onSongsChanged(); lifecycleSupport.serializeDownloadQueue(); updateRemotePlaylist(); setNextPlaying(); @@ -694,7 +696,7 @@ public class DownloadService extends Service { reset(); downloadList.clear(); - revision++; + onSongsChanged(); if (currentDownloading != null && !backgroundDownloadList.contains(currentDownloading)) { currentDownloading.cancelDownload(); currentDownloading = null; @@ -733,6 +735,7 @@ public class DownloadService extends Service { currentPlayingIndex = downloadList.indexOf(currentPlaying); backgroundDownloadList.remove(downloadFile); revision++; + onSongsChanged(); lifecycleSupport.serializeDownloadQueue(); updateRemotePlaylist(); if(downloadFile == nextPlaying) { @@ -789,6 +792,7 @@ public class DownloadService extends Service { Util.broadcastNewTrackInfo(this, null); Notifications.hidePlayingNotification(this, this, handler); } + onSongChanged(); } synchronized void setNextPlaying() { @@ -1284,6 +1288,7 @@ public class DownloadService extends Service { positionCache.stop(); positionCache = null; } + onStateUpdate(); } private class PositionCache implements Runnable { @@ -1318,6 +1323,7 @@ public class DownloadService extends Service { subtractNextPosition = 0; } } + onSongProgress(); Thread.sleep(1000L); } catch(Exception e) { @@ -1989,10 +1995,16 @@ public class DownloadService extends Service { } private synchronized void checkRemovePlayed() { + boolean changed = false; while(currentPlayingIndex > 0) { downloadList.remove(0); currentPlayingIndex = downloadList.indexOf(currentPlaying); + changed = true; + } + + if(changed) { revision++; + onSongsChanged(); } } @@ -2030,6 +2042,7 @@ public class DownloadService extends Service { currentPlayingIndex = downloadList.indexOf(currentPlaying); if (revisionBefore != revision) { + onSongsChanged(); updateRemotePlaylist(); } @@ -2071,6 +2084,7 @@ public class DownloadService extends Service { currentPlayingIndex = downloadList.indexOf(currentPlaying); if (revisionBefore != revision) { + onSongsChanged(); updateRemotePlaylist(); } @@ -2338,6 +2352,71 @@ public class DownloadService extends Service { } } + public void addOnSongChangedListener(OnSongChangedListener listener) { + addOnSongChangedListener(listener, false); + } + public void addOnSongChangedListener(OnSongChangedListener listener, boolean run) { + int index = onSongChangedListeners.indexOf(listener); + if(index == -1) { + onSongChangedListeners.add(listener); + } + + if(run) { + onSongsChanged(); + onSongProgress(); + onStateUpdate(); + } + } + public void removeOnSongChangeListener(OnSongChangedListener listener) { + int index = onSongChangedListeners.indexOf(listener); + if(index != -1) { + onSongChangedListeners.remove(index); + } + } + + private void onSongChanged() { + for(final OnSongChangedListener listener: onSongChangedListeners) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onSongChanged(currentPlaying, currentPlayingIndex); + } + }); + } + } + private void onSongsChanged() { + for(final OnSongChangedListener listener: onSongChangedListeners) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onSongsChanged(downloadList, currentPlaying, currentPlayingIndex); + } + }); + } + } + private void onSongProgress() { + final Integer duration = getPlayerDuration(); + final boolean isSeekable = isSeekable(); + for(final OnSongChangedListener listener: onSongChangedListeners) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onSongProgress(currentPlaying, cachedPosition, duration, isSeekable); + } + }); + } + } + private void onStateUpdate() { + for(final OnSongChangedListener listener: onSongChangedListeners) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onStateUpdate(currentPlaying, playerState); + } + }); + } + } + private class BufferTask extends SilentBackgroundTask { private final DownloadFile downloadFile; private final int position; @@ -2441,4 +2520,11 @@ public class DownloadService extends Service { return "CheckCompletionTask (" + downloadFile + ")"; } } + + public interface OnSongChangedListener { + void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex); + void onSongsChanged(List songs, DownloadFile currentPlaying, int currentPlayingIndex); + void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable); + void onStateUpdate(DownloadFile downloadFile, PlayerState playerState); + } } -- cgit v1.2.3