/* This file is part of Subsonic. Subsonic is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Subsonic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Subsonic. If not, see . Copyright 2014 (C) Scott Jackson */ package net.nullsum.audinaut.fragments; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.Display; import android.view.GestureDetector; import android.view.GestureDetector.OnGestureListener; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.AnimationUtils; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; import android.widget.ViewFlipper; import net.nullsum.audinaut.R; import net.nullsum.audinaut.activity.SubsonicFragmentActivity; import net.nullsum.audinaut.adapter.DownloadFileAdapter; import net.nullsum.audinaut.adapter.SectionAdapter; import net.nullsum.audinaut.audiofx.EqualizerController; import net.nullsum.audinaut.domain.PlayerState; import net.nullsum.audinaut.domain.RepeatMode; import net.nullsum.audinaut.service.DownloadFile; import net.nullsum.audinaut.service.DownloadService; import net.nullsum.audinaut.service.DownloadService.OnSongChangedListener; import net.nullsum.audinaut.util.Constants; import net.nullsum.audinaut.util.DownloadFileItemHelperCallback; import net.nullsum.audinaut.util.DrawableTint; import net.nullsum.audinaut.util.FileUtil; import net.nullsum.audinaut.util.MenuUtil; import net.nullsum.audinaut.util.SilentBackgroundTask; import net.nullsum.audinaut.util.Util; import net.nullsum.audinaut.view.AutoRepeatButton; import net.nullsum.audinaut.view.FadeOutAnimation; import net.nullsum.audinaut.view.FastScroller; import net.nullsum.audinaut.view.UpdateView; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import static net.nullsum.audinaut.domain.MusicDirectory.Entry; import static net.nullsum.audinaut.domain.PlayerState.COMPLETED; import static net.nullsum.audinaut.domain.PlayerState.IDLE; import static net.nullsum.audinaut.domain.PlayerState.PAUSED; import static net.nullsum.audinaut.domain.PlayerState.STOPPED; public class NowPlayingFragment extends SubsonicFragment implements OnGestureListener, SectionAdapter.OnItemClickedListener, OnSongChangedListener { private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 10; private static final int ACTION_PREVIOUS = 1; private static final int ACTION_NEXT = 2; private static final int ACTION_REWIND = 3; private static final int ACTION_FORWARD = 4; private ViewFlipper playlistFlipper; private TextView emptyTextView; private TextView songTitleTextView; private ImageView albumArtImageView; private RecyclerView playlistView; private TextView positionTextView; private TextView durationTextView; private TextView statusTextView; private SeekBar progressBar; private AutoRepeatButton previousButton; private AutoRepeatButton nextButton; private AutoRepeatButton rewindButton; private AutoRepeatButton fastforwardButton; private View pauseButton; private View stopButton; private View startButton; private ImageButton repeatButton; private View toggleListButton; private ScheduledExecutorService executorService; private DownloadFile currentPlaying; private int swipeDistance; private int swipeVelocity; private ScheduledFuture hideControlsFuture; private List songList; private DownloadFileAdapter songListAdapter; 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. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { if (savedInstanceState.getInt(Constants.FRAGMENT_DOWNLOAD_FLIPPER) == 1) { startFlipped = true; } } primaryFragment = false; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(Constants.FRAGMENT_DOWNLOAD_FLIPPER, playlistFlipper.getDisplayedChild()); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) { rootView = inflater.inflate(R.layout.download, container, false); setTitle(R.string.button_bar_now_playing); WindowManager w = context.getWindowManager(); Display d = w.getDefaultDisplay(); swipeDistance = (d.getWidth() + d.getHeight()) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100; swipeVelocity = (d.getWidth() + d.getHeight()) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100; gestureScanner = new GestureDetector(context, this); playlistFlipper = rootView.findViewById(R.id.download_playlist_flipper); emptyTextView = rootView.findViewById(R.id.download_empty); songTitleTextView = rootView.findViewById(R.id.download_song_title); albumArtImageView = rootView.findViewById(R.id.download_album_art_image); positionTextView = rootView.findViewById(R.id.download_position); durationTextView = rootView.findViewById(R.id.download_duration); statusTextView = rootView.findViewById(R.id.download_status); progressBar = rootView.findViewById(R.id.download_progress_bar); previousButton = rootView.findViewById(R.id.download_previous); nextButton = rootView.findViewById(R.id.download_next); rewindButton = rootView.findViewById(R.id.download_rewind); fastforwardButton = rootView.findViewById(R.id.download_fastforward); pauseButton = rootView.findViewById(R.id.download_pause); stopButton = rootView.findViewById(R.id.download_stop); startButton = rootView.findViewById(R.id.download_start); repeatButton = rootView.findViewById(R.id.download_repeat); toggleListButton = rootView.findViewById(R.id.download_toggle_list); playlistView = rootView.findViewById(R.id.download_list); FastScroller fastScroller = rootView.findViewById(R.id.download_fast_scroller); fastScroller.attachRecyclerView(playlistView); setupLayoutManager(playlistView, false); ItemTouchHelper touchHelper = new ItemTouchHelper(new DownloadFileItemHelperCallback(this, true)); touchHelper.attachToRecyclerView(playlistView); View.OnTouchListener touchListener = (v, me) -> gestureScanner.onTouchEvent(me); pauseButton.setOnTouchListener(touchListener); stopButton.setOnTouchListener(touchListener); startButton.setOnTouchListener(touchListener); emptyTextView.setOnTouchListener(touchListener); albumArtImageView.setOnTouchListener((v, me) -> { if (me.getAction() == MotionEvent.ACTION_DOWN) { lastY = (int) me.getRawY(); } return gestureScanner.onTouchEvent(me); }); previousButton.setOnClickListener(view -> { warnIfStorageUnavailable(); new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { getDownloadService().previous(); return null; } }.execute(); setControlsVisible(true); }); previousButton.setOnRepeatListener(() -> changeProgress(true)); nextButton.setOnClickListener(view -> { warnIfStorageUnavailable(); new SilentBackgroundTask(context) { @Override protected Boolean doInBackground() throws Throwable { getDownloadService().next(); return true; } }.execute(); setControlsVisible(true); }); nextButton.setOnRepeatListener(() -> changeProgress(false)); rewindButton.setOnClickListener(view -> changeProgress(true)); rewindButton.setOnRepeatListener(() -> changeProgress(true)); fastforwardButton.setOnClickListener(view -> changeProgress(false)); fastforwardButton.setOnRepeatListener(() -> changeProgress(false)); pauseButton.setOnClickListener(view -> new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { getDownloadService().pause(); return null; } }.execute()); stopButton.setOnClickListener(view -> new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { getDownloadService().reset(); return null; } }.execute()); startButton.setOnClickListener(view -> { warnIfStorageUnavailable(); new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { start(); return null; } }.execute(); }); repeatButton.setOnClickListener(view -> { RepeatMode repeatMode = getDownloadService().getRepeatMode().next(); getDownloadService().setRepeatMode(repeatMode); switch (repeatMode) { case OFF: Util.toast(context, R.string.download_repeat_off); break; case ALL: Util.toast(context, R.string.download_repeat_all); break; case SINGLE: Util.toast(context, R.string.download_repeat_single); break; default: break; } updateRepeatButton(); setControlsVisible(true); }); toggleListButton.setOnClickListener(view -> { toggleFullscreenAlbumArt(); setControlsVisible(true); }); View overlay = rootView.findViewById(R.id.download_overlay_buttons); final int overlayHeight = overlay != null ? overlay.getHeight() : -1; albumArtImageView.setOnClickListener(view -> { if (overlayHeight == -1 || lastY < (view.getBottom() - overlayHeight)) { toggleFullscreenAlbumArt(); setControlsVisible(true); } }); progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(final SeekBar seekBar) { new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { getDownloadService().seekTo(progressBar.getProgress()); return null; } @Override protected void done(Void result) { seekInProgress = false; } }.execute(); } @Override public void onStartTrackingTouch(final SeekBar seekBar) { seekInProgress = true; } @Override public void onProgressChanged(final SeekBar seekBar, final int position, final boolean fromUser) { if (fromUser) { positionTextView.setText(Util.formatDuration(position / 1000)); setControlsVisible(true); } } }); return rootView; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) { DownloadService downloadService = getDownloadService(); if (Util.isOffline(context)) { menuInflater.inflate(R.menu.nowplaying_offline, menu); } else { menuInflater.inflate(R.menu.nowplaying, menu); } if (downloadService != null && downloadService.isRemovePlayed()) { menu.findItem(R.id.menu_remove_played).setChecked(true); } if (downloadService != null) { SharedPreferences prefs = Util.getPreferences(context); boolean equalizerOn = prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false); if (equalizerOn && downloadService != null) { if (downloadService.getEqualizerController() != null && downloadService.getEqualizerController().isEnabled()) { menu.findItem(R.id.menu_equalizer).setChecked(true); } } } else { menu.removeItem(R.id.menu_equalizer); } if (Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_BATCH_MODE, false)) { menu.findItem(R.id.menu_batch_mode).setChecked(true); } } @Override public boolean onOptionsItemSelected(MenuItem menuItem) { return menuItemSelected(menuItem.getItemId(), null) || super.onOptionsItemSelected(menuItem); } @Override public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView updateView, DownloadFile downloadFile) { if (Util.isOffline(context)) { menuInflater.inflate(R.menu.nowplaying_context_offline, menu); } else { menuInflater.inflate(R.menu.nowplaying_context, menu); } if (downloadFile.getSong().getParent() == null) { menu.findItem(R.id.menu_show_album).setVisible(false); menu.findItem(R.id.menu_show_artist).setVisible(false); } MenuUtil.hideMenuItems(context, menu, updateView); } @Override public boolean onContextItemSelected(MenuItem menuItem, UpdateView updateView, DownloadFile downloadFile) { return onContextItemSelected(menuItem, downloadFile.getSong()) || menuItemSelected(menuItem.getItemId(), downloadFile); } private boolean menuItemSelected(int menuItemId, final DownloadFile song) { switch (menuItemId) { case R.id.menu_show_album: case R.id.menu_show_artist: Entry entry = song.getSong(); Intent intent = new Intent(context, SubsonicFragmentActivity.class); intent.putExtra(Constants.INTENT_EXTRA_VIEW_ALBUM, true); String albumId; String albumName; if (menuItemId == R.id.menu_show_album) { albumId = entry.getAlbumId(); albumName = entry.getAlbum(); } else { albumId = entry.getArtistId(); albumName = entry.getArtist(); intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, true); } intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, albumId); intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, albumName); intent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, "Artist"); if (Util.isOffline(context)) { try { // This should only be successful if this is a online song in offline mode Integer.parseInt(entry.getParent()); String root = FileUtil.getMusicDirectory(context).getPath(); String id = root + "/" + entry.getPath(); id = id.substring(0, id.lastIndexOf("/")); if (menuItemId == R.id.menu_show_album) { intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, id); } id = id.substring(0, id.lastIndexOf("/")); if (menuItemId != R.id.menu_show_album) { intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, id); intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist()); intent.removeExtra(Constants.INTENT_EXTRA_NAME_CHILD_ID); } } catch (Exception e) { // Do nothing, entry.getParent() is fine } } intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); Util.startActivityWithoutTransition(context, intent); return true; case R.id.menu_remove_all: Util.confirmDialog(context, R.string.download_menu_remove_all, "", (dialog, which) -> new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { getDownloadService().setShufflePlayEnabled(false); getDownloadService().clear(); return null; } @Override protected void done(Void result) { context.closeNowPlaying(); } }.execute()); return true; case R.id.menu_remove_played: if (getDownloadService().isRemovePlayed()) { getDownloadService().setRemovePlayed(false); } else { getDownloadService().setRemovePlayed(true); } context.supportInvalidateOptionsMenu(); return true; case R.id.menu_shuffle: new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { getDownloadService().shuffle(); return null; } @Override protected void done(Void result) { Util.toast(context, R.string.download_menu_shuffle_notification); } }.execute(); return true; case R.id.menu_save_playlist: List entries = new LinkedList<>(); for (DownloadFile downloadFile : getDownloadService().getSongs()) { entries.add(downloadFile.getSong()); } createNewPlaylist(entries, true); return true; case R.id.menu_info: displaySongInfo(song.getSong()); return true; case R.id.menu_equalizer: { DownloadService downloadService = getDownloadService(); if (downloadService != null) { EqualizerController controller = downloadService.getEqualizerController(); if (controller != null) { SubsonicFragment fragment = new EqualizerFragment(); replaceFragment(fragment); setControlsVisible(true); return true; } } // Any failed condition will get here Util.toast(context, "Failed to start equalizer. Try restarting."); return true; } case R.id.menu_batch_mode: if (Util.isBatchMode(context)) { Util.setBatchMode(context, false); songListAdapter.notifyDataSetChanged(); } else { Util.setBatchMode(context, true); songListAdapter.notifyDataSetChanged(); } context.supportInvalidateOptionsMenu(); return true; default: return false; } } @Override public void onResume() { super.onResume(); if (this.primaryFragment) { onResumeHandlers(); } else { update(); } } private void onResumeHandlers() { executorService = Executors.newSingleThreadScheduledExecutor(); setControlsVisible(true); final DownloadService downloadService = getDownloadService(); if (downloadService == null || downloadService.getCurrentPlaying() == null || startFlipped) { playlistFlipper.setDisplayedChild(1); startFlipped = false; } if (currentPlaying == null && downloadService != null && currentPlaying == downloadService.getCurrentPlaying()) { getImageLoader().loadImage(albumArtImageView, null, true, false); } context.runWhenServiceAvailable(() -> { if (primaryFragment) { DownloadService downloadService1 = getDownloadService(); downloadService1.addOnSongChangedListener(NowPlayingFragment.this); } updateRepeatButton(); updateTitle(); }); } @Override public void onPause() { super.onPause(); onPauseHandlers(); } private void onPauseHandlers() { if (executorService != null) { DownloadService downloadService = getDownloadService(); if (downloadService != null) { downloadService.removeOnSongChangeListener(this); } playlistFlipper.setDisplayedChild(0); } } @Override public void setPrimaryFragment(boolean primary) { super.setPrimaryFragment(primary); if (rootView != null) { if (primary) { onResumeHandlers(); } else { onPauseHandlers(); } } } @Override void setTitle(int title) { this.title = context.getResources().getString(title); if (this.primaryFragment) { context.setTitle(this.title); } } @Override void setSubtitle(CharSequence title) { this.subtitle = title; if (this.primaryFragment) { context.setSubtitle(title); } } @Override public SectionAdapter getCurrentAdapter() { return songListAdapter; } private void scheduleHideControls() { if (hideControlsFuture != null) { hideControlsFuture.cancel(false); } final Handler handler = new Handler(); Runnable runnable = () -> handler.post(() -> setControlsVisible(false)); hideControlsFuture = executorService.schedule(runnable, 3000L, TimeUnit.MILLISECONDS); } private void setControlsVisible(boolean visible) { try { long duration = 1700L; FadeOutAnimation.createAndStart(rootView.findViewById(R.id.download_overlay_buttons), !visible, duration); if (visible) { scheduleHideControls(); } } catch (Exception ignored) { } } // Scroll to current playing/downloading. private void scrollToCurrent() { if (getDownloadService() == null || songListAdapter == null) { scrollWhenLoaded = true; return; } // Try to get position of current playing/downloading int position = songListAdapter.getItemPosition(currentPlaying); if (position == -1) { DownloadFile currentDownloading = getDownloadService().getCurrentDownloading(); position = songListAdapter.getItemPosition(currentDownloading); } // If found, scroll to it if (position != -1) { // RecyclerView.scrollToPosition just puts it on the screen (ie: bottom if scrolled below it) LinearLayoutManager layoutManager = (LinearLayoutManager) playlistView.getLayoutManager(); layoutManager.scrollToPositionWithOffset(position, 0); } } private void update() { if (startFlipped) { startFlipped = false; scrollToCurrent(); } } private void toggleFullscreenAlbumArt() { if (playlistFlipper.getDisplayedChild() == 1) { playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(context, R.anim.push_down_in)); playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.push_down_out)); playlistFlipper.setDisplayedChild(0); } else { scrollToCurrent(); playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(context, R.anim.push_up_in)); playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.push_up_out)); playlistFlipper.setDisplayedChild(1); UpdateView.triggerUpdate(); } } private void start() { DownloadService service = getDownloadService(); PlayerState state = service.getPlayerState(); if (state == PAUSED || state == COMPLETED || state == STOPPED) { service.start(); } else if (state == STOPPED || state == IDLE) { warnIfStorageUnavailable(); int current = service.getCurrentPlayingIndex(); // TODO: Use play() method. if (current == -1) { service.play(0); } else { service.play(current); } } } private void changeProgress(final boolean rewind) { final DownloadService downloadService = getDownloadService(); if (downloadService == null) { return; } new SilentBackgroundTask(context) { int seekTo; @Override protected Void doInBackground() throws Throwable { if (rewind) { seekTo = downloadService.rewind(); } else { seekTo = downloadService.fastForward(); } return null; } @Override protected void done(Void result) { progressBar.setProgress(seekTo); } }.execute(); } @Override public boolean onDown(MotionEvent me) { setControlsVisible(true); return false; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { final DownloadService downloadService = getDownloadService(); if (downloadService == null || e1 == null || e2 == null) { return false; } // Right to Left swipe int action = 0; if (e1.getX() - e2.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) { action = ACTION_NEXT; } // Left to Right swipe else if (e2.getX() - e1.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) { action = ACTION_PREVIOUS; } // Top to Bottom swipe else if (e2.getY() - e1.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) { action = ACTION_FORWARD; } // Bottom to Top swipe else if (e1.getY() - e2.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) { action = ACTION_REWIND; } if (action > 0) { final int performAction = action; warnIfStorageUnavailable(); new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { switch (performAction) { case ACTION_NEXT: downloadService.next(); break; case ACTION_PREVIOUS: downloadService.previous(); break; case ACTION_FORWARD: downloadService.seekTo(downloadService.getPlayerPosition() + DownloadService.FAST_FORWARD); break; case ACTION_REWIND: downloadService.seekTo(downloadService.getPlayerPosition() - DownloadService.REWIND); break; } return null; } }.execute(); return true; } else { return false; } } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return false; } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } @Override public void onItemClicked(UpdateView updateView, final DownloadFile item) { warnIfStorageUnavailable(); new SilentBackgroundTask(context) { @Override protected Void doInBackground() throws Throwable { getDownloadService().play(item); return null; } }.execute(); } @Override public void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex) { this.currentPlaying = currentPlaying; setupSubtitle(currentPlayingIndex); if (currentPlaying != null && !currentPlaying.isSong()) { previousButton.setVisibility(View.GONE); nextButton.setVisibility(View.GONE); rewindButton.setVisibility(View.VISIBLE); fastforwardButton.setVisibility(View.VISIBLE); } else { previousButton.setVisibility(View.VISIBLE); nextButton.setVisibility(View.VISIBLE); rewindButton.setVisibility(View.GONE); fastforwardButton.setVisibility(View.GONE); } updateTitle(); } private void setupSubtitle(int currentPlayingIndex) { if (currentPlaying != null) { Entry song = currentPlaying.getSong(); songTitleTextView.setText(song.getTitle()); getImageLoader().loadImage(albumArtImageView, song, true, true); DownloadService downloadService = getDownloadService(); if (downloadService.isShufflePlayEnabled()) { setSubtitle(context.getResources().getString(R.string.download_playerstate_playing_shuffle)); } else { setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex + 1, currentPlayingSize)); } } else { songTitleTextView.setText(null); getImageLoader().loadImage(albumArtImageView, null, true, false); 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); if (scrollWhenLoaded) { scrollToCurrent(); scrollWhenLoaded = false; } if (this.currentPlaying != currentPlaying) { onSongChanged(currentPlaying, currentPlayingIndex); onMetadataUpdate(currentPlaying != null ? currentPlaying.getSong() : null, DownloadService.METADATA_UPDATED_ALL); } else { setupSubtitle(currentPlayingIndex); } toggleListButton.setVisibility(View.VISIBLE); repeatButton.setVisibility(View.VISIBLE); } @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(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) { Entry entry = currentPlaying.getSong(); if (entry.getAlbum() != null) { String artist = ""; if (entry.getArtist() != null) { artist = currentPlaying.getSong().getArtist().trim() + " - "; } statusTextView.setText(artist + entry.getAlbum().trim()); } else { statusTextView.setText(null); } } 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; } } @Override public void onMetadataUpdate(Entry song, int fieldChange) { if (song != null && albumArtImageView != null && fieldChange == DownloadService.METADATA_UPDATED_COVER_ART) { getImageLoader().loadImage(albumArtImageView, song, true, true); } } private void updateRepeatButton() { DownloadService downloadService = getDownloadService(); switch (downloadService.getRepeatMode()) { case OFF: repeatButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.media_button_repeat_off)); break; case ALL: repeatButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.media_button_repeat_all)); break; case SINGLE: repeatButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.media_button_repeat_single)); break; default: break; } } private void updateTitle() { String title = context.getResources().getString(R.string.button_bar_now_playing); setTitle(title); } @Override protected List getSelectedEntries() { List selected = getCurrentAdapter().getSelected(); List entries = new ArrayList<>(); for (DownloadFile downloadFile : selected) { if (downloadFile.getSong() != null) { entries.add(downloadFile.getSong()); } } return entries; } }