/* This is Main Activity*/
package org.music.player;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.PaintDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.support.v4.view.ViewPager;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.inputmethod.InputMethodManager;
import android.widget.HorizontalScrollView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.viewpagerindicator.TabPageIndicator;
import java.io.File;
import org.music.player.R;
import junit.framework.Assert;
/**
* The library activity where songs to play can be selected from the library.
*/
public class LibraryActivity
extends PlaybackActivity
implements TextWatcher
, DialogInterface.OnClickListener
, DialogInterface.OnDismissListener
{
/**
* Action for row click: play the row.
*/
public static final int ACTION_PLAY = 0;
/**
* Action for row click: enqueue the row.
*/
public static final int ACTION_ENQUEUE = 1;
/**
* Action for row click: perform the last used action.
*/
public static final int ACTION_LAST_USED = 2;
/**
* Action for row click: play all the songs in the adapter, starting with
* the current row.
*/
public static final int ACTION_PLAY_ALL = 3;
/**
* Action for row click: enqueue all the songs in the adapter, starting with
* the current row.
*/
public static final int ACTION_ENQUEUE_ALL = 4;
/**
* Action for row click: do nothing.
*/
public static final int ACTION_DO_NOTHING = 5;
/**
* Action for row click: expand the row.
*/
public static final int ACTION_EXPAND = 6;
/**
* Action for row click: play if paused or enqueue if playing.
*/
public static final int ACTION_PLAY_OR_ENQUEUE = 7;
/**
* The SongTimeline add song modes corresponding to each relevant action.
*/
private static final int[] modeForAction =
{ SongTimeline.MODE_PLAY, SongTimeline.MODE_ENQUEUE, -1,
SongTimeline.MODE_PLAY_ID_FIRST, SongTimeline.MODE_ENQUEUE_ID_FIRST };
private static final String SEARCH_BOX_VISIBLE = "search_box_visible";
public ViewPager mViewPager;
private TabPageIndicator mTabs;
private View mSearchBox;
private boolean mSearchBoxVisible;
private TextView mTextFilter;
private View mClearButton;
private View mActionControls;
private View mControls;
private TextView mTitle;
private TextView mArtist;
private ImageView mCover;
private View mEmptyQueue;
private HorizontalScrollView mLimiterScroller;
private ViewGroup mLimiterViews;
/**
* The action to execute when a row is tapped.
*/
private int mDefaultAction;
/**
* The last used action from the menu. Used with ACTION_LAST_USED.
*/
private int mLastAction = ACTION_PLAY;
/**
* The id of the media that was last pressed in the current adapter. Used to
* open the playback activity when an item is pressed twice.
*/
private long mLastActedId;
/**
* The pager adapter that manages each media ListView.
*/
public LibraryPagerAdapter mPagerAdapter;
/**
* The adapter for the currently visible list.
*/
private LibraryAdapter mCurrentAdapter;
/**
* If true, return target GINGERBREAD from getApplicationInfo().
*/
boolean mFakeTarget;
/**
* ApplicationInfo with targetSdkVersion set to Gingerbread.
*/
private ApplicationInfo mFakeInfo;
@Override
public void onCreate(Bundle state)
{
super.onCreate(state);
if (state == null) {
checkForLaunch(getIntent());
}
setContentView(R.layout.library_content);
mSearchBox = findViewById(R.id.search_box);
mTextFilter = (TextView)findViewById(R.id.filter_text);
mTextFilter.addTextChangedListener(this);
mClearButton = findViewById(R.id.clear_button);
mClearButton.setOnClickListener(this);
mLimiterScroller = (HorizontalScrollView)findViewById(R.id.limiter_scroller);
mLimiterViews = (ViewGroup)findViewById(R.id.limiter_layout);
LibraryPagerAdapter pagerAdapter = new LibraryPagerAdapter(this, mLooper);
mPagerAdapter = pagerAdapter;
ViewPager pager = (ViewPager)findViewById(R.id.pager);
pager.setAdapter(pagerAdapter);
mViewPager = pager;
SharedPreferences settings = PlaybackService.getSettings(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
pager.setOnPageChangeListener(pagerAdapter);
View controls = getLayoutInflater().inflate(R.layout.actionbar_controls, null);
mTitle = (TextView)controls.findViewById(R.id.title);
mArtist = (TextView)controls.findViewById(R.id.artist);
mCover = (ImageView)controls.findViewById(R.id.cover);
controls.setOnClickListener(this);
mActionControls = controls;
} else {
TabPageIndicator tabs = new TabPageIndicator(this);
tabs.setViewPager(pager);
tabs.setOnPageChangeListener(pagerAdapter);
mTabs = tabs;
LinearLayout content = (LinearLayout)findViewById(R.id.content);
content.addView(tabs, 0, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
if (settings.getBoolean(PrefKeys.CONTROLS_IN_SELECTOR, false)) {
getLayoutInflater().inflate(R.layout.library_controls, content, true);
mControls = findViewById(R.id.controls);
mTitle = (TextView)mControls.findViewById(R.id.title);
mArtist = (TextView)mControls.findViewById(R.id.artist);
mCover = (ImageView)mControls.findViewById(R.id.cover);
View previous = mControls.findViewById(R.id.previous);
mPlayPauseButton = (ImageButton)mControls.findViewById(R.id.play_pause);
View next = mControls.findViewById(R.id.next);
mCover.setOnClickListener(this);
previous.setOnClickListener(this);
mPlayPauseButton.setOnClickListener(this);
next.setOnClickListener(this);
mShuffleButton = (ImageButton)findViewById(R.id.shuffle);
mShuffleButton.setOnClickListener(this);
registerForContextMenu(mShuffleButton);
mEndButton = (ImageButton)findViewById(R.id.end_action);
mEndButton.setOnClickListener(this);
registerForContextMenu(mEndButton);
mEmptyQueue = findViewById(R.id.empty_queue);
mEmptyQueue.setOnClickListener(this);
}
}
loadTabOrder();
int page = settings.getInt(PrefKeys.LIBRARY_PAGE, 0);
if (page != 0) {
pager.setCurrentItem(page);
}
loadAlbumIntent(getIntent());
}
@Override
public void onRestart()
{
super.onRestart();
loadTabOrder();
}
@Override
public void onStart()
{
super.onStart();
SharedPreferences settings = PlaybackService.getSettings(this);
if (settings.getBoolean(PrefKeys.CONTROLS_IN_SELECTOR, false) != (mControls != null)) {
finish();
startActivity(new Intent(this, LibraryActivity.class));
}
mDefaultAction = Integer.parseInt(settings.getString(PrefKeys.DEFAULT_ACTION_INT, "7"));
mLastActedId = LibraryAdapter.INVALID_ID;
updateHeaders();
}
/**
* Load the tab order and update the tab bars if needed.
*/
private void loadTabOrder()
{
if (mPagerAdapter.loadTabOrder()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
CompatHoneycomb.addActionBarTabs(this);
} else {
mTabs.notifyDataSetChanged();
}
}
}
/**
* If this intent looks like a launch from icon/widget/etc, perform
* launch actions.
*/
private void checkForLaunch(Intent intent)
{
SharedPreferences settings = PlaybackService.getSettings(this);
if (settings.getBoolean(PrefKeys.PLAYBACK_ON_STARTUP, false) && Intent.ACTION_MAIN.equals(intent.getAction())) {
startActivity(new Intent(this, FullPlaybackActivity.class));
}
}
/**
* If the given intent has album data, set a limiter built from that
* data.
*/
private void loadAlbumIntent(Intent intent)
{
long albumId = intent.getLongExtra("albumId", -1);
if (albumId != -1) {
String[] fields = { intent.getStringExtra("artist"), intent.getStringExtra("album") };
String data = String.format("album_id=%d", albumId);
Limiter limiter = new Limiter(MediaUtils.TYPE_ALBUM, fields, data);
int tab = mPagerAdapter.setLimiter(limiter);
if (tab == -1 || tab == mViewPager.getCurrentItem())
updateLimiterViews();
else
mViewPager.setCurrentItem(tab);
}
}
@Override
public void onNewIntent(Intent intent)
{
if (intent == null)
return;
checkForLaunch(intent);
loadAlbumIntent(intent);
}
@Override
public void onRestoreInstanceState(Bundle in)
{
if (in.getBoolean(SEARCH_BOX_VISIBLE))
setSearchBoxVisible(true);
super.onRestoreInstanceState(in);
}
@Override
protected void onSaveInstanceState(Bundle out)
{
super.onSaveInstanceState(out);
out.putBoolean(SEARCH_BOX_VISIBLE, mSearchBoxVisible);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
{
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (mSearchBoxVisible) {
mTextFilter.setText("");
setSearchBoxVisible(false);
} else {
Limiter limiter = mPagerAdapter.getCurrentLimiter();
if (limiter != null && limiter.type != MediaUtils.TYPE_FILE) {
int pos = -1;
switch (limiter.type) {
case MediaUtils.TYPE_ALBUM:
setLimiter(MediaUtils.TYPE_ARTIST, limiter.data.toString());
pos = mPagerAdapter.mAlbumsPosition;
break;
case MediaUtils.TYPE_ARTIST:
mPagerAdapter.clearLimiter(MediaUtils.TYPE_ARTIST);
pos = mPagerAdapter.mArtistsPosition;
break;
case MediaUtils.TYPE_GENRE:
mPagerAdapter.clearLimiter(MediaUtils.TYPE_GENRE);
pos = mPagerAdapter.mGenresPosition;
break;
}
if (pos == -1) {
updateLimiterViews();
} else {
mViewPager.setCurrentItem(pos);
}
} else {
finish();
}
}
break;
case KeyEvent.KEYCODE_SEARCH:
setSearchBoxVisible(!mSearchBoxVisible);
break;
default:
return false;
}
return true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL)
// On ICS, EditText reports backspace events as unhandled despite
// actually handling them. To workaround, just assume the event was
// handled if we get here.
return true;
if (super.onKeyDown(keyCode, event))
return true;
if (mTextFilter.onKeyDown(keyCode, event)) {
if (!mSearchBoxVisible)
setSearchBoxVisible(true);
else
mTextFilter.requestFocus();
return true;
}
return false;
}
/**
* Update the first row of the lists with the appropriate action (play all
* or enqueue all).
*/
private void updateHeaders()
{
int action = mDefaultAction;
if (action == ACTION_LAST_USED)
action = mLastAction;
boolean isEnqueue = action == ACTION_ENQUEUE || action == ACTION_ENQUEUE_ALL;
String text = getString(isEnqueue ? R.string.enqueue_all : R.string.play_all);
mPagerAdapter.setHeaderText(text);
}
/**
* Adds songs matching the data from the given intent to the song timelime.
*
* @param intent An intent created with
* {@link LibraryAdapter#createData(View)}.
* @param action One of LibraryActivity.ACTION_*
*/
private void pickSongs(Intent intent, int action)
{
long id = intent.getLongExtra("id", LibraryAdapter.INVALID_ID);
boolean all = false;
int mode = action;
if (action == ACTION_PLAY_ALL || action == ACTION_ENQUEUE_ALL) {
int type = mCurrentAdapter.getMediaType();
boolean notPlayAllAdapter = type > MediaUtils.TYPE_SONG || id == LibraryAdapter.HEADER_ID;
if (mode == ACTION_ENQUEUE_ALL && notPlayAllAdapter) {
mode = ACTION_ENQUEUE;
} else if (mode == ACTION_PLAY_ALL && notPlayAllAdapter) {
mode = ACTION_PLAY;
} else {
all = true;
}
}
QueryTask query = buildQueryFromIntent(intent, false, all);
query.mode = modeForAction[mode];
PlaybackService.get(this).addSongs(query);
mLastActedId = id;
if (mDefaultAction == ACTION_LAST_USED && mLastAction != action) {
mLastAction = action;
updateHeaders();
}
}
/**
* "Expand" the view represented by the given intent by setting the limiter
* from the view and switching to the appropriate tab.
*
* @param intent An intent created with
* {@link LibraryAdapter#createData(View)}.
*/
private void expand(Intent intent)
{
int type = intent.getIntExtra("type", MediaUtils.TYPE_INVALID);
long id = intent.getLongExtra("id", LibraryAdapter.INVALID_ID);
int tab = mPagerAdapter.setLimiter(mPagerAdapter.mAdapters[type].buildLimiter(id));
if (tab == -1 || tab == mViewPager.getCurrentItem())
updateLimiterViews();
else
mViewPager.setCurrentItem(tab);
}
/**
* Open the playback activity and close any activities above it in the
* stack.
*/
public void openPlaybackActivity()
{
startActivity(new Intent(this, FullPlaybackActivity.class));
}
/**
* Called by LibraryAdapters when a row has been clicked.
*
* @param rowData The data for the row that was clicked.
*/
public void onItemClicked(Intent rowData)
{
int action = mDefaultAction;
if (action == ACTION_LAST_USED)
action = mLastAction;
if (action == ACTION_EXPAND && rowData.getBooleanExtra(LibraryAdapter.DATA_EXPANDABLE, false)) {
onItemExpanded(rowData);
} else if (rowData.getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID) == mLastActedId) {
openPlaybackActivity();
} else if (action != ACTION_DO_NOTHING) {
if (action == ACTION_EXPAND) {
// default to playing when trying to expand something that can't
// be expanded
action = ACTION_PLAY;
} else if (action == ACTION_PLAY_OR_ENQUEUE) {
action = (mState & PlaybackService.FLAG_PLAYING) == 0 ? ACTION_PLAY : ACTION_ENQUEUE;
}
pickSongs(rowData, action);
}
}
/**
* Called by LibraryAdapters when a row's expand arrow has been clicked.
*
* @param rowData The data for the row that was clicked.
*/
public void onItemExpanded(Intent rowData)
{
int type = rowData.getIntExtra(LibraryAdapter.DATA_TYPE, MediaUtils.TYPE_INVALID);
if (type == MediaUtils.TYPE_PLAYLIST)
editPlaylist(rowData);
else
expand(rowData);
}
@Override
public void afterTextChanged(Editable editable)
{
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
@Override
public void onTextChanged(CharSequence text, int start, int before, int count)
{
mPagerAdapter.setFilter(text.toString());
}
/**
* Create or recreate the limiter breadcrumbs.
*/
@SuppressWarnings("deprecation")
public void updateLimiterViews()
{
mLimiterViews.removeAllViews();
Limiter limiterData = mPagerAdapter.getCurrentLimiter();
if (limiterData != null) {
String[] limiter = limiterData.names;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.leftMargin = 5;
for (int i = 0; i != limiter.length; ++i) {
PaintDrawable background = new PaintDrawable(Color.GRAY);
background.setCornerRadius(5);
TextView view = new TextView(this);
view.setSingleLine();
view.setEllipsize(TextUtils.TruncateAt.MARQUEE);
view.setText(limiter[i] + " | X");
view.setTextColor(Color.WHITE);
view.setBackgroundDrawable(background);
view.setLayoutParams(params);
view.setPadding(5, 2, 5, 2);
view.setTag(i);
view.setOnClickListener(this);
mLimiterViews.addView(view);
}
mLimiterScroller.setVisibility(View.VISIBLE);
} else {
mLimiterScroller.setVisibility(View.GONE);
}
}
@Override
public void onClick(View view)
{
if (view == mClearButton) {
if (mTextFilter.getText().length() == 0)
setSearchBoxVisible(false);
else
mTextFilter.setText("");
} else if (view == mCover || view == mActionControls) {
openPlaybackActivity();
} else if (view == mEmptyQueue) {
setState(PlaybackService.get(this).setFinishAction(SongTimeline.FINISH_RANDOM));
} else if (view.getTag() != null) {
// a limiter view was clicked
int i = (Integer)view.getTag();
Limiter limiter = mPagerAdapter.getCurrentLimiter();
int type = limiter.type;
if (i == 1 && type == MediaUtils.TYPE_ALBUM) {
setLimiter(MediaUtils.TYPE_ARTIST, limiter.data.toString());
} else if (i > 0) {
Assert.assertEquals(MediaUtils.TYPE_FILE, limiter.type);
File file = (File)limiter.data;
int diff = limiter.names.length - i;
while (--diff != -1) {
file = file.getParentFile();
}
mPagerAdapter.setLimiter(FileSystemAdapter.buildLimiter(file));
} else {
mPagerAdapter.clearLimiter(type);
}
updateLimiterViews();
} else {
super.onClick(view);
}
}
/**
* Set a new limiter of the given type built from the first
* MediaStore.Audio.Media row that matches the selection.
*
* @param limiterType The type of limiter to create. Must be either
* MediaUtils.TYPE_ARTIST or MediaUtils.TYPE_ALBUM.
* @param selection Selection to pass to the query.
*/
private void setLimiter(int limiterType, String selection)
{
ContentResolver resolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = new String[] { MediaStore.Audio.Media.ARTIST_ID, MediaStore.Audio.Media.ALBUM_ID, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM };
Cursor cursor = resolver.query(uri, projection, selection, null, null);
if (cursor != null) {
if (cursor.moveToNext()) {
String[] fields;
String data;
switch (limiterType) {
case MediaUtils.TYPE_ARTIST:
fields = new String[] { cursor.getString(2) };
data = String.format("artist_id=%d", cursor.getLong(0));
break;
case MediaUtils.TYPE_ALBUM:
fields = new String[] { cursor.getString(2), cursor.getString(3) };
data = String.format("album_id=%d", cursor.getLong(1));
break;
default:
throw new IllegalArgumentException("setLimiter() does not support limiter type " + limiterType);
}
mPagerAdapter.setLimiter(new Limiter(limiterType, fields, data));
}
cursor.close();
}
}
/**
* Builds a media query based off the data stored in the given intent.
*
* @param intent An intent created with
* {@link LibraryAdapter#createData(View)}.
* @param empty If true, use the empty projection (only query id).
* @param all If true query all songs in the adapter; otherwise query based
* on the row selected.
*/
private QueryTask buildQueryFromIntent(Intent intent, boolean empty, boolean all)
{
int type = intent.getIntExtra("type", MediaUtils.TYPE_INVALID);
String[] projection;
if (type == MediaUtils.TYPE_PLAYLIST)
projection = empty ? Song.EMPTY_PLAYLIST_PROJECTION : Song.FILLED_PLAYLIST_PROJECTION;
else
projection = empty ? Song.EMPTY_PROJECTION : Song.FILLED_PROJECTION;
long id = intent.getLongExtra("id", LibraryAdapter.INVALID_ID);
QueryTask query;
if (type == MediaUtils.TYPE_FILE) {
query = MediaUtils.buildFileQuery(intent.getStringExtra("file"), projection);
} else if (all || id == LibraryAdapter.HEADER_ID) {
query = ((MediaAdapter)mPagerAdapter.mAdapters[type]).buildSongQuery(projection);
query.data = id;
} else {
query = MediaUtils.buildQuery(type, id, projection, null);
}
return query;
}
private static final int MENU_PLAY = 0;
private static final int MENU_ENQUEUE = 1;
private static final int MENU_EXPAND = 2;
private static final int MENU_ADD_TO_PLAYLIST = 3;
private static final int MENU_NEW_PLAYLIST = 4;
private static final int MENU_DELETE = 5;
private static final int MENU_RENAME_PLAYLIST = 7;
private static final int MENU_SELECT_PLAYLIST = 8;
private static final int MENU_PLAY_ALL = 9;
private static final int MENU_ENQUEUE_ALL = 10;
private static final int MENU_MORE_FROM_ALBUM = 11;
private static final int MENU_MORE_FROM_ARTIST = 12;
/**
* Creates a context menu for an adapter row.
*
* @param menu The menu to create.
* @param rowData Data for the adapter row.
*/
public void onCreateContextMenu(ContextMenu menu, Intent rowData)
{
if (rowData.getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID) == LibraryAdapter.HEADER_ID) {
menu.setHeaderTitle(getString(R.string.all_songs));
menu.add(0, MENU_PLAY_ALL, 0, R.string.play_all).setIntent(rowData);
menu.add(0, MENU_ENQUEUE_ALL, 0, R.string.enqueue_all).setIntent(rowData);
menu.addSubMenu(0, MENU_ADD_TO_PLAYLIST, 0, R.string.add_to_playlist).getItem().setIntent(rowData);
} else {
int type = rowData.getIntExtra(LibraryAdapter.DATA_TYPE, MediaUtils.TYPE_INVALID);
boolean isAllAdapter = type <= MediaUtils.TYPE_SONG;
menu.setHeaderTitle(rowData.getStringExtra(LibraryAdapter.DATA_TITLE));
menu.add(0, MENU_PLAY, 0, R.string.play).setIntent(rowData);
if (isAllAdapter)
menu.add(0, MENU_PLAY_ALL, 0, R.string.play_all).setIntent(rowData);
menu.add(0, MENU_ENQUEUE, 0, R.string.enqueue).setIntent(rowData);
if (isAllAdapter)
menu.add(0, MENU_ENQUEUE_ALL, 0, R.string.enqueue_all).setIntent(rowData);
if (type == MediaUtils.TYPE_PLAYLIST) {
menu.add(0, MENU_RENAME_PLAYLIST, 0, R.string.rename).setIntent(rowData);
menu.add(0, MENU_EXPAND, 0, R.string.edit).setIntent(rowData);
} else if (rowData.getBooleanExtra(LibraryAdapter.DATA_EXPANDABLE, false)) {
menu.add(0, MENU_EXPAND, 0, R.string.expand).setIntent(rowData);
}
if (type == MediaUtils.TYPE_ALBUM || type == MediaUtils.TYPE_SONG)
menu.add(0, MENU_MORE_FROM_ARTIST, 0, R.string.more_from_artist).setIntent(rowData);
if (type == MediaUtils.TYPE_SONG)
menu.add(0, MENU_MORE_FROM_ALBUM, 0, R.string.more_from_album).setIntent(rowData);
menu.addSubMenu(0, MENU_ADD_TO_PLAYLIST, 0, R.string.add_to_playlist).getItem().setIntent(rowData);
menu.add(0, MENU_DELETE, 0, R.string.delete).setIntent(rowData);
}
}
/**
* Add a set of songs represented by the intent to a playlist. Displays a
* Toast notifying of success.
*
* @param playlistId The id of the playlist to add to.
* @param intent An intent created with
* {@link LibraryAdapter#createData(View)}.
*/
private void addToPlaylist(long playlistId, Intent intent)
{
QueryTask query = buildQueryFromIntent(intent, true, false);
int count = Playlist.addToPlaylist(getContentResolver(), playlistId, query);
String message = getResources().getQuantityString(R.plurals.added_to_playlist, count, count, intent.getStringExtra("playlistName"));
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
/**
* Open the playlist editor for the playlist with the given id.
*/
private void editPlaylist(Intent rowData)
{
Intent launch = new Intent(this, PlaylistActivity.class);
launch.putExtra("playlist", rowData.getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID));
launch.putExtra("title", rowData.getStringExtra(LibraryAdapter.DATA_TITLE));
startActivity(launch);
}
/**
* Delete the media represented by the given intent and show a Toast
* informing the user of this.
*
* @param intent An intent created with
* {@link LibraryAdapter#createData(View)}.
*/
private void delete(Intent intent)
{
int type = intent.getIntExtra("type", MediaUtils.TYPE_INVALID);
long id = intent.getLongExtra("id", LibraryAdapter.INVALID_ID);
String message = null;
Resources res = getResources();
if (type == MediaUtils.TYPE_FILE) {
String file = intent.getStringExtra("file");
boolean success = MediaUtils.deleteFile(new File(file));
if (!success) {
message = res.getString(R.string.delete_file_failed, file);
}
} else if (type == MediaUtils.TYPE_PLAYLIST) {
Playlist.deletePlaylist(getContentResolver(), id);
} else {
int count = PlaybackService.get(this).deleteMedia(type, id);
message = res.getQuantityString(R.plurals.deleted, count, count);
}
if (message == null) {
message = res.getString(R.string.deleted, intent.getStringExtra("title"));
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
@Override
public boolean onContextItemSelected(MenuItem item)
{
if (item.getGroupId() != 0)
return super.onContextItemSelected(item);
Intent intent = item.getIntent();
switch (item.getItemId()) {
case MENU_EXPAND:
expand(intent);
if (mDefaultAction == ACTION_LAST_USED && mLastAction != ACTION_EXPAND) {
mLastAction = ACTION_EXPAND;
updateHeaders();
}
break;
case MENU_ENQUEUE:
pickSongs(intent, ACTION_ENQUEUE);
break;
case MENU_PLAY:
pickSongs(intent, ACTION_PLAY);
break;
case MENU_PLAY_ALL:
pickSongs(intent, ACTION_PLAY_ALL);
break;
case MENU_ENQUEUE_ALL:
pickSongs(intent, ACTION_ENQUEUE_ALL);
break;
case MENU_NEW_PLAYLIST: {
NewPlaylistDialog dialog = new NewPlaylistDialog(this, null, R.string.create, intent);
dialog.setDismissMessage(mHandler.obtainMessage(MSG_NEW_PLAYLIST, dialog));
dialog.show();
break;
}
case MENU_RENAME_PLAYLIST: {
NewPlaylistDialog dialog = new NewPlaylistDialog(this, intent.getStringExtra("title"), R.string.rename, intent);
dialog.setDismissMessage(mHandler.obtainMessage(MSG_RENAME_PLAYLIST, dialog));
dialog.show();
break;
}
case MENU_DELETE:
mHandler.sendMessage(mHandler.obtainMessage(MSG_DELETE, intent));
break;
case MENU_ADD_TO_PLAYLIST: {
SubMenu playlistMenu = item.getSubMenu();
playlistMenu.add(0, MENU_NEW_PLAYLIST, 0, R.string.new_playlist).setIntent(intent);
Cursor cursor = Playlist.queryPlaylists(getContentResolver());
if (cursor != null) {
for (int i = 0, count = cursor.getCount(); i != count; ++i) {
cursor.moveToPosition(i);
long id = cursor.getLong(0);
String name = cursor.getString(1);
Intent copy = new Intent(intent);
copy.putExtra("playlist", id);
copy.putExtra("playlistName", name);
playlistMenu.add(0, MENU_SELECT_PLAYLIST, 0, name).setIntent(copy);
}
cursor.close();
}
break;
}
case MENU_SELECT_PLAYLIST:
mHandler.sendMessage(mHandler.obtainMessage(MSG_ADD_TO_PLAYLIST, intent));
break;
case MENU_MORE_FROM_ARTIST: {
String selection;
if (intent.getIntExtra(LibraryAdapter.DATA_TYPE, -1) == MediaUtils.TYPE_ALBUM) {
selection = "album_id=";
} else {
selection = "_id=";
}
selection += intent.getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID);
setLimiter(MediaUtils.TYPE_ARTIST, selection);
updateLimiterViews();
break;
}
case MENU_MORE_FROM_ALBUM:
setLimiter(MediaUtils.TYPE_ALBUM, "_id=" + intent.getLongExtra(LibraryAdapter.DATA_ID, LibraryAdapter.INVALID_ID));
updateLimiterViews();
break;
}
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
MenuItem controls = menu.add(null);
CompatHoneycomb.setActionView(controls, mActionControls);
CompatHoneycomb.setShowAsAction(controls, MenuItem.SHOW_AS_ACTION_ALWAYS);
MenuItem search = menu.add(0, MENU_SEARCH, 0, R.string.search).setIcon(R.drawable.ic_menu_search);
CompatHoneycomb.setShowAsAction(search, MenuItem.SHOW_AS_ACTION_IF_ROOM);
} else {
menu.add(0, MENU_SEARCH, 0, R.string.search).setIcon(R.drawable.ic_menu_search);
menu.add(0, MENU_PLAYBACK, 0, R.string.playback_view).setIcon(R.drawable.ic_menu_gallery);
}
menu.add(0, MENU_SORT, 0, R.string.sort_by).setIcon(R.drawable.ic_menu_sort_alphabetically);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu)
{
LibraryAdapter adapter = mCurrentAdapter;
menu.findItem(MENU_SORT).setEnabled(adapter != null && adapter.getMediaType() != MediaUtils.TYPE_FILE);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId()) {
case MENU_SEARCH:
setSearchBoxVisible(!mSearchBoxVisible);
return true;
case MENU_PLAYBACK:
openPlaybackActivity();
return true;
case MENU_SORT: {
MediaAdapter adapter = (MediaAdapter)mCurrentAdapter;
int mode = adapter.getSortMode();
int check;
if (mode < 0) {
check = R.id.descending;
mode = ~mode;
} else {
check = R.id.ascending;
}
int[] itemIds = adapter.getSortEntries();
String[] items = new String[itemIds.length];
Resources res = getResources();
for (int i = itemIds.length; --i != -1; ) {
items[i] = res.getString(itemIds[i]);
}
RadioGroup header = (RadioGroup)getLayoutInflater().inflate(R.layout.sort_dialog, null);
header.check(check);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.sort_by);
builder.setSingleChoiceItems(items, mode + 1, this); // add 1 for header
builder.setNeutralButton(R.string.done, null);
AlertDialog dialog = builder.create();
dialog.getListView().addHeaderView(header);
dialog.setOnDismissListener(this);
dialog.show();
return true;
}
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Call addToPlaylist with the results from a NewPlaylistDialog stored in
* obj.
*/
private static final int MSG_NEW_PLAYLIST = 11;
/**
* Delete the songs represented by the intent stored in obj.
*/
private static final int MSG_DELETE = 12;
/**
* Call renamePlaylist with the results from a NewPlaylistDialog stored in
* obj.
*/
private static final int MSG_RENAME_PLAYLIST = 13;
/**
* Call addToPlaylist with data from the intent in obj.
*/
private static final int MSG_ADD_TO_PLAYLIST = 15;
/**
* Save the current page, passed in arg1, to SharedPreferences.
*/
private static final int MSG_SAVE_PAGE = 16;
@Override
public boolean handleMessage(Message message)
{
switch (message.what) {
case MSG_ADD_TO_PLAYLIST: {
Intent intent = (Intent)message.obj;
addToPlaylist(intent.getLongExtra("playlist", -1), intent);
break;
}
case MSG_NEW_PLAYLIST: {
NewPlaylistDialog dialog = (NewPlaylistDialog)message.obj;
if (dialog.isAccepted()) {
String name = dialog.getText();
long playlistId = Playlist.createPlaylist(getContentResolver(), name);
Intent intent = dialog.getIntent();
intent.putExtra("playlistName", name);
addToPlaylist(playlistId, intent);
}
break;
}
case MSG_DELETE:
delete((Intent)message.obj);
break;
case MSG_RENAME_PLAYLIST: {
NewPlaylistDialog dialog = (NewPlaylistDialog)message.obj;
if (dialog.isAccepted()) {
long playlistId = dialog.getIntent().getLongExtra("id", -1);
Playlist.renamePlaylist(getContentResolver(), playlistId, dialog.getText());
}
break;
}
case MSG_SAVE_PAGE: {
SharedPreferences.Editor editor = PlaybackService.getSettings(this).edit();
editor.putInt("library_page", message.arg1);
editor.commit();
break;
}
default:
return super.handleMessage(message);
}
return true;
}
@Override
public void onMediaChange()
{
mPagerAdapter.invalidateData();
}
private void setSearchBoxVisible(boolean visible)
{
mSearchBoxVisible = visible;
mSearchBox.setVisibility(visible ? View.VISIBLE : View.GONE);
if (mControls != null) {
mControls.setVisibility(visible || (mState & PlaybackService.FLAG_NO_MEDIA) != 0 ? View.GONE : View.VISIBLE);
} else if (mActionControls != null) {
// try to hide the bottom action bar
ViewParent parent = mActionControls.getParent();
if (parent != null)
parent = parent.getParent();
if (parent != null && parent instanceof ViewGroup) {
ViewGroup ab = (ViewGroup)parent;
if (ab.getChildCount() == 1) {
ab.setVisibility(visible ? View.GONE : View.VISIBLE);
}
}
}
if (visible) {
mTextFilter.requestFocus();
((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).showSoftInput(mTextFilter, 0);
}
}
@Override
protected void onStateChange(int state, int toggled)
{
super.onStateChange(state, toggled);
if ((toggled & PlaybackService.FLAG_NO_MEDIA) != 0) {
// update visibility of controls
setSearchBoxVisible(mSearchBoxVisible);
}
if ((toggled & PlaybackService.FLAG_EMPTY_QUEUE) != 0 && mEmptyQueue != null) {
mEmptyQueue.setVisibility((state & PlaybackService.FLAG_EMPTY_QUEUE) == 0 ? View.GONE : View.VISIBLE);
}
}
@Override
protected void onSongChange(Song song)
{
super.onSongChange(song);
if (mTitle != null) {
Bitmap cover = null;
if (song == null) {
if (mActionControls == null) {
mTitle.setText(R.string.none);
mArtist.setText(null);
} else {
mTitle.setText(null);
mArtist.setText(null);
mCover.setImageDrawable(null);
return;
}
} else {
Resources res = getResources();
String title = song.title == null ? res.getString(R.string.unknown) : song.title;
String artist = song.artist == null ? res.getString(R.string.unknown) : song.artist;
mTitle.setText(title);
mArtist.setText(artist);
cover = song.getCover(this);
}
if (Song.mDisableCoverArt)
mCover.setVisibility(View.GONE);
else if (cover == null)
mCover.setImageResource(R.drawable.fallback_cover);
else
mCover.setImageBitmap(cover);
}
}
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
@Override
public void onDismiss(DialogInterface dialog)
{
ListView list = ((AlertDialog)dialog).getListView();
// subtract 1 for header
int which = list.getCheckedItemPosition() - 1;
RadioGroup group = (RadioGroup)list.findViewById(R.id.sort_direction);
if (group.getCheckedRadioButtonId() == R.id.descending)
which = ~which;
mPagerAdapter.setSortMode(which);
}
/**
* Called when a new page becomes visible.
*
* @param position The position of the new page.
* @param adapter The new visible adapter.
*/
public void onPageChanged(int position, LibraryAdapter adapter)
{
mCurrentAdapter = adapter;
mLastActedId = LibraryAdapter.INVALID_ID;
updateLimiterViews();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
CompatHoneycomb.selectTab(this, position);
}
if (adapter != null && adapter.getLimiter() == null) {
// Save current page so it is opened on next startup. Don't save if
// the page was expanded to, as the expanded page isn't the starting
// point.
Handler handler = mHandler;
handler.sendMessage(mHandler.obtainMessage(MSG_SAVE_PAGE, position, 0));
}
}
@Override
public ApplicationInfo getApplicationInfo()
{
ApplicationInfo info;
if (mFakeTarget) {
info = mFakeInfo;
if (info == null) {
info = new ApplicationInfo(super.getApplicationInfo());
info.targetSdkVersion = Build.VERSION_CODES.GINGERBREAD;
mFakeInfo = info;
}
} else {
info = super.getApplicationInfo();
}
return info;
}
}