/* * Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner, * Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain, * Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter, * Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann, * Samuel Zweifel * * This file is part of Jukefox. * * Jukefox 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 any later version. Jukefox 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 * Jukefox. If not, see <http://www.gnu.org/licenses/>. */ package ch.ethz.dcg.pancho3.tablet.view.queue; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.app.Fragment; import android.content.res.Resources; import android.database.DataSetObserver; import android.os.Bundle; import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import ch.ethz.dcg.jukefox.commons.utils.Log; import ch.ethz.dcg.jukefox.commons.utils.SongTimeFormatter; import ch.ethz.dcg.jukefox.controller.player.IOnPlayerStateChangeListener; import ch.ethz.dcg.jukefox.controller.player.IOnPlaylistStateChangeListener; import ch.ethz.dcg.jukefox.controller.player.IReadOnlyPlayerController; import ch.ethz.dcg.jukefox.model.collection.BaseAlbum; import ch.ethz.dcg.jukefox.model.collection.BaseArtist; import ch.ethz.dcg.jukefox.model.collection.BaseSong; import ch.ethz.dcg.jukefox.model.collection.IReadOnlyPlaylist; import ch.ethz.dcg.jukefox.model.collection.PlaylistSong; import ch.ethz.dcg.jukefox.model.commons.EmptyPlaylistException; import ch.ethz.dcg.jukefox.model.player.PlayerState; import ch.ethz.dcg.jukefox.playmode.IPlayMode; import ch.ethz.dcg.pancho3.R; import ch.ethz.dcg.pancho3.controller.eventhandlers.TabletActivityEventListener; import ch.ethz.dcg.pancho3.model.JukefoxApplication; import ch.ethz.dcg.pancho3.tablet.TabletFactory; import ch.ethz.dcg.pancho3.tablet.TabletFactory.TabletFactoryGetter; import ch.ethz.dcg.pancho3.tablet.presenter.queue.QueuePresenter; import ch.ethz.dcg.pancho3.tablet.presenter.queue.QueuePresenter.IQueue; import ch.ethz.dcg.pancho3.tablet.view.TabletActivity; import ch.ethz.dcg.pancho3.tablet.widget.MagicListAdapter; import ch.ethz.dcg.pancho3.tablet.widget.MagicView; import ch.ethz.dcg.pancho3.view.commons.SongProgressBar; public class QueueFragment extends Fragment implements IQueue { public static final String TAG = TabletActivity.class.getSimpleName(); private View view; private QueuePresenter presenter; private TabletActivityEventListener eventListener; private final Handler handler = new Handler(); private ImageView playPauseButton; private Timer progressUpdateTimer; private Timer undoDisplayTimer; private SongProgressBar songProgressBar; private TextView progressText; private View undoClear; private SongTimeFormatter songTimeFormatter; private IOnPlaylistStateChangeListener playlistStateEventListener; private IOnPlayerStateChangeListener playerStateEventListener; private MagicListAdapter<PlaylistSong<BaseArtist, BaseAlbum>> adapter; private PlaylistAdapter playlistAdapter; private IReadOnlyPlayerController playManager; private View[] borders; private int borderColorUnhighlighted; private int borderColorHighlighted; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreate(savedInstanceState); playManager = JukefoxApplication.getPlayerController(); view = inflater.inflate(R.layout.tablet_queue, null); borders = new View[] { view.findViewById(R.id.border0), view.findViewById(R.id.border1), view.findViewById(R.id.border2), view.findViewById(R.id.border3) }; // Progress Bar is initialized as soon as possible to avoid flickering // of the progress. songProgressBar = (SongProgressBar) view.findViewById(R.id.songProgressBar); songProgressBar.setMax(100); songProgressBar.setProgress(0); return view; } @Override public void onAttach(Activity activity) { super.onAttach(activity); Resources resources = activity.getResources(); borderColorUnhighlighted = resources.getColor(R.color.queue_dark); borderColorHighlighted = resources.getColor(R.color.highlight); } @Override public void highlight() { for (View border : borders) { border.setBackgroundColor(borderColorHighlighted); } } @Override public void unhighlight() { for (View border : borders) { border.setBackgroundColor(borderColorUnhighlighted); } } @Override public void onResume() { super.onResume(); if (JukefoxApplication.getPlayerController().isReady()) { startUpdateTimer(); } } @Override public void onPause() { cancelUpdateTimer(); super.onPause(); } public void initialize() { TabletFactory tabletFactory = ((TabletFactoryGetter) getActivity()).getTabletFactory(); eventListener = tabletFactory.getEventListener(); playlistAdapter = tabletFactory.createPlaylistAdapter(); MagicView magicView = (MagicView) view.findViewById(R.id.magicview); adapter = tabletFactory.createMagicListAdapter(playlistAdapter, new MagicListAdapter.NewItemListener() { @Override public void onRequestNewItem() { // TODO: we are not using this ATM. } }); magicView.setAdapter(adapter); magicView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (position == 0) { eventListener.onSongSelected(adapter.getHeaderItem()); } else { eventListener.onSongSelected(adapter.getItem(position - 1)); } } }); songTimeFormatter = new SongTimeFormatter(); songProgressBar.setReactOnMoveEvents(true); songProgressBar.setOnProgressChangeListener(new SongProgressBar.OnProgressChangeListener() { public void onProgressChanged(View v, int progress) { eventListener.setProgress(progress); } }); progressText = (TextView) view.findViewById(R.id.songProgressText); registerButtonEventListeners(view); registerPlayerEventListeners(); updateView(); presenter = tabletFactory.getQueuePresenter(); undoClear = view.findViewById(R.id.undo_container); view.findViewById(R.id.undo).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { playlistAdapter.undoClear(); undoClear.setVisibility(View.GONE); } }); presenter.viewFinishedInit(this); } private void updateView() { updatePlayerState(playManager.getPlayerState()); try { updateSongInfo(playManager.getCurrentSong()); } catch (EmptyPlaylistException e) { updateSongInfo(null); } loadPlaylist(playManager.getCurrentPlaylist()); } private void updateSongInfo(final BaseSong<? extends BaseArtist, ? extends BaseAlbum> currentSong) { handler.post(new Runnable() { @Override public void run() { if (currentSong != null) { updateProgress(); } } }); } private void registerPlayerEventListeners() { playlistStateEventListener = new IOnPlaylistStateChangeListener() { @Override public void onCurrentSongChanged(PlaylistSong<BaseArtist, BaseAlbum> newSong) { updateSongInfo(newSong); playlistAdapter.currentSongChanged(); } @Override public void onPlayModeChanged(IPlayMode newPlayMode) { } @Override public void onPlaylistChanged(IReadOnlyPlaylist newPlaylist) { Log.v(TAG, "Playlist changed. New size: " + newPlaylist.getPlaylistSize()); loadPlaylist(newPlaylist); undoClear.setVisibility(View.GONE); } }; playManager.addOnPlaylistStateChangeListener(playlistStateEventListener); playerStateEventListener = new IOnPlayerStateChangeListener() { @Override public void onSongStarted(PlaylistSong<BaseArtist, BaseAlbum> song) { } @Override public void onSongSkipped(PlaylistSong<BaseArtist, BaseAlbum> song, int position) { } @Override public void onSongCompleted(PlaylistSong<BaseArtist, BaseAlbum> song) { } @Override public void onPlayerStateChanged(PlayerState playerState) { updatePlayerState(playerState); } }; playManager.addOnPlayerStateChangeListener(playerStateEventListener); } @Override public void onDestroy() { playManager.removeOnPlayerStateChangeListener(playerStateEventListener); playManager.removeOnPlaylistStateChangeListener(playlistStateEventListener); super.onDestroy(); } private void updatePlayerState(PlayerState newPlayerState) { if (newPlayerState == PlayerState.PLAY) { startUpdateTimer(); } else { cancelUpdateTimer(); updateProgress(); } setPlayPauseButton(newPlayerState); } private void setPlayPauseButton(final PlayerState newPlayerState) { handler.post(new Runnable() { @Override public void run() { if (newPlayerState == PlayerState.PLAY) { playPauseButton.setImageResource(R.drawable.d164_pause); } else { playPauseButton.setImageResource(R.drawable.d165_play); } } }); } private void cancelUpdateTimer() { if (progressUpdateTimer != null) { progressUpdateTimer.cancel(); } } private void startUpdateTimer() { cancelUpdateTimer(); progressUpdateTimer = new Timer(); progressUpdateTimer.schedule(new TimerTask() { @Override public void run() { updateProgress(); } }, 0, 1000); Log.w(TAG, "started update timer"); } public void updateProgress() { handler.post(new Runnable() { @Override public void run() { int songPos = playManager.getPlaybackPosition() < 0 ? 0 : playManager.getPlaybackPosition(); int songDuration = playManager.getDuration() < 0 ? 0 : playManager.getDuration(); songProgressBar.setMax(songDuration); songProgressBar.setProgress(songPos); // TODO: can we get rid of this null check and ensure they are // not null? if (progressText != null && songTimeFormatter != null) { progressText.setText(songTimeFormatter.format(songPos) + "/" + songTimeFormatter.format(songDuration)); } } }); } private void loadPlaylist(final IReadOnlyPlaylist playlist) { handler.post(new Runnable() { @Override public void run() { playlistAdapter.playlistChanged(playlist); } }); } private void registerButtonEventListeners(View view) { playPauseButton = (ImageView) view.findViewById(R.id.playPauseButton); playPauseButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { eventListener.onPlayPauseButtonClicked(); } }); playPauseButton.setOnLongClickListener(bottomBarOnLongClickListener); final View shuffleButton = view.findViewById(R.id.shuffleButton); shuffleButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { playlistAdapter.shuffle(); } }); shuffleButton.setOnLongClickListener(bottomBarOnLongClickListener); final View clearButton = view.findViewById(R.id.clearButton); clearButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { clear(); } }); clearButton.setOnLongClickListener(bottomBarOnLongClickListener); View skipButton = view.findViewById(R.id.skipButton); skipButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { playlistAdapter.removeItem(0); } }); skipButton.setOnLongClickListener(bottomBarOnLongClickListener); playlistAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { int count = playlistAdapter.getCount(); boolean shuffleButtonEnabled = count > 2 ? true : false; shuffleButton.setEnabled(shuffleButtonEnabled); shuffleButton.setAlpha(shuffleButtonEnabled ? 1.0f : 0.3f); boolean clearButtonEnabled = count > 1 ? true : false; clearButton.setEnabled(clearButtonEnabled); clearButton.setAlpha(clearButtonEnabled ? 1.0f : 0.3f); } }); } public Handler getHandler() { return handler; } private void clear() { if (playlistAdapter.clearPlaylistExceptPlayingSong()) { if (undoDisplayTimer != null) { undoDisplayTimer.cancel(); } undoClear.setAlpha(0.0f); undoClear.setVisibility(View.VISIBLE); undoClear.animate().alpha(0.8f); undoDisplayTimer = new Timer(); undoDisplayTimer.schedule(new TimerTask() { @Override public void run() { handler.post(new Runnable() { @Override public void run() { undoClear.setVisibility(View.GONE); } }); } }, 10000); } } private OnLongClickListener bottomBarOnLongClickListener = new OnLongClickListener() { @Override public boolean onLongClick(View v) { Toast.makeText(getActivity(), v.getContentDescription(), Toast.LENGTH_SHORT).show(); return true; } }; }