/* 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 2009 (C) Sindre Mehus */ package org.moire.ultrasonic.activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import timber.log.Timber; import android.view.ContextMenu; 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.AdapterView; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.SeekBar; import android.widget.TextView; import android.widget.ViewFlipper; import com.mobeta.android.dslv.DragSortListView; import org.koin.java.KoinJavaComponent; import org.moire.ultrasonic.R; import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory.Entry; import org.moire.ultrasonic.domain.PlayerState; import org.moire.ultrasonic.domain.RepeatMode; import org.moire.ultrasonic.featureflags.Feature; import org.moire.ultrasonic.featureflags.FeatureStorage; import org.moire.ultrasonic.service.DownloadFile; import org.moire.ultrasonic.service.MediaPlayerController; import org.moire.ultrasonic.service.MusicService; import org.moire.ultrasonic.service.MusicServiceFactory; import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.SilentBackgroundTask; import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.view.AutoRepeatButton; import org.moire.ultrasonic.view.SongListAdapter; import org.moire.ultrasonic.view.VisualizerView; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static org.moire.ultrasonic.domain.PlayerState.COMPLETED; import static org.moire.ultrasonic.domain.PlayerState.IDLE; import static org.moire.ultrasonic.domain.PlayerState.PAUSED; import static org.moire.ultrasonic.domain.PlayerState.STOPPED; public class DownloadActivity extends SubsonicTabActivity implements OnGestureListener { private static final int DIALOG_SAVE_PLAYLIST = 100; private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 5; private ViewFlipper playlistFlipper; private TextView emptyTextView; private TextView songTitleTextView; private TextView albumTextView; private TextView artistTextView; private ImageView albumArtImageView; private DragSortListView playlistView; private TextView positionTextView; private TextView downloadTrackTextView; private TextView downloadTotalDurationTextView; private TextView durationTextView; private static SeekBar progressBar; private View pauseButton; private View stopButton; private View startButton; private ImageView repeatButton; private ScheduledExecutorService executorService; private DownloadFile currentPlaying; private Entry currentSong; private long currentRevision; private EditText playlistNameView; private GestureDetector gestureScanner; private int swipeDistance; private int swipeVelocity; private VisualizerView visualizerView; private boolean visualizerAvailable; private boolean equalizerAvailable; private boolean jukeboxAvailable; private SilentBackgroundTask onProgressChangedTask; LinearLayout visualizerViewLayout; private MenuItem starMenuItem; private LinearLayout ratingLinearLayout; private ImageView fiveStar1ImageView; private ImageView fiveStar2ImageView; private ImageView fiveStar3ImageView; private ImageView fiveStar4ImageView; private ImageView fiveStar5ImageView; private boolean useFiveStarRating; private Drawable hollowStar; private Drawable fullStar; /** * Called when the activity is first created. */ @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.download); final WindowManager windowManager = getWindowManager(); final Display display = windowManager.getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; int height = size.y; useFiveStarRating = KoinJavaComponent.get(FeatureStorage.class).isFeatureEnabled(Feature.FIVE_STAR_RATING); swipeDistance = (width + height) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100; swipeVelocity = swipeDistance; gestureScanner = new GestureDetector(this, this); playlistFlipper = (ViewFlipper) findViewById(R.id.download_playlist_flipper); emptyTextView = (TextView) findViewById(R.id.download_empty); songTitleTextView = (TextView) findViewById(R.id.download_song_title); albumTextView = (TextView) findViewById(R.id.download_album); artistTextView = (TextView) findViewById(R.id.download_artist); albumArtImageView = (ImageView) findViewById(R.id.download_album_art_image); positionTextView = (TextView) findViewById(R.id.download_position); downloadTrackTextView = (TextView) findViewById(R.id.download_track); downloadTotalDurationTextView = (TextView) findViewById(R.id.download_total_duration); durationTextView = (TextView) findViewById(R.id.download_duration); progressBar = (SeekBar) findViewById(R.id.download_progress_bar); playlistView = (DragSortListView) findViewById(R.id.download_list); final AutoRepeatButton previousButton = (AutoRepeatButton) findViewById(R.id.download_previous); final AutoRepeatButton nextButton = (AutoRepeatButton) findViewById(R.id.download_next); pauseButton = findViewById(R.id.download_pause); stopButton = findViewById(R.id.download_stop); startButton = findViewById(R.id.download_start); final View shuffleButton = findViewById(R.id.download_shuffle); repeatButton = (ImageView) findViewById(R.id.download_repeat); visualizerViewLayout = (LinearLayout) findViewById(R.id.download_visualizer_view_layout); ratingLinearLayout = findViewById(R.id.song_rating); fiveStar1ImageView = findViewById(R.id.song_five_star_1); fiveStar2ImageView = findViewById(R.id.song_five_star_2); fiveStar3ImageView = findViewById(R.id.song_five_star_3); fiveStar4ImageView = findViewById(R.id.song_five_star_4); fiveStar5ImageView = findViewById(R.id.song_five_star_5); if (!useFiveStarRating) ratingLinearLayout.setVisibility(View.GONE); hollowStar = Util.getDrawableFromAttribute(this, R.attr.star_hollow); fullStar = Util.getDrawableFromAttribute(this, R.attr.star_full); fiveStar1ImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { setSongRating(1); } }); fiveStar2ImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { setSongRating(2); } }); fiveStar3ImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { setSongRating(3); } }); fiveStar4ImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { setSongRating(4); } }); fiveStar5ImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { setSongRating(5); } }); View.OnTouchListener touchListener = new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent me) { return gestureScanner.onTouchEvent(me); } }; albumArtImageView.setOnTouchListener(touchListener); albumArtImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { toggleFullScreenAlbumArt(); } }); previousButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { warnIfNetworkOrStorageUnavailable(); new SilentBackgroundTask(DownloadActivity.this) { @Override protected Void doInBackground() throws Throwable { getMediaPlayerController().previous(); return null; } @Override protected void done(final Void result) { onCurrentChanged(); onSliderProgressChanged(); } }.execute(); } }); previousButton.setOnRepeatListener(new Runnable() { @Override public void run() { int incrementTime = Util.getIncrementTime(DownloadActivity.this); changeProgress(-incrementTime); } }); nextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { warnIfNetworkOrStorageUnavailable(); new SilentBackgroundTask(DownloadActivity.this) { @Override protected Boolean doInBackground() throws Throwable { if (getMediaPlayerController().getCurrentPlayingNumberOnPlaylist() < getMediaPlayerController().getPlaylistSize() - 1) { getMediaPlayerController().next(); return true; } else { return false; } } @Override protected void done(final Boolean result) { if (result) { onCurrentChanged(); onSliderProgressChanged(); } } }.execute(); } }); nextButton.setOnRepeatListener(new Runnable() { @Override public void run() { int incrementTime = Util.getIncrementTime(DownloadActivity.this); changeProgress(incrementTime); } }); pauseButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { new SilentBackgroundTask(DownloadActivity.this) { @Override protected Void doInBackground() throws Throwable { getMediaPlayerController().pause(); return null; } @Override protected void done(final Void result) { onCurrentChanged(); onSliderProgressChanged(); } }.execute(); } }); stopButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { new SilentBackgroundTask(DownloadActivity.this) { @Override protected Void doInBackground() throws Throwable { getMediaPlayerController().reset(); return null; } @Override protected void done(final Void result) { onCurrentChanged(); onSliderProgressChanged(); } }.execute(); } }); startButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { warnIfNetworkOrStorageUnavailable(); new SilentBackgroundTask(DownloadActivity.this) { @Override protected Void doInBackground() throws Throwable { start(); return null; } @Override protected void done(final Void result) { onCurrentChanged(); onSliderProgressChanged(); } }.execute(); } }); shuffleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { getMediaPlayerController().shuffle(); Util.toast(DownloadActivity.this, R.string.download_menu_shuffle_notification); } }); repeatButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { final RepeatMode repeatMode = getMediaPlayerController().getRepeatMode().next(); getMediaPlayerController().setRepeatMode(repeatMode); onDownloadListChanged(); switch (repeatMode) { case OFF: Util.toast(DownloadActivity.this, R.string.download_repeat_off); break; case ALL: Util.toast(DownloadActivity.this, R.string.download_repeat_all); break; case SINGLE: Util.toast(DownloadActivity.this, R.string.download_repeat_single); break; default: break; } } }); progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(final SeekBar seekBar) { new SilentBackgroundTask(DownloadActivity.this) { @Override protected Void doInBackground() throws Throwable { getMediaPlayerController().seekTo(getProgressBar().getProgress()); return null; } @Override protected void done(final Void result) { onSliderProgressChanged(); } }.execute(); } @Override public void onStartTrackingTouch(final SeekBar seekBar) { } @Override public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) { } }); playlistView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(final AdapterView parent, final View view, final int position, final long id) { warnIfNetworkOrStorageUnavailable(); new SilentBackgroundTask(DownloadActivity.this) { @Override protected Void doInBackground() throws Throwable { getMediaPlayerController().play(position); return null; } @Override protected void done(final Void result) { onCurrentChanged(); onSliderProgressChanged(); } }.execute(); } }); registerForContextMenu(playlistView); final MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController != null && getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, false)) { warnIfNetworkOrStorageUnavailable(); mediaPlayerController.setShufflePlayEnabled(true); } visualizerAvailable = (mediaPlayerController != null) && (mediaPlayerController.getVisualizerController() != null); equalizerAvailable = (mediaPlayerController != null) && (mediaPlayerController.getEqualizerController() != null); new Thread(new Runnable() { @Override public void run() { try { MediaPlayerController mediaPlayerController = getMediaPlayerController(); jukeboxAvailable = (mediaPlayerController != null) && (mediaPlayerController.isJukeboxAvailable()); } catch (Exception e) { Timber.e(e); } } }).start(); final View nowPlayingMenuItem = findViewById(R.id.menu_now_playing); menuDrawer.setActiveView(nowPlayingMenuItem); if (visualizerAvailable) { visualizerView = new VisualizerView(this); visualizerViewLayout.addView(visualizerView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); if (!visualizerView.isActive()) { visualizerViewLayout.setVisibility(View.GONE); } else { visualizerViewLayout.setVisibility(View.VISIBLE); } visualizerView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(final View view, final MotionEvent motionEvent) { visualizerView.setActive(!visualizerView.isActive()); getMediaPlayerController().setShowVisualization(visualizerView.isActive()); return true; } }); } else { visualizerViewLayout.setVisibility(View.GONE); } } @Override protected void onResume() { super.onResume(); final MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController == null || mediaPlayerController.getCurrentPlaying() == null) { playlistFlipper.setDisplayedChild(1); } final Handler handler = new Handler(); final Runnable runnable = new Runnable() { @Override public void run() { handler.post(new Runnable() { @Override public void run() { update(); } }); } }; executorService = Executors.newSingleThreadScheduledExecutor(); executorService.scheduleWithFixedDelay(runnable, 0L, 250L, TimeUnit.MILLISECONDS); if (mediaPlayerController != null && mediaPlayerController.getKeepScreenOn()) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } else { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } if (visualizerView != null) { visualizerView.setActive(mediaPlayerController != null && mediaPlayerController.getShowVisualization()); } invalidateOptionsMenu(); } // Scroll to current playing/downloading. private void scrollToCurrent() { if (getMediaPlayerController() == null) { return; } ListAdapter adapter = playlistView.getAdapter(); if (adapter != null) { int count = adapter.getCount(); for (int i = 0; i < count; i++) { if (currentPlaying == playlistView.getItemAtPosition(i)) { playlistView.smoothScrollToPositionFromTop(i, 40); return; } } final DownloadFile currentDownloading = getMediaPlayerController().getCurrentDownloading(); for (int i = 0; i < count; i++) { if (currentDownloading == playlistView.getItemAtPosition(i)) { playlistView.smoothScrollToPositionFromTop(i, 40); return; } } } } @Override protected void onPause() { super.onPause(); executorService.shutdown(); if (visualizerView != null) { visualizerView.setActive(false); } } @Override protected Dialog onCreateDialog(final int id) { if (id == DIALOG_SAVE_PLAYLIST) { final AlertDialog.Builder builder; final LayoutInflater layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); final View layout = layoutInflater.inflate(R.layout.save_playlist, (ViewGroup) findViewById(R.id.save_playlist_root)); if (layout != null) { playlistNameView = (EditText) layout.findViewById(R.id.save_playlist_name); } builder = new AlertDialog.Builder(this); builder.setTitle(R.string.download_playlist_title); builder.setMessage(R.string.download_playlist_name); builder.setPositiveButton(R.string.common_save, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int clickId) { savePlaylistInBackground(String.valueOf(playlistNameView.getText())); } }); builder.setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int clickId) { dialog.cancel(); } }); builder.setView(layout); builder.setCancelable(true); return builder.create(); } else { return super.onCreateDialog(id); } } @Override protected void onPrepareDialog(final int id, final Dialog dialog) { if (id == DIALOG_SAVE_PLAYLIST) { final String playlistName = (getMediaPlayerController() != null) ? getMediaPlayerController().getSuggestedPlaylistName() : null; if (playlistName != null) { playlistNameView.setText(playlistName); } else { final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); playlistNameView.setText(dateFormat.format(new Date())); } } } @Override public boolean onCreateOptionsMenu(final Menu menu) { final MenuInflater menuInflater = getMenuInflater(); menuInflater.inflate(R.menu.nowplaying, menu); super.onCreateOptionsMenu(menu); return true; } @Override public boolean onPrepareOptionsMenu(final Menu menu) { super.onPrepareOptionsMenu(menu); final MenuItem screenOption = menu.findItem(R.id.menu_item_screen_on_off); final MenuItem jukeboxOption = menu.findItem(R.id.menu_item_jukebox); final MenuItem equalizerMenuItem = menu.findItem(R.id.menu_item_equalizer); final MenuItem visualizerMenuItem = menu.findItem(R.id.menu_item_visualizer); final MenuItem shareMenuItem = menu.findItem(R.id.menu_item_share); starMenuItem = menu.findItem(R.id.menu_item_star); MenuItem bookmarkMenuItem = menu.findItem(R.id.menu_item_bookmark_set); MenuItem bookmarkRemoveMenuItem = menu.findItem(R.id.menu_item_bookmark_delete); if (ActiveServerProvider.Companion.isOffline(this)) { if (shareMenuItem != null) { shareMenuItem.setVisible(false); } if (starMenuItem != null) { starMenuItem.setVisible(false); } if (bookmarkMenuItem != null) { bookmarkMenuItem.setVisible(false); } if (bookmarkRemoveMenuItem != null) { bookmarkRemoveMenuItem.setVisible(false); } } if (equalizerMenuItem != null) { equalizerMenuItem.setEnabled(equalizerAvailable); equalizerMenuItem.setVisible(equalizerAvailable); } if (visualizerMenuItem != null) { visualizerMenuItem.setEnabled(visualizerAvailable); visualizerMenuItem.setVisible(visualizerAvailable); } final MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController != null) { DownloadFile downloadFile = mediaPlayerController.getCurrentPlaying(); if (downloadFile != null) { currentSong = downloadFile.getSong(); } if (useFiveStarRating) starMenuItem.setVisible(false); if (currentSong != null) { if (starMenuItem != null) { starMenuItem.setIcon(currentSong.getStarred() ? fullStar : hollowStar); } } else { if (starMenuItem != null) { starMenuItem.setIcon(hollowStar); } } if (mediaPlayerController.getKeepScreenOn()) { if (screenOption != null) { screenOption.setTitle(R.string.download_menu_screen_off); } } else { if (screenOption != null) { screenOption.setTitle(R.string.download_menu_screen_on); } } if (jukeboxOption != null) { jukeboxOption.setEnabled(jukeboxAvailable); jukeboxOption.setVisible(jukeboxAvailable); if (mediaPlayerController.isJukeboxEnabled()) { jukeboxOption.setTitle(R.string.download_menu_jukebox_off); } else { jukeboxOption.setTitle(R.string.download_menu_jukebox_on); } } } return true; } @Override public void onCreateContextMenu(final ContextMenu menu, final View view, final ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); if (view == playlistView) { final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; final DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position); final MenuInflater menuInflater = getMenuInflater(); menuInflater.inflate(R.menu.nowplaying_context, menu); Entry song = null; if (downloadFile != null) { song = downloadFile.getSong(); } if (song != null && song.getParent() == null) { MenuItem menuItem = menu.findItem(R.id.menu_show_album); if (menuItem != null) { menuItem.setVisible(false); } } if (ActiveServerProvider.Companion.isOffline(this) || !Util.getShouldUseId3Tags(this)) { MenuItem menuItem = menu.findItem(R.id.menu_show_artist); if (menuItem != null) { menuItem.setVisible(false); } } if (ActiveServerProvider.Companion.isOffline(this)) { MenuItem menuItem = menu.findItem(R.id.menu_lyrics); if (menuItem != null) { menuItem.setVisible(false); } } } } @Override public boolean onContextItemSelected(final MenuItem menuItem) { final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); DownloadFile downloadFile = null; if (info != null) { downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position); } return menuItemSelected(menuItem.getItemId(), downloadFile) || super.onContextItemSelected(menuItem); } @Override public boolean onOptionsItemSelected(final MenuItem menuItem) { return menuItemSelected(menuItem.getItemId(), null) || super.onOptionsItemSelected(menuItem); } private boolean menuItemSelected(final int menuItemId, final DownloadFile song) { Entry entry = null; if (song != null) { entry = song.getSong(); } switch (menuItemId) { case R.id.menu_show_artist: if (entry == null) { return false; } if (Util.getShouldUseId3Tags(DownloadActivity.this)) { Intent intent = new Intent(DownloadActivity.this, SelectAlbumActivity.class); intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId()); intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist()); intent.putExtra(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getArtistId()); intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, true); startActivityForResultWithoutTransition(DownloadActivity.this, intent); } return true; case R.id.menu_show_album: if (entry == null) { return false; } Intent intent = new Intent(this, SelectAlbumActivity.class); String albumId = Util.getShouldUseId3Tags(this) ? entry.getAlbumId() : entry.getParent(); intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, albumId); intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getAlbum()); intent.putExtra(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true); intent.putExtra(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getParent()); startActivityForResultWithoutTransition(this, intent); return true; case R.id.menu_lyrics: if (entry == null) { return false; } intent = new Intent(this, LyricsActivity.class); intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, entry.getArtist()); intent.putExtra(Constants.INTENT_EXTRA_NAME_TITLE, entry.getTitle()); startActivityForResultWithoutTransition(this, intent); return true; case R.id.menu_remove: getMediaPlayerController().remove(song); onDownloadListChanged(); return true; case R.id.menu_item_screen_on_off: if (getMediaPlayerController().getKeepScreenOn()) { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getMediaPlayerController().setKeepScreenOn(false); } else { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getMediaPlayerController().setKeepScreenOn(true); } return true; case R.id.menu_shuffle: getMediaPlayerController().shuffle(); Util.toast(this, R.string.download_menu_shuffle_notification); return true; case R.id.menu_item_equalizer: startActivity(new Intent(DownloadActivity.this, EqualizerActivity.class)); return true; case R.id.menu_item_visualizer: final boolean active = !visualizerView.isActive(); visualizerView.setActive(active); if (!visualizerView.isActive()) { visualizerViewLayout.setVisibility(View.GONE); } else { visualizerViewLayout.setVisibility(View.VISIBLE); } getMediaPlayerController().setShowVisualization(visualizerView.isActive()); Util.toast(DownloadActivity.this, active ? R.string.download_visualizer_on : R.string.download_visualizer_off); return true; case R.id.menu_item_jukebox: final boolean jukeboxEnabled = !getMediaPlayerController().isJukeboxEnabled(); getMediaPlayerController().setJukeboxEnabled(jukeboxEnabled); Util.toast(DownloadActivity.this, jukeboxEnabled ? R.string.download_jukebox_on : R.string.download_jukebox_off, false); return true; case R.id.menu_item_toggle_list: toggleFullScreenAlbumArt(); return true; case R.id.menu_item_clear_playlist: getMediaPlayerController().setShufflePlayEnabled(false); getMediaPlayerController().clear(); onDownloadListChanged(); return true; case R.id.menu_item_save_playlist: if (getMediaPlayerController().getPlaylistSize() > 0) { showDialog(DIALOG_SAVE_PLAYLIST); } return true; case R.id.menu_item_star: if (currentSong == null) { return true; } final boolean isStarred = currentSong.getStarred(); final String id = currentSong.getId(); if (isStarred) { starMenuItem.setIcon(hollowStar); currentSong.setStarred(false); } else { starMenuItem.setIcon(fullStar); currentSong.setStarred(true); } new Thread(new Runnable() { @Override public void run() { final MusicService musicService = MusicServiceFactory.getMusicService(DownloadActivity.this); try { if (isStarred) { musicService.unstar(id, null, null, DownloadActivity.this, null); } else { musicService.star(id, null, null, DownloadActivity.this, null); } } catch (Exception e) { Timber.e(e); } } }).start(); return true; case R.id.menu_item_bookmark_set: if (currentSong == null) { return true; } final String songId = currentSong.getId(); final int playerPosition = getMediaPlayerController().getPlayerPosition(); currentSong.setBookmarkPosition(playerPosition); String bookmarkTime = Util.formatTotalDuration(playerPosition, true); new Thread(new Runnable() { @Override public void run() { final MusicService musicService = MusicServiceFactory.getMusicService(DownloadActivity.this); try { musicService.createBookmark(songId, playerPosition, DownloadActivity.this, null); } catch (Exception e) { Timber.e(e); } } }).start(); String msg = getResources().getString(R.string.download_bookmark_set_at_position, bookmarkTime); Util.toast(DownloadActivity.this, msg); return true; case R.id.menu_item_bookmark_delete: if (currentSong == null) { return true; } final String bookmarkSongId = currentSong.getId(); currentSong.setBookmarkPosition(0); new Thread(new Runnable() { @Override public void run() { final MusicService musicService = MusicServiceFactory.getMusicService(DownloadActivity.this); try { musicService.deleteBookmark(bookmarkSongId, DownloadActivity.this, null); } catch (Exception e) { Timber.e(e); } } }).start(); Util.toast(DownloadActivity.this, R.string.download_bookmark_removed); return true; case R.id.menu_item_share: MediaPlayerController mediaPlayerController = getMediaPlayerController(); List entries = new ArrayList(); if (mediaPlayerController != null) { List downloadServiceSongs = mediaPlayerController.getPlayList(); if (downloadServiceSongs != null) { for (DownloadFile downloadFile : downloadServiceSongs) { if (downloadFile != null) { Entry playlistEntry = downloadFile.getSong(); if (playlistEntry != null) { entries.add(playlistEntry); } } } } } createShare(entries); return true; default: return false; } } private void update() { MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController == null) { return; } if (currentRevision != mediaPlayerController.getPlayListUpdateRevision()) { onDownloadListChanged(); } if (currentPlaying != mediaPlayerController.getCurrentPlaying()) { onCurrentChanged(); } onSliderProgressChanged(); invalidateOptionsMenu(); } private void savePlaylistInBackground(final String playlistName) { Util.toast(DownloadActivity.this, getResources().getString(R.string.download_playlist_saving, playlistName)); getMediaPlayerController().setSuggestedPlaylistName(playlistName); new SilentBackgroundTask(this) { @Override protected Void doInBackground() throws Throwable { final List entries = new LinkedList(); for (final DownloadFile downloadFile : getMediaPlayerController().getPlayList()) { entries.add(downloadFile.getSong()); } final MusicService musicService = MusicServiceFactory.getMusicService(DownloadActivity.this); musicService.createPlaylist(null, playlistName, entries, DownloadActivity.this, null); return null; } @Override protected void done(final Void result) { Util.toast(DownloadActivity.this, R.string.download_playlist_done); } @Override protected void error(final Throwable error) { final String msg = String.format("%s %s", getResources().getString(R.string.download_playlist_error), getErrorMessage(error)); Util.toast(DownloadActivity.this, msg); } }.execute(); } private void toggleFullScreenAlbumArt() { if (playlistFlipper.getDisplayedChild() == 1) { playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_down_in)); playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_down_out)); playlistFlipper.setDisplayedChild(0); } else { playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_up_in)); playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_up_out)); playlistFlipper.setDisplayedChild(1); } scrollToCurrent(); } private void start() { final MediaPlayerController service = getMediaPlayerController(); final PlayerState state = service.getPlayerState(); if (state == PAUSED || state == COMPLETED || state == STOPPED) { service.start(); } else if (state == IDLE) { warnIfNetworkOrStorageUnavailable(); final int current = getMediaPlayerController().getCurrentPlayingNumberOnPlaylist(); if (current == -1) { service.play(0); } else { service.play(current); } } } private void onDownloadListChanged() { final MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController == null) { return; } final List list = mediaPlayerController.getPlayList(); emptyTextView.setText(R.string.download_empty); final SongListAdapter adapter = new SongListAdapter(this, list); playlistView.setAdapter(adapter); playlistView.setDragSortListener(new DragSortListView.DragSortListener() { @Override public void drop(int from, int to) { if (from != to) { DownloadFile item = adapter.getItem(from); adapter.remove(item); adapter.notifyDataSetChanged(); adapter.insert(item, to); adapter.notifyDataSetChanged(); } } @Override public void drag(int from, int to) { } @Override public void remove(int which) { DownloadFile item = adapter.getItem(which); MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (item == null || mediaPlayerController == null) { return; } DownloadFile currentPlaying = mediaPlayerController.getCurrentPlaying(); if (currentPlaying == item) { getMediaPlayerController().next(); } adapter.remove(item); adapter.notifyDataSetChanged(); String songRemoved = String.format(getResources().getString(R.string.download_song_removed), item.getSong().getTitle()); Util.toast(DownloadActivity.this, songRemoved); onDownloadListChanged(); onCurrentChanged(); } }); emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE); currentRevision = mediaPlayerController.getPlayListUpdateRevision(); switch (mediaPlayerController.getRepeatMode()) { case OFF: repeatButton.setImageDrawable(Util.getDrawableFromAttribute(this, R.attr.media_repeat_off)); break; case ALL: repeatButton.setImageDrawable(Util.getDrawableFromAttribute(this, R.attr.media_repeat_all)); break; case SINGLE: repeatButton.setImageDrawable(Util.getDrawableFromAttribute(this, R.attr.media_repeat_single)); break; default: break; } } private void onCurrentChanged() { MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController == null) { return; } currentPlaying = mediaPlayerController.getCurrentPlaying(); scrollToCurrent(); long totalDuration = mediaPlayerController.getPlayListDuration(); long totalSongs = mediaPlayerController.getPlaylistSize(); int currentSongIndex = mediaPlayerController.getCurrentPlayingNumberOnPlaylist() + 1; String duration = Util.formatTotalDuration(totalDuration); String trackFormat = String.format(Locale.getDefault(), "%d / %d", currentSongIndex, totalSongs); if (currentPlaying != null) { currentSong = currentPlaying.getSong(); songTitleTextView.setText(currentSong.getTitle()); albumTextView.setText(currentSong.getAlbum()); artistTextView.setText(currentSong.getArtist()); downloadTrackTextView.setText(trackFormat); downloadTotalDurationTextView.setText(duration); getImageLoader().loadImage(albumArtImageView, currentSong, true, 0, false, true); displaySongRating(); } else { currentSong = null; songTitleTextView.setText(null); albumTextView.setText(null); artistTextView.setText(null); downloadTrackTextView.setText(null); downloadTotalDurationTextView.setText(null); getImageLoader().loadImage(albumArtImageView, null, true, 0, false, true); } } private void onSliderProgressChanged() { MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController == null || onProgressChangedTask != null) { return; } onProgressChangedTask = new SilentBackgroundTask(this) { MediaPlayerController mediaPlayerController; boolean isJukeboxEnabled; int millisPlayed; Integer duration; PlayerState playerState; @Override protected Void doInBackground() throws Throwable { this.mediaPlayerController = getMediaPlayerController(); isJukeboxEnabled = this.mediaPlayerController.isJukeboxEnabled(); millisPlayed = Math.max(0, this.mediaPlayerController.getPlayerPosition()); duration = this.mediaPlayerController.getPlayerDuration(); playerState = getMediaPlayerController().getPlayerState(); return null; } @Override protected void done(final Void result) { if (currentPlaying != null) { final int millisTotal = duration == null ? 0 : duration; positionTextView.setText(Util.formatTotalDuration(millisPlayed, true)); durationTextView.setText(Util.formatTotalDuration(millisTotal, true)); progressBar.setMax(millisTotal == 0 ? 100 : millisTotal); // Work-around for apparent bug. progressBar.setProgress(millisPlayed); progressBar.setEnabled(currentPlaying.isWorkDone() || isJukeboxEnabled); } else { positionTextView.setText(R.string.util_zero_time); durationTextView.setText(R.string.util_no_time); progressBar.setProgress(0); progressBar.setMax(0); progressBar.setEnabled(false); } switch (playerState) { case DOWNLOADING: final long bytes = currentPlaying != null ? currentPlaying.getPartialFile().length() : 0; String downloadStatus = getResources().getString(R.string.download_playerstate_downloading, Util.formatLocalizedBytes(bytes, DownloadActivity.this)); setActionBarSubtitle(downloadStatus); break; case PREPARING: setActionBarSubtitle(R.string.download_playerstate_buffering); break; case STARTED: final MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController != null && mediaPlayerController.isShufflePlayEnabled()) { setActionBarSubtitle(R.string.download_playerstate_playing_shuffle); } else { setActionBarSubtitle(null); } break; default: setActionBarSubtitle(null); break; case IDLE: break; case PREPARED: break; case STOPPED: break; case PAUSED: break; case COMPLETED: break; } switch (playerState) { case STARTED: pauseButton.setVisibility(View.VISIBLE); stopButton.setVisibility(View.GONE); startButton.setVisibility(View.GONE); break; case DOWNLOADING: case PREPARING: pauseButton.setVisibility(View.GONE); stopButton.setVisibility(View.VISIBLE); startButton.setVisibility(View.GONE); break; default: pauseButton.setVisibility(View.GONE); stopButton.setVisibility(View.GONE); startButton.setVisibility(View.VISIBLE); break; } // TODO: It would be a lot nicer if MediaPlayerController would send an event when this is necessary instead of updating every time displaySongRating(); onProgressChangedTask = null; } }; onProgressChangedTask.execute(); } private void changeProgress(final int ms) { final MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController == null) { return; } new SilentBackgroundTask(this) { int msPlayed; Integer duration; int seekTo; @Override protected Void doInBackground() throws Throwable { msPlayed = Math.max(0, mediaPlayerController.getPlayerPosition()); duration = mediaPlayerController.getPlayerDuration(); final int msTotal = duration; seekTo = msPlayed + ms > msTotal ? msTotal : msPlayed + ms; mediaPlayerController.seekTo(seekTo); return null; } @Override protected void done(final Void result) { progressBar.setProgress(seekTo); } }.execute(); } @Override public boolean onTouchEvent(final MotionEvent me) { return gestureScanner.onTouchEvent(me); } @Override public boolean onDown(final MotionEvent me) { return false; } @Override public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) { final MediaPlayerController mediaPlayerController = getMediaPlayerController(); if (mediaPlayerController == null || e1 == null || e2 == null) { return false; } float e1X = e1.getX(); float e2X = e2.getX(); float e1Y = e1.getY(); float e2Y = e2.getY(); float absX = Math.abs(velocityX); float absY = Math.abs(velocityY); // Right to Left swipe if (e1X - e2X > swipeDistance && absX > swipeVelocity) { warnIfNetworkOrStorageUnavailable(); if (mediaPlayerController.getCurrentPlayingNumberOnPlaylist() < mediaPlayerController.getPlaylistSize() - 1) { mediaPlayerController.next(); onCurrentChanged(); onSliderProgressChanged(); } return true; } // Left to Right swipe if (e2X - e1X > swipeDistance && absX > swipeVelocity) { warnIfNetworkOrStorageUnavailable(); mediaPlayerController.previous(); onCurrentChanged(); onSliderProgressChanged(); return true; } // Top to Bottom swipe if (e2Y - e1Y > swipeDistance && absY > swipeVelocity) { warnIfNetworkOrStorageUnavailable(); mediaPlayerController.seekTo(mediaPlayerController.getPlayerPosition() + 30000); onSliderProgressChanged(); return true; } // Bottom to Top swipe if (e1Y - e2Y > swipeDistance && absY > swipeVelocity) { warnIfNetworkOrStorageUnavailable(); mediaPlayerController.seekTo(mediaPlayerController.getPlayerPosition() - 8000); onSliderProgressChanged(); return true; } return false; } @Override public void onLongPress(final MotionEvent e) { } @Override public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) { return false; } @Override public void onShowPress(final MotionEvent e) { } @Override public boolean onSingleTapUp(final MotionEvent e) { return false; } public static SeekBar getProgressBar() { return progressBar; } private void displaySongRating() { int rating = currentSong == null || currentSong.getUserRating() == null ? 0 : currentSong.getUserRating(); fiveStar1ImageView.setImageDrawable(rating > 0 ? fullStar : hollowStar); fiveStar2ImageView.setImageDrawable(rating > 1 ? fullStar : hollowStar); fiveStar3ImageView.setImageDrawable(rating > 2 ? fullStar : hollowStar); fiveStar4ImageView.setImageDrawable(rating > 3 ? fullStar : hollowStar); fiveStar5ImageView.setImageDrawable(rating > 4 ? fullStar : hollowStar); } private void setSongRating(final int rating) { if (currentSong == null) return; displaySongRating(); getMediaPlayerController().setSongRating(rating); } }