Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Last active January 26, 2024 09:40
Show Gist options
  • Save sunmeat/319e4395b547869db334cd9f5619d2ba to your computer and use it in GitHub Desktop.
Save sunmeat/319e4395b547869db334cd9f5619d2ba to your computer and use it in GitHub Desktop.
simple mp3-player android (service example)
MainActivity.java: (CREATE NEW EMPTY ACTIVITY PROJECT!)
package com.alex.player;
import package com.alex.player.MusicService.MusicBinder;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.os.IBinder;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.view.MenuItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import android.net.Uri;
import android.content.ContentResolver;
import android.database.Cursor;
import android.widget.ListView;
import android.widget.MediaController;
import android.widget.MediaController.MediaPlayerControl;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
public class MainActivity extends AppCompatActivity implements MediaPlayerControl {
private boolean paused;
private boolean playbackPaused;
private MediaController controller;
private MusicService musicSrv;
private Intent playIntent;
private boolean musicBound = false;
private ArrayList<Song> songList;
@Override
protected void onPause() {
super.onPause();
paused = true;
}
@Override
protected void onResume() {
super.onResume();
if (paused) {
setController();
paused = false;
}
}
@Override
protected void onStop() {
controller.hide();
super.onStop();
}
private void setController() {
controller = new MediaController(this);
controller.setPrevNextListeners(new View.OnClickListener() {
@Override
public void onClick(View v) {
playNext();
}
}, new View.OnClickListener() {
@Override
public void onClick(View v) {
playPrev();
}
});
controller.setMediaPlayer(this);
controller.setAnchorView(findViewById(R.id.song_list));
controller.setEnabled(true);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// https://github.com/Karumi/Dexter
Dexter.withActivity(this)
.withPermissions(Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WAKE_LOCK)
.withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
// Toast.makeText(MainActivity.this, "OK", Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
}
}).check();
ListView songView = findViewById(R.id.song_list);
songList = new ArrayList<>();
getSongList();
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);
setController();
}
private void playNext() {
musicSrv.playNext();
if (playbackPaused) {
setController();
playbackPaused = false;
}
controller.show(0);
}
private void playPrev() {
musicSrv.playPrev();
if (playbackPaused) {
setController();
playbackPaused = false;
}
controller.show(0);
}
public void getSongList() {
ContentResolver musicResolver = getContentResolver();
Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null);
if (musicCursor != null && musicCursor.moveToFirst()) {
int titleColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.TITLE);
int idColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media._ID);
int artistColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.ARTIST);
do {
long thisId = musicCursor.getLong(idColumn);
String thisTitle = musicCursor.getString(titleColumn);
String thisArtist = musicCursor.getString(artistColumn);
songList.add(new Song(thisId, thisTitle, thisArtist));
}
while (musicCursor.moveToNext());
}
}
private ServiceConnection musicConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicBinder binder = (MusicBinder) service;
musicSrv = binder.getService();
musicSrv.setList(songList);
musicBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};
public void songPicked(View view) {
musicSrv.setSong(Integer.parseInt(view.getTag().toString()));
musicSrv.playSong();
if (playbackPaused) {
setController();
playbackPaused = false;
}
controller.show(0);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_shuffle:
musicSrv.setShuffle();
break;
case R.id.action_end:
stopService(playIntent);
musicSrv = null;
System.exit(0);
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
protected void onStart() {
super.onStart();
if (playIntent == null) {
playIntent = new Intent(this, MusicService.class);
bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
startService(playIntent);
}
}
@Override
protected void onDestroy() {
stopService(playIntent);
musicSrv = null;
super.onDestroy();
}
@Override
public void pause() {
playbackPaused = true;
musicSrv.pausePlayer();
}
@Override
public void seekTo(int pos) {
musicSrv.seek(pos);
}
@Override
public void start() {
musicSrv.go();
}
@Override
public int getDuration() {
if (musicSrv != null && musicBound && musicSrv.isPng())
return musicSrv.getDur();
else return 0;
}
@Override
public int getCurrentPosition() {
if (musicSrv != null && musicBound && musicSrv.isPng())
return musicSrv.getPosn();
else return 0;
}
@Override
public boolean isPlaying() {
return musicSrv != null && musicBound && musicSrv.isPng();
}
@Override
public int getBufferPercentage() {
return 0;
}
@Override
public boolean canPause() {
return true;
}
@Override
public boolean canSeekBackward() {
return true;
}
@Override
public boolean canSeekForward() {
return true;
}
@Override
public int getAudioSessionId() {
return 0;
}
}
==================================================================================================================================
MusicService.java:
package com.alex.player;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import java.util.ArrayList;
import android.content.ContentUris;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Binder;
import android.os.PowerManager;
import android.util.Log;
import java.util.Random;
import android.app.Notification;
import android.app.PendingIntent;
public class MusicService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
private boolean shuffle = false;
private Random rand;
private String songTitle = "";
private static final int NOTIFY_ID = 1;
private MediaPlayer player;
private ArrayList<Song> songs;
private int songPosn;
private final IBinder musicBind = new MusicBinder();
public void setShuffle() {
shuffle = !shuffle;
}
public void onCreate() {
super.onCreate();
rand = new Random();
songPosn = 0;
player = new MediaPlayer();
initMusicPlayer();
}
public void initMusicPlayer() {
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 class MusicBinder extends Binder {
MusicService getService() {
return MusicService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return musicBind;
}
@Override
public boolean onUnbind(Intent intent) {
player.stop();
player.release();
return false;
}
public void playSong() {
player.reset();
Song playSong = songs.get(songPosn);
songTitle = playSong.getTitle();
long currSong = playSong.getID();
Uri trackUri = ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
currSong);
try {
player.setDataSource(getApplicationContext(), trackUri);
} catch (Exception e) {
Log.e("MUSIC SERVICE", "Error setting data source", e);
}
player.prepareAsync();
}
@Override
public void onDestroy() {
stopForeground(true);
}
@Override
public void onCompletion(MediaPlayer mp) {
if (player.getCurrentPosition() > 0) {
mp.reset();
playNext();
}
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return false;
}
public void setSong(int songIndex) {
songPosn = songIndex;
}
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
Intent notIntent = new Intent(this, MainActivity.class);
notIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendInt = PendingIntent.getActivity(this, 0, notIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(pendInt)
.setSmallIcon(R.drawable.play)
.setTicker(songTitle)
.setOngoing(true)
.setContentTitle("Playing")
.setContentText(songTitle);
Notification not = builder.build();
startForeground(NOTIFY_ID, not);
}
public int getPosn() {
return player.getCurrentPosition();
}
public int getDur() {
return player.getDuration();
}
public boolean isPng() {
return player.isPlaying();
}
public void pausePlayer() {
player.pause();
}
public void seek(int posn) {
player.seekTo(posn);
}
public void go() {
player.start();
}
public void playPrev() {
songPosn--;
if (songPosn < 0) songPosn = songs.size() - 1;
playSong();
}
public void playNext() {
if (shuffle) {
int newSong = songPosn;
while (newSong == songPosn) {
newSong = rand.nextInt(songs.size());
}
songPosn = newSong;
} else {
songPosn++;
if (songPosn >= songs.size()) songPosn = 0;
}
playSong();
}
}
==================================================================================================================================
Song.java:
package com.alex.player;
class Song {
private long id;
private String title;
private String artist;
Song(long songID, String songTitle, String songArtist) {
id = songID;
title = songTitle;
artist = songArtist;
}
long getID() {
return id;
}
String getTitle() {
return title;
}
String getArtist() {
return artist;
}
}
==================================================================================================================================
SongAdapter.java:
package com.alex.player;
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.BaseAdapter;
class SongAdapter extends BaseAdapter {
private ArrayList<Song> songs;
private LayoutInflater songInf;
SongAdapter(Context c, ArrayList<Song> theSongs) {
songs = theSongs;
songInf = LayoutInflater.from(c);
}
@Override
public int getCount() {
return songs.size();
}
@Override
public Object getItem(int arg0) {
return null;
}
@Override
public long getItemId(int arg0) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout songLay = (LinearLayout) songInf.inflate
(R.layout.song, parent, false);
TextView songView = (TextView) songLay.findViewById(R.id.song_title);
TextView artistView = (TextView) songLay.findViewById(R.id.song_artist);
Song currSong = songs.get(position);
songView.setText(currSong.getTitle());
artistView.setText(currSong.getArtist());
songLay.setTag(position);
return songLay;
}
}
==================================================================================================================================
drawable: end, play, rand
==================================================================================================================================
layout / activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF000000"
android:orientation="vertical"
tools:context=".MainActivity">
<ListView
android:id="@+id/song_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
==================================================================================================================================
layout / song.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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="#FFFFFFFF"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/song_artist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#FFAAAAAA"
android:textSize="18sp" />
</LinearLayout>
==================================================================================================================================
menu / menu_main.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_shuffle"
android:icon="@drawable/rand"
android:orderInCategory="1"
app:showAsAction="ifRoom"
android:title="Shuffle"/>
<item
android:id="@+id/action_end"
android:icon="@drawable/end"
android:orderInCategory="2"
app:showAsAction="ifRoom"
android:title="End"/>
</menu>
==================================================================================================================================
AndroidManifest.xml: (check service!)
...
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
==================================================================================================================================
build.gradle (Module:app):
dependencies {
...
implementation 'com.karumi:dexter:5.0.0'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment