Makille Makille - 2 months ago 10
Android Question

Android: How to use shuffle mode in a music player without reordering the ListView?

I'm making a Music Player for Android and I'm facing with the shuffle problem.
Actually the shuffle mode works perfectly, but it also reorder my ListView with all my songs, and I don't want it to.
I can't find a way to use shuffle mode without reordering also my ListView.

Here's part of the code I've used:

-This is the class I used to define song objects:

public class Song {

private long ID;
private String Title;
private String Artist;
private long AlbumID;

public Song(long songID, String songTitle, String songArtist, long songAlbumID) {
ID=songID;
Title=songTitle;
Artist=songArtist;
AlbumID=songAlbumID;
}


public long getID(){return ID;}
public String getTitle(){return Title;}
public String getArtist(){return Artist;}
public long getAlbumID(){return AlbumID;}
}


-This is the MainActivity, connected to the activity_main layout:

public class MainActivity extends AppCompatActivity {

//Variables for the song list.
private static ArrayList<Song> songList;
private static ListView songView;
//For the playback
private MusicService musicSrv;
private Intent playIntent;
private boolean musicBound = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setVolumeControlStream(AudioManager.STREAM_MUSIC);

loadSongLayout();
}

//Load the song list
public void loadSongLayout(){
try{
//Obtain song list
songView = (ListView) findViewById(R.id.song_list);
songList = new ArrayList<Song>();
getSongList();
//Sort the song list
Collections.sort(songList, new Comparator<Song>() {
public int compare(Song a, Song b) {
return a.getTitle().compareTo(b.getTitle());
}
});
SongAdapter songAdt = new SongAdapter(this, songList);
songView.setAdapter(songAdt);
}
catch(Exception e){
Log.e("ERROR", String.valueOf(e));
}
}
public void getSongList() {
//Retrieve song info
ContentResolver musicResolver = getContentResolver();
Uri musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null);

if (musicCursor != null && musicCursor.moveToFirst()) {
//Get columns
int idColumn = musicCursor.getColumnIndex
(MediaStore.Audio.Media._ID);
int titleColumn = musicCursor.getColumnIndex
(MediaStore.Audio.Media.TITLE);
int artistColumn = musicCursor.getColumnIndex
(MediaStore.Audio.Media.ARTIST);
int albumIdColumn = musicCursor.getColumnIndex
(MediaStore.Audio.Media.ALBUM_ID);
int isMusicColumn = musicCursor.getColumnIndex
(MediaStore.Audio.Media.IS_MUSIC);
//Add songs to list
do {
long thisId = musicCursor.getLong(idColumn);
String thisTitle = musicCursor.getString(titleColumn);
String thisArtist = musicCursor.getString(artistColumn);
Long thisAlbumId = musicCursor.getLong(albumIdColumn);

if (musicCursor.getInt(isMusicColumn) == 1) {
songList.add(new Song(thisId, thisTitle, thisArtist, thisAlbumId));
}
}
while (musicCursor.moveToNext());
}
}


//For the playback
//Connect to the service
private ServiceConnection musicConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
//Get service
musicSrv = binder.getService();
//Pass list
musicSrv.setList(songList);
musicBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};
@Override
protected void onStart() {
super.onStart();
if (playIntent == null) {
playIntent = new Intent(this, MusicService.class);
bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
startService(playIntent);
}
}
public void songPicked(View view) {
musicSrv.setSong(Integer.parseInt(view.getTag().toString()));
musicSrv.playSong();
isMusicStarted=true;
this.invalidateOptionsMenu();
}
}


-And then, the MusicService:

public class MusicService extends Service implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {

private static MediaPlayer player; //Media player
private static ArrayList<Song> songs; //Song list
private static int songPosn; //Current position
private static boolean shuffle = false;

public void onCreate(){
super.onCreate(); //Create the service
songPosn=0; //Initialize position
player = new MediaPlayer(); //Create player
context=getApplicationContext();
initMusicPlayer();
}

public void initMusicPlayer(){
//set player properties
player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
}

public void setList(ArrayList<Song> theSongs){
songs=theSongs;
}
public static void setShuffle(){
if (shuffle){
long tempID = songs.get(songPosn).getID();
shuffle = false;
Collections.sort(songs, new Comparator<Song>() {
public int compare(Song a, Song b) {
return a.getTitle().compareTo(b.getTitle());
}
});
int pos=0;
//update the position in the list with the correct one
while (pos<songs.size() && tempID != songs.get(pos).getID()){
pos++;
}
songPosn=pos;
}
else {
long tempID = songs.get(songPosn).getID();
shuffle = true;
long seed = System.nanoTime();
Collections.shuffle(songs, new Random(seed));
Collections.shuffle(songs, new Random(seed));
int pos=0;
while (pos<songs.size() && tempID != songs.get(pos).getID()){
pos++;
}
songPosn=pos;
}
}

public static void playSong(){
isRefreshing=true;
player.reset();
//get song id
long currSong = songs.get(songPosn).getID();
//set uri
Uri trackUri = ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
currSong);
try{
player.setDataSource(context, trackUri);
}
catch(Exception e){
Log.e("MUSIC SERVICE", "Error playing the song", e);
}
player.prepareAsync();
}

public static void playNext(){
if (songPosn<(songs.size()-1)) {
songPosn++;
}
else {
songPosn=0;
}
playSong();
}

public class MusicBinder extends Binder {
MusicService getService() {return MusicService.this;}
}
private final IBinder musicBind = new MusicBinder();
@Override
public IBinder onBind(Intent intent) {
return musicBind;
}
@Override
public boolean onUnbind(Intent intent){
player.stop();
player.release();
return false;
}
public void onCompletion(MediaPlayer mp) {
playNext();
}
}


-Last class, this is the SongAdapter:

public class SongAdapter extends BaseAdapter {


private ArrayList<Song> songs;
private LayoutInflater songInf;


public SongAdapter(Context c, ArrayList<Song> theSongs){
songs=theSongs;
songInf=LayoutInflater.from(c);
}

@Override
public int getCount() {
return songs.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//map to song layout
LinearLayout songLay = (LinearLayout)songInf.inflate
(R.layout.song_details, parent, false);
//get title and artist views
TextView songView = (TextView)songLay.findViewById(R.id.song_title);
TextView artistView = (TextView)songLay.findViewById(R.id.song_artist);
//get song using position
Song currSong = songs.get(position);
//get title and artist strings
songView.setText(currSong.getTitle());
artistView.setText(currSong.getArtist());
//set position as tag
songLay.setTag(position);
return songLay;
}
}


-These are the two layouts used to build the list:

activity_main

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.marcomarchiori.mplayer.MainActivity">

<ListView
android:id="@+id/song_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#bdbdbd"
android:dividerHeight="1px">
</ListView>

</LinearLayout>


song_details

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="songPicked"
android:orientation="vertical"
android:padding="5dp" >

<TextView
android:id="@+id/song_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#424242"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center_horizontal" />

<TextView
android:id="@+id/song_artist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#616161"
android:textSize="14sp"
android:gravity="center_horizontal" />

</LinearLayout>


Obviously I didn't copy all my code, most of that isn't about my problem.
Thanks in advance and sorry for my bad English.

Answer

Ok, I found a solution: I shouldn't use an ArrayList in this case but a normale array. So, instead of ArrayList<Song>, I should use Song[].