/*
* Copyright (c) 2013, Sorokin Alexander (uas.sorokin@gmail.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The names of the authors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.uas.media.aimp.mobile;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import com.uas.media.aimp.AimpPlayerInstance;
import com.uas.media.aimp.R;
import com.uas.media.aimp.api.models.Playlist;
import com.uas.media.aimp.api.models.Song;
import com.uas.media.aimp.player.AimpPlayer;
import com.uas.media.aimp.player.StateObserver;
import com.uas.media.aimp.player.StateObserverViaHandler;
import com.uas.media.aimp.utils.InitErrorHandler;
import com.uas.media.aimp.utils.Logger;
import java.util.List;
/**
* User: uas.sorokin@gmail.com
*/
public class PlaylistFragment extends Fragment {
public static final String PLAYLIST_ID = "playlistId";
public static final String SCROLL_POSITION = "scrollPosition";
static class ViewHolder {
public ViewGroup wrapper;
public TextView title;
public TextView duration;
public ListView list;
public EditText searcher;
public ImageView searchCancel;
}
private ViewHolder mViewHolder;
private AimpPlayer mAimpPlayer;
private SongsAdapter mSongsAdapter;
private int mPlaylistId;
private boolean mIsCurrentPlaylist;
private Song mCurrentSong;
private int mScrollPosition;
private TextWatcher mFilterTextWatcher;
private View.OnClickListener mSearcherCancelListener;
private AbsListView.OnScrollListener mScrollListener;
private StateObserver mAimpStateObserver;
public static PlaylistFragment newInstance(int playlistId, int scrollPosition) {
PlaylistFragment pageFragment = new PlaylistFragment();
Bundle bundle = new Bundle();
bundle.putInt(PLAYLIST_ID, playlistId);
bundle.putInt(SCROLL_POSITION, scrollPosition);
pageFragment.setArguments(bundle);
return pageFragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup wrapper = (ViewGroup) inflater.inflate(R.layout.mobile_pls, container, false);
mPlaylistId = getArguments().getInt(PLAYLIST_ID);
mScrollPosition = getArguments().getInt(SCROLL_POSITION);
mAimpPlayer = AimpPlayerInstance.get();
mCurrentSong = mAimpPlayer.getCurrentSong();
Playlist currentPlaylist = mAimpPlayer.getCurrentPlaylist();
mIsCurrentPlaylist = (currentPlaylist != null && mPlaylistId == currentPlaylist.getId());
try {
initViewHolder(wrapper);
initUi();
initUiEvents();
} catch (Exception ex) {
InitErrorHandler.handle(ex);
}
mAimpStateObserver = new StateObserverImpl();
mAimpPlayer.registerStateObserver(mAimpStateObserver);
return wrapper;
}
@Override
public void onDestroyView() {
clearUiEvents();
mAimpPlayer.unregisterStateObserver(mAimpStateObserver);
mAimpPlayer = null;
mSongsAdapter = null;
super.onDestroyView();
}
// ============================================================
// =========== INIT UI
// ============================================================
protected void initViewHolder(ViewGroup w) {
mViewHolder = new ViewHolder();
mViewHolder.wrapper = w;
mViewHolder.title = (TextView) w.findViewById(R.id.title);
mViewHolder.duration = (TextView) w.findViewById(R.id.duration);
mViewHolder.list = (ListView) w.findViewById(R.id.list);
mViewHolder.searcher = (EditText) w.findViewById(R.id.search);
mViewHolder.searchCancel = (ImageView) w.findViewById(R.id.cancel);
}
protected void initUi() {
final Playlist pl = mAimpPlayer.getPlaylistById(mPlaylistId);
mViewHolder.title.setText(formatPlaylistHeader(pl));
mViewHolder.duration.setText(formatPlaylistDuration(pl));
mViewHolder.list.setTextFilterEnabled(true);
updateAdapter();
scrollTo(mScrollPosition);
}
protected void initUiEvents() {
mFilterTextWatcher = new SearchTextWatcherImpl();
mSearcherCancelListener = new SearchCancelClickListener();
mScrollListener = new SrollListener();
mViewHolder.searcher.addTextChangedListener(mFilterTextWatcher);
mViewHolder.searchCancel.setOnClickListener(mSearcherCancelListener);
mViewHolder.list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Playlist current = mAimpPlayer.getPlaylistById(mPlaylistId);
Song s = mSongsAdapter.getItem(position);
onSongClick(current, s);
}
});
mViewHolder.list.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Playlist current = mAimpPlayer.getPlaylistById(mPlaylistId);
Song s = mSongsAdapter.getItem(position);
onSongLongClick(current, s);
return true;
}
});
mViewHolder.list.setOnScrollListener(mScrollListener);
}
protected void clearUiEvents() {
mViewHolder.searcher.removeTextChangedListener(mFilterTextWatcher);
}
// ============================================================
// =========== UI UTILS
// ============================================================
public int getScrollPosition() {
return mScrollPosition;
}
public int getPlaylistId() {
return mPlaylistId;
}
public void scrollTo(final int position) {
mViewHolder.list.requestFocusFromTouch();
mViewHolder.list.setSelection(position);
//mViewHolder.list.setSelectionFromTop(position, 0);
mViewHolder.list.requestFocus();
}
protected void updateAdapter() {
Playlist pl = mAimpPlayer.getPlaylistById(mPlaylistId);
mSongsAdapter = new SongsAdapter(getActivity(), pl.getSongs());
mViewHolder.list.setAdapter(mSongsAdapter);
}
protected String formatPlaylistHeader(Playlist playlist) {
return getActivity().getString(R.string.playlist_header_format)
.replace("%PLAYLIST_POSITION%", String.valueOf(mAimpPlayer.getPlaylistPosition(playlist.getId()) + 1))
.replace("%PLAYLIST_COUNT%", String.valueOf(mAimpPlayer.getPlaylists().size()))
.replace("%PLAYLIST_NAME%", playlist.getName())
;
}
protected String formatPlaylistDuration(Playlist playlist) {
int duration = playlist.getDuration()/1000;
int hours = duration / 3600;
int seconds = duration%60;
int minutes = duration / 60 - hours * 60;
return getString(R.string.playlist_length_format, hours, minutes, seconds);
}
protected void onSongClick(Playlist playlist, Song s) {
mAimpPlayer.changeSong(playlist.getId(), playlist.findSongPosition(s));
}
protected void onSongLongClick(final Playlist playlist, final Song s) {
AlertDialog d = new AlertDialog.Builder(getActivity())
.setTitle(R.string.song_delete_title)
.setMessage(getString(R.string.song_delete_message, s.getName()))
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.song_delete_submit, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Playlist pl = mAimpPlayer.getPlaylistById(mPlaylistId);
int songPosition = pl.findSongPosition(s);
mAimpPlayer.removeSong(mPlaylistId, songPosition);
}
})
.setCancelable(true)
.create()
;
d.show();
}
class SearchTextWatcherImpl implements TextWatcher {
@Override
public void afterTextChanged(Editable s) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
mViewHolder.searchCancel.setVisibility(
s.length() == 0 ? View.GONE : View.VISIBLE
);
mSongsAdapter.getFilter().filter(s);
}
}
class SearchCancelClickListener implements View.OnClickListener {
@Override
public void onClick(View view) {
mViewHolder.searcher.setText("");
}
}
class StateObserverImpl extends StateObserverViaHandler {
@Override
public void onSongChanged(Playlist playlist, Song song, int position, double percentage) {
if (playlist != null) { // YES, it should not be happen, but it happens :(
mIsCurrentPlaylist = playlist.getId() == mPlaylistId;
mCurrentSong = song;
mSongsAdapter.notifyDataSetChanged();
}
}
@Override
public void onPlaylistUpdated(Playlist playlist) {
int scrollPosition = getScrollPosition();
updateAdapter();
scrollTo(scrollPosition);
}
}
class SrollListener implements AbsListView.OnScrollListener {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mScrollPosition = firstVisibleItem;
}
}
// ============================================================
// =========== SONG's ADAPTER
// ============================================================
static class SongViewHolder {
public TextView position;
public TextView title;
public TextView duration;
}
class SongsAdapter extends ArrayAdapter<Song> {
private String mSongLengthFormat;
public SongsAdapter(Context context, List<Song> songs) {
super(context, -1, songs);
mSongLengthFormat = getString(R.string.song_length_format);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
v = getActivity().getLayoutInflater().inflate(R.layout.mobile_pls_song, parent, false);
SongViewHolder holder = new SongViewHolder();
holder.position = (TextView) v.findViewById(R.id.position);
holder.title = (TextView) v.findViewById(R.id.title);
holder.duration = (TextView) v.findViewById(R.id.duration);
v.setTag(holder);
}
final Song s = getItem(position);
final SongViewHolder holder = (SongViewHolder) v.getTag();
holder.position.setText(String.valueOf(position + 1));
holder.title.setText(s.getName());
holder.duration.setText(String.format(
mSongLengthFormat,
s.getMinutesDuration(),
s.getSecondsDuration())
);
if (mIsCurrentPlaylist && mCurrentSong != null && mCurrentSong.equals(s)) {
v.setBackgroundResource(R.drawable.bg_pls_song_default);
} else {
v.setBackgroundResource(R.drawable.bg_pls_song_current);
}
return v;
}
}
}