I have searched a lot on stackoverflow regarding this issue but nothing seems to work.
I am developing a music player which plays music by binding to a service. I have two activities.The first activity(called as AllSongs) provides me with the list of songs.When I select a song it starts another activity(called as SongUI) which plays the song by binding to a service.
Now when I move back to my AllSongs activity the music stops.Now when I select a song again,my SongUI activity is started and when I go back to my AllSongs activity, the music does not stop and plays in the background.I am not able to understand what is causing this issue. I think I am doing something silly somewhere but am not able to figure it out. I want songs to be played in the background same as any music player does.Here is the code.
AllSongs activity:
public class AllSongs extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.allsongs_activity);
getSongList();
from = new String[]{MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.DURATION};
to = new int[]{R.id.title_entry,R.id.artist_entry,R.id.duration_entry};
listView = (ListView) findViewById(R.id.listView);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,R.layout.singlesong,myCursor,from,to,SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent songUIIntent = new Intent(getApplicationContext(),SongUI.class);
songUIIntent.putExtra("position",id);
startActivity(songUIIntent);
}
});
}
private void getSongList() {
ContentResolver myContentResolver = getContentResolver();
myCursor = myContentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null);
if(myCursor!=null && myCursor.moveToFirst()) {
int titleColumn = myCursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int idColumn = myCursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
int artistColumn = myCursor.getColumnIndex(android.provider.MediaStore.Audio.Media.ARTIST);
long albumID = myCursor.getLong(myCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
do {
long ID = myCursor.getLong(idColumn);
String title = myCursor.getString(titleColumn);
String artist = myCursor.getString(artistColumn);
songList.add(new currentSong(ID,title,artist));
}while (myCursor.moveToNext());
}
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
SongUI activity:
public class SongUI extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song_ui);
button = (Button) findViewById(R.id.play);
isBound = false;
Intent receivedIntent = getIntent();
position = receivedIntent.getLongExtra("position",0);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main,menu);
return super.onCreateOptionsMenu(menu);
}
public void playAudio(View view) {
Intent objIntent = new Intent(this,MyService.class);
if(!isBound)
{
objIntent.putExtra("position", position);
startService(objIntent);
isBound=true;
button.setBackgroundResource(R.drawable.play);
bindService(objIntent, myConnection, Context.BIND_AUTO_CREATE);
}
else
{
myServiceObject.pauseAudio();
button.setBackgroundResource(R.drawable.play);
isBound=false;
unbindService(myConnection);
}
}
public void stopAudio(View view) {
Intent objIntent = new Intent(this,MyService.class);
if(isBound)
{
isBound=false;
unbindService(myConnection);
stopService(objIntent);
}
else {
stopService(objIntent);
}
button.setBackgroundResource(R.drawable.play);
}
private ServiceConnection myConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
myServiceObject = ((MusicBinder) service).getService();
isBound = true;
}
public void onServiceDisconnected(ComponentName arg0) {
isBound = false;
}
};
#Override
protected void onDestroy() {
super.onDestroy();
if (isBound) {
unbindService(myConnection);
isBound = false;
}
}
}
MyService class
public class MyService extends Service {
#Override
public void onCreate() {
super.onCreate();
player = new MediaPlayer();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "on startCommand is called");
long id = intent.getLongExtra("position",0);
Uri contentUri = ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
Log.d(TAG, "Service is Started");
player = MediaPlayer.create(this,contentUri);
player.start();
Intent notIntent = new Intent(this, SongUI.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 notification = builder.build();
startForeground(NOTIFY_ID, notification);
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "on Bind is called");
return myBinder;
}
#Override
public boolean onUnbind(Intent intent){
Log.d(TAG,"onUnbind is called");
player.stop();
player.release();
return false;
}
public class MusicBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
public void pauseAudio() {
if(player.isPlaying()) {
player.pause();
}
}
#Override
public void onDestroy() {
Log.d(TAG,"on destroy is called");
stopForeground(true);
}
I have figured out where I was going wrong. I was calling unbindService(myConnection) in onDestroy of my SongsUI activity which was stopping the playback.
And the reason why this was not the case the second time round was that I was returning false in onUnbind method. So the system was not calling onBind method the second time and subsequently onUnbind was also not getting called. Hence the playback was also not stopping.
Related
I created service class to play/pause songs from Activity. But am unable to run that service from AppWidget & also unable to handle click event like how I handled in Activity. Valuable comments/answers are highly appreciated. Thank you.
Service Class
public class MusicService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
//media player
private MediaPlayer player;
//song list
private ArrayList<Song> songs;
//current position
private int songPosn;
//binder
private final IBinder musicBind = new MusicBinder();
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.e("***", "MusicService - onBind");
return musicBind;
}
#Override
public boolean onUnbind(Intent intent) {
Log.e("***", "MusicService - onUnbind");
player.stop();
player.release();
return false;
}
#Override
public void onDestroy() {
Log.e("***", "MusicService - onDestroy");
super.onDestroy();
}
#Override
public void onCreate() {
super.onCreate();
Log.e("***", "MusicService - onCreate");
//initialize position
songPosn = 0;
//create player
player = new MediaPlayer();
//initialize
initMusicPlayer();
}
public void initMusicPlayer() {
//set player properties
player.setWakeMode(getApplicationContext(),
PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
//set listeners
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
}
#Override
public void onCompletion(MediaPlayer mp) {
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return false;
}
#Override
public void onPrepared(MediaPlayer mp) {
//start playback
mp.start();
}
public void setList(ArrayList<Song> theSongs) {
songs = theSongs;
}
public class MusicBinder extends Binder {
public MusicService getService() {
return MusicService.this;
}
}
public void playSong() {
//play a song
player.reset();
//get song
Song playSong = songs.get(songPosn);
//get id
long currSong = playSong.getID();
//set uri
Uri trackUri = ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
currSong);
//set the data source
try {
player.setDataSource(getApplicationContext(), trackUri);
} catch (Exception e) {
Log.e("MUSIC SERVICE", "Error setting data source", e);
}
player.prepareAsync();
}
public void stopSong() {
player.stop();
player.reset();
}
public void pauseSong() {
player.pause();
}
public void resumeSong() {
player.start();
}
public void setSong(int songIndex) {
songPosn = songIndex;
}
}
AppWidget class
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
// //views.setTextViewText(R.id.appwidget_text, String.valueOf(appWidgetId));
// Create an Intent to launch ExampleActivity
// Intent intent = new Intent(context, MainActivity.class);
// PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
Intent intent = new Intent(context, MusicService.class);
PendingIntent pendingIntent = PendingIntent.getForegroundService(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener
// to the button
views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
Your service is bound, so problem is that you can't bind to service from AppWidgetProvider (since it's a BroadcastReceiver under the hood). You already make a pending intents on button clicks, just make this intents unique and determine which action is right now in onStartCommand() call in service.
While preparing your remoteview:
Intent intent = new Intent(context, MusicService.class);
intent.setAction("TEXT_CLICK");
PendingIntent pendingIntent = PendingIntent.getForegroundService(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent);
So now in onStartCommand check for this action like this:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null || intent.getAction() == null) {
return super.onStartCommand(null, flags, startId);
}
if (intent.getAction().equals("TEXT_CLICK")) {
//make whatever you want (play/stop song, next/previous etc)
}
return super.onStartCommand(intent, flags, startId);
}
I have implemented a Service to run the audio in background which runs perfectly but I am unable to get the instance of the SimpleExoPlayer from the service to the activity to update the UI and also the Audio plays twice in the background if I exit and reopen the activity.
AudioPlayerService
public class AudioPlayerService extends Service {
private final IBinder mBinder = new LocalBinder();
private SimpleExoPlayer player;
private Item item;
private PlayerNotificationManager playerNotificationManager;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
playerNotificationManager.setPlayer(null);
player.release();
player = null;
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
enter code here
public SimpleExoPlayer getplayerInstance() {
return player;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle b = intent.getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
item = b.getParcelable(AppConstants.ITEM_KEY);
}
startPlayer();
return START_STICKY;
}
private void startPlayer() {
final Context context = this;
Uri uri = Uri.parse(item.getUrl());
player = ExoPlayerFactory.newSimpleInstance(context, new DefaultTrackSelector());
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, getString(R.string.app_name)));
MediaSource mediaSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(uri);
player.prepare(mediaSource);
player.setPlayWhenReady(true);
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(context, AppConstants.PLAYBACK_CHANNEL_ID,
R.string.playback_channel_name,
AppConstants.PLAYBACK_NOTIFICATION_ID,
new PlayerNotificationManager.MediaDescriptionAdapter() {
#Override
public String getCurrentContentTitle(Player player) {
return item.getTitle();
}
#Nullable
#Override
public PendingIntent createCurrentContentIntent(Player player) {
Intent intent = new Intent(context, PlayerActivity.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
#Nullable
#Override
public String getCurrentContentText(Player player) {
return item.getSummary();
}
#Nullable
#Override
public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
return item.getBitmap();
}
}
);
playerNotificationManager.setNotificationListener(new PlayerNotificationManager.NotificationListener() {
#Override
public void onNotificationStarted(int notificationId, Notification notification) {
startForeground(notificationId, notification);
}
#Override
public void onNotificationCancelled(int notificationId) {
stopSelf();
}
});
playerNotificationManager.setPlayer(player);
}
public class LocalBinder extends Binder {
public AudioPlayerService getService() {
return AudioPlayerService.this;
}
}
}
This is my activity where I am starting the service and binding to it. I have to pass the Item object in-order the service to run, if I don't pass the data using the intent the service will crash so I can't do startService() in the service itself I have to start in the activity I guess.
PlayerActivity
public class PlayerActivity extends BaseActivity {
#BindView(R.id.video_view)
PlayerView mPlayerView;
#BindView(R.id.tvTitle)
TextView mTvTitle;
#BindView(R.id.tvSummary)
TextView mTvSummary;
#BindView(R.id.ivThumbnail)
ImageView mIvThumb;
private SimpleExoPlayer player;
private String mURL, mTitle, mSummary, mImage;
private AudioPlayerService mService;
private boolean mBound = false;
private Intent intent;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
AudioPlayerService.LocalBinder binder = (AudioPlayerService.LocalBinder) iBinder;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false;
}
};
#SuppressLint("MissingSuperCall")
#Override
protected void onCreate(Bundle savedInstanceState) {
onCreate(savedInstanceState, R.layout.activity_player);
Bundle b = getIntent().getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
Item item = b.getParcelable(AppConstants.ITEM_KEY);
mURL = item.getUrl();
mImage = item.getImage();
mTitle = item.getTitle();
mSummary = item.getSummary();
intent = new Intent(this, AudioPlayerService.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
Util.startForegroundService(this, intent);
}
}
private void initializePlayer() {
if (player == null && !mURL.isEmpty() && mBound) {
player = mService.getplayerInstance();
mPlayerView.setPlayer(player);
mPlayerView.setControllerHideOnTouch(false);
mPlayerView.setControllerShowTimeoutMs(10800000);
}
}
#Override
public void onStart() {
super.onStart();
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
initializePlayer();
setUI();
}
private void setUI() {
mTvTitle.setText(mTitle);
mTvSummary.setText(mSummary);
GlideApp.with(this)
.load(mImage)
.placeholder(R.color.colorPrimary)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(mIvThumb);
}
#Override
protected void onStop() {
unbindService(mConnection);
mBound = false;
releasePlayer();
super.onStop();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.player_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.share_podcast:
//Logic for Share
return true;
case R.id.download_podcast:
//Logic for download
return true;
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void releasePlayer() {
if (player != null) {
player.release();
player = null;
}
}
#Override
public void onToolBarSetUp(Toolbar toolbar, ActionBar actionBar) {
TextView tvHeader = toolbar.findViewById(R.id.tvClassName);
tvHeader.setText(R.string.app_name);
actionBar.setHomeAsUpIndicator(R.drawable.ic_arrow_back_black_24dp);
}
}
I have tried everything I know but I'm unable to move forward because of this.
So after a lot of research I was able to resolve this using the bound service and getting the SimpleExoPlayer instance from the service and set the Player view to always show up using the following method.
mPlayerView.showController()
After all the modification and setup it takes only two classes to achieve background audio playback with notification control, one is the activity and the other is the service using the latest exoplayer release.
PlayerActivity
public class PlayerActivity extends BaseActivity {
#BindView(R.id.video_view)
PlayerView mPlayerView;
#BindView(R.id.tvTitle)
TextView mTvTitle;
#BindView(R.id.tvSummary)
TextView mTvSummary;
#BindView(R.id.ivThumbnail)
ImageView mIvThumb;
private String mUrl, mTitle, mSummary, mImage;
private AudioPlayerService mService;
private Intent intent;
private String shareableLink;
private boolean mBound = false;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
AudioPlayerService.LocalBinder binder = (AudioPlayerService.LocalBinder) iBinder;
mService = binder.getService();
mBound = true;
initializePlayer();
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false;
}
};
#SuppressLint("MissingSuperCall")
#Override
protected void onCreate(Bundle savedInstanceState) {
onCreate(savedInstanceState, R.layout.activity_player);
Bundle b = getIntent().getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
Item item = b.getParcelable(AppConstants.ITEM_KEY);
shareableLink = b.getString(AppConstants.SHARE_KEY);
mImage = item.getImage();
mUrl = item.getUrl();
mTitle = item.getTitle();
mSummary = item.getSummary();
intent = new Intent(this, AudioPlayerService.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
Util.startForegroundService(this, intent);
mPlayerView.setUseController(true);
mPlayerView.showController();
mPlayerView.setControllerAutoShow(true);
mPlayerView.setControllerHideOnTouch(false);
}
}
private void initializePlayer() {
if (mBound) {
SimpleExoPlayer player = mService.getplayerInstance();
mPlayerView.setPlayer(player);
}
}
#Override
public void onStart() {
super.onStart();
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
initializePlayer();
setUI();
}
private void setUI() {
mTvTitle.setText(mTitle);
mTvSummary.setText(mSummary);
GlideApp.with(this)
.load(mImage)
.placeholder(R.color.colorPrimary)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(mIvThumb);
}
#Override
protected void onStop() {
unbindService(mConnection);
mBound = false;
super.onStop();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.player_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.share_podcast:
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_SUBJECT, mTitle);
shareIntent.putExtra(Intent.EXTRA_TEXT, mTitle + "\n\n" + shareableLink);
shareIntent.setType("text/plain");
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_text)));
return true;
case R.id.download_podcast:
Uri uri = Uri.parse(mUrl);
ProgressiveDownloadAction progressiveDownloadAction
= new ProgressiveDownloadAction(uri, false, null, null);
AudioDownloadService.startWithAction(PlayerActivity.this, AudioDownloadService.class,
progressiveDownloadAction, false);
return true;
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public void onToolBarSetUp(Toolbar toolbar, ActionBar actionBar) {
TextView tvHeader = toolbar.findViewById(R.id.tvClassName);
tvHeader.setText(R.string.app_name);
actionBar.setHomeAsUpIndicator(R.drawable.ic_arrow_back_black_24dp);
}
}
AudioPlayerService
public class AudioPlayerService extends Service {
private final IBinder mBinder = new LocalBinder();
private SimpleExoPlayer player;
private Item item;
private PlayerNotificationManager playerNotificationManager;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
releasePlayer();
super.onDestroy();
}
private void releasePlayer() {
if (player != null) {
playerNotificationManager.setPlayer(null);
player.release();
player = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public SimpleExoPlayer getplayerInstance() {
if (player == null) {
startPlayer();
}
return player;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//releasePlayer();
Bundle b = intent.getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
item = b.getParcelable(AppConstants.ITEM_KEY);
}
if (player == null) {
startPlayer();
}
return START_STICKY;
}
private void startPlayer() {
final Context context = this;
Uri uri = Uri.parse(item.getUrl());
player = ExoPlayerFactory.newSimpleInstance(context, new DefaultTrackSelector());
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, getString(R.string.app_name)));
CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(
DownloadUtil.getCache(context),
dataSourceFactory,
CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
MediaSource mediaSource = new ExtractorMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(uri);
player.prepare(mediaSource);
player.setPlayWhenReady(true);
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(context, AppConstants.PLAYBACK_CHANNEL_ID,
R.string.playback_channel_name,
AppConstants.PLAYBACK_NOTIFICATION_ID,
new PlayerNotificationManager.MediaDescriptionAdapter() {
#Override
public String getCurrentContentTitle(Player player) {
return item.getTitle();
}
#Nullable
#Override
public PendingIntent createCurrentContentIntent(Player player) {
Intent intent = new Intent(context, PlayerActivity.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
#Nullable
#Override
public String getCurrentContentText(Player player) {
return item.getSummary();
}
#Nullable
#Override
public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
return null;
}
}
);
playerNotificationManager.setNotificationListener(new PlayerNotificationManager.NotificationListener() {
#Override
public void onNotificationStarted(int notificationId, Notification notification) {
startForeground(notificationId, notification);
}
#Override
public void onNotificationCancelled(int notificationId) {
stopSelf();
}
});
playerNotificationManager.setPlayer(player);
}
public class LocalBinder extends Binder {
public AudioPlayerService getService() {
return AudioPlayerService.this;
}
}
}
I am developing a very simple app that play a song forever until the user stop it.
I am using the bind/unbind service functions just to send a simple message ( hello ) from the activity to the service.
The problem is that when I tap to stop the song, it doesn't stop ( even if in the Logcat I see the "stop" string ). Why ?
This is my code:
MainActivity:
public class MainActivity extends AppCompatActivity {
boolean started = false;
boolean musicBound = false;
TextView button;
private Intent playIntent;
private PlayerService musicSrv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (TextView) findViewById(R.id.playstopbutton);
playIntent = new Intent(this, PlayerService.class);
}
#Override
public void onResume(){
super.onResume();
// put your code here...
if(!started) {
// button.setText(">");
}else{
// button.setText("||");
}
}
//connect to the service
private ServiceConnection musicConnection = new ServiceConnection(){
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("My App", "conn");
PlayerService.MusicBinder binder = (PlayerService.MusicBinder)service;
//get service
musicSrv = binder.getService();
//pass list
musicSrv.tryit("hello");
musicBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.i("My App", "disc");
musicBound = false;
}
};
public void playstop(View v)
{
if(!started) {
Log.i("My App", "start");
started = true;
// button.setText("||");
bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
startService(playIntent);
}else{
Log.i("My App", "stop");
started = false;
// button.setText(">");
stopService(playIntent);
musicSrv=null;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
protected void onDestroy() {
if (musicConnection != null) {
unbindService(musicConnection);
}
stopService(playIntent);
musicSrv=null;
super.onDestroy();
}
}
PlayerService:
public class PlayerService extends Service
{
MediaPlayer mediaPlayer = null;
String title = "aaaa";
String msg = "bbbb";
String ticker = "just a ticker";
private final IBinder musicBind = new MusicBinder();
#Override
public void onCreate() {
mediaPlayer = MediaPlayer.create(this, R.raw.song);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.setLooping(true);
mediaPlayer.start();
}
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(this)
.setContentTitle(title)
.setContentText(msg)
.setSmallIcon(R.drawable.note2)
.setContentIntent(pendingIntent)
.setTicker(ticker).setColor(0xff222222).build();
startForeground(11, notification);
return START_STICKY;
}
//binder
public class MusicBinder extends Binder {
PlayerService getService() {
return PlayerService.this;
}
}
//activity will bind to service
#Override
public IBinder onBind(Intent intent) {
return musicBind;
}
//release resources when unbind
#Override
public boolean onUnbind(Intent intent){
mediaPlayer.stop();
mediaPlayer.release();
return false;
}
public void tryit(String stringa){
Log.i("My App", stringa);
}
public void onDestroy() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.release();
stopForeground(true);
Log.i("PROVA SERVICE", "Distruzione Service");
}
}
when I tap to stop the song, it doesn't stop
Because you are only calling stop() on the MediaPlayer if either:
the client unbinds from it (onUnbind()), which is not what your client is doing, or
the client unbinds from it and you stop it (onDestroy()), which is not what your client is doing
A service will be destroyed when both of the following statements are true:
Each client that had bound to is unbinds, and
If startService() had been called for this service, stopService() is called
In this case, you are calling stopService(), but you are still bound to the service, so onDestroy() is not called.
Following [this tutorial] (http://www.tutorialsface.com/2015/08/android-custom-notification-tutorial) I ended up with the controls showing in the notification, but all are unresponsive (other than showing the Toasts the author included).
The controls don't seem to be bound to the music service in any way. I've tried creating a broadcast, calling the service directly from within the notifications, creating a new instance of the service. I've tried so many things I can't even remember all of them. I've seen people ask similar questions but nothing I've tried works. Now I've tried so many things that I've gotten myself confused about when/where I thought I was on a 'right path'. Any ideas as to how to get these controls functioning properly?
Notification Service:
public class NotificationService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(Constant.ACTION.STARTFOREGROUND_ACTION)) {
showNotification();
Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();
} else if (intent.getAction().equals(Constant.ACTION.PREV_ACTION)) {
Toast.makeText(this, "Clicked Previous", Toast.LENGTH_SHORT).show();
Log.i("NS", "Clicked Previous");
} else if (intent.getAction().equals(Constant.ACTION.PLAY_ACTION)) {
Log.i("NS", "Clicked Play");
} else if (intent.getAction().equals(Constant.ACTION.NEXT_ACTION)) {
Toast.makeText(this, "Clicked Next", Toast.LENGTH_SHORT).show();
Log.i("NS", "Clicked Next");
} else if (intent.getAction().equals(
Constant.ACTION.STOPFOREGROUND_ACTION)) {
Log.i("NS", "Received Stop Foreground Intent");
Toast.makeText(this, "Service Stoped", Toast.LENGTH_SHORT).show();
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void showNotification() {
Notification status;
// Using RemoteViews to bind custom layouts into Notification
RemoteViews views = new RemoteViews(getPackageName(),
R.layout.status_bar);
RemoteViews bigViews = new RemoteViews(getPackageName(),
R.layout.status_bar_expanded);
// showing default album image
views.setViewVisibility(R.id.status_bar_icon, View.VISIBLE);
views.setViewVisibility(R.id.status_bar_album_art, View.GONE);
bigViews.setImageViewBitmap(R.id.status_bar_album_art,
Constant.getDefaultAlbumArt(this));
Intent notificationIntent = new Intent(this, NotificationService.class);
notificationIntent.setAction(Constant.ACTION.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Intent previousIntent = new Intent(this, NotificationService.class);
previousIntent.setAction(Constant.ACTION.PREV_ACTION);
PendingIntent ppreviousIntent = PendingIntent.getService(this, 0,
previousIntent, 0);
Intent playIntent = new Intent(this, NotificationService.class);
playIntent.setAction(Constant.ACTION.PLAY_ACTION);
PendingIntent pplayIntent = PendingIntent.getService(this, 0,
playIntent, 0);
Intent nextIntent = new Intent(this, NotificationService.class);
nextIntent.setAction(Constant.ACTION.NEXT_ACTION);
PendingIntent pnextIntent = PendingIntent.getService(this, 0,
nextIntent, 0);
Intent closeIntent = new Intent(this, NotificationService.class);
closeIntent.setAction(Constant.ACTION.STOPFOREGROUND_ACTION);
PendingIntent pcloseIntent = PendingIntent.getService(this, 0,
closeIntent, 0);
views.setOnClickPendingIntent(R.id.status_bar_play, pplayIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_play, pplayIntent);
views.setOnClickPendingIntent(R.id.status_bar_next, pnextIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_next, pnextIntent);
views.setOnClickPendingIntent(R.id.status_bar_prev, ppreviousIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_prev, ppreviousIntent);
views.setOnClickPendingIntent(R.id.status_bar_collapse, pcloseIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_collapse, pcloseIntent);
views.setImageViewResource(R.id.status_bar_play,
R.drawable.apollo_holo_dark_pause);
bigViews.setImageViewResource(R.id.status_bar_play,
R.drawable.apollo_holo_dark_pause);
views.setTextViewText(R.id.status_bar_track_name, "Song Title");
bigViews.setTextViewText(R.id.status_bar_track_name, "Song Title");
views.setTextViewText(R.id.status_bar_artist_name, "Artist Name");
bigViews.setTextViewText(R.id.status_bar_artist_name, "Artist Name");
bigViews.setTextViewText(R.id.status_bar_album_name, "Album Name");
status = new Notification.Builder(this).build();
status.contentView = views;
status.bigContentView = bigViews;
status.flags = Notification.FLAG_ONGOING_EVENT;
status.icon = R.drawable.ic_launcher;
status.contentIntent = pendingIntent;
startForeground(Constant.NOTIFICATION_ID.FOREGROUND_SERVICE, status);
}
}
MusicService:
public class MusicService extends Service implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
//media player
private MediaPlayer player;
//song list
private ArrayList<Song> songs;
//current position
private int songPosn;
private final IBinder musicBind = new MusicBinder();
private String songTitle="";
private static final int NOTIFY_ID=1;
private boolean shuffle=false;
private Random rand;
public void onCreate(){
//create the service
super.onCreate();
songPosn=0;
player = new MediaPlayer();
initMusicPlayer();
rand=new Random();
}
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 class MusicBinder extends Binder {
MusicService getService() {
return MusicService.this;
}
}
public void setShuffle(){
if(shuffle) shuffle=false;
else shuffle=true;
}
public void playSong(){
player.reset();
//get song
Song playSong = songs.get(songPosn);
//get id
long currSong = playSong.getID();
//set uri
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();
songTitle=playSong.getTitle();
}
public void setSong(int songIndex){
songPosn=songIndex;
}
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();
}
//skip to next
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();
}
#Override
public void onDestroy() {
stopForeground(true);
}
#Override
public IBinder onBind(Intent intent) {
return musicBind;
}
#Override
public boolean onUnbind(Intent intent){
player.stop();
player.release();
return false;
}
#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;
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
#Override
public void onPrepared(MediaPlayer mp) {
//start playback
mp.start();
Intent onPreparedIntent = new Intent("MEDIA_PLAYER_PREPARED");
LocalBroadcastManager.getInstance(this).sendBroadcast(onPreparedIntent);
Intent notIntent = new Intent(this, MusicPlayer.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.player_play)
.setTicker(songTitle)
.setOngoing(true)
.setContentTitle("Playing")
.setContentText(songTitle);
Notification not = builder.build();
Intent serviceIntent = new Intent(MusicService.this, NotificationService.class);
serviceIntent.setAction(Constant.ACTION.STARTFOREGROUND_ACTION);
startService(serviceIntent);
}
}
Music Player:
public class MusicPlayer extends AppCompatActivity implements MediaPlayerControl {
private ArrayList<Song> songList;
private ListView songView;
public MusicService musicSrv;
private Intent playIntent;
private boolean musicBound=false;
private MusicController controller;
private boolean paused=false, playbackPaused=false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music_player);
songView = (ListView)findViewById(R.id.song_list);
songList = new ArrayList<Song>();
getSongList();
Collections.sort(songList, new Comparator<Song>(){
public int compare(Song a, Song b){
return a.getArtist().compareTo(b.getArtist());
}
});
songAdapter songAdt = new songAdapter(this, songList);
songView.setAdapter(songAdt);
setController();
FloatingActionButton fabRandom = (FloatingActionButton)findViewById(R.id.fabRandom);
fabRandom.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
//play next
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 songPicked(View view){
musicSrv.setSong(Integer.parseInt(view.getTag().toString()));
musicSrv.playSong();
if(playbackPaused){
setController();
playbackPaused=false;
}
setController();
controller.show(0);
}
#Override
protected void onPause(){
super.onPause();
paused=true;
}
#Override
protected void onResume(){
super.onResume();
// Set up receiver for media player onPrepared broadcast
LocalBroadcastManager.getInstance(this).registerReceiver(onPrepareReceiver,
new IntentFilter("MEDIA_PLAYER_PREPARED"));
if(paused){
setController();
paused=false;
}
}
#Override
protected void onStop() {
super.onStop();
}
private void setController(){
//set the controller up
if (controller == null) controller = new MusicController(this);
controller.invalidate();
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));
}
#Override
public void onBackPressed() {
moveTaskToBack(true);
Intent intent = new Intent (getApplicationContext(), FitnessActivity.class);
startActivity(intent);
}
// Broadcast receiver to determine when music player has been prepared
private BroadcastReceiver onPrepareReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context c, Intent i) {
// When music player has been prepared, show controller
controller.show(0);
}
};
private BroadcastReceiver onNextClickedReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
musicSrv.playNext();
}
};
//connect to the service
public 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);
setController();
}
}
#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
protected void onDestroy() {
stopService(playIntent);
musicSrv=null;
super.onDestroy();
}
public void getSongList() {
//retrieve song info
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()){
//get columns
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);
int durationColumn = musicCursor.getColumnIndex
(MediaStore.Audio.Media.DURATION);
//add songs to list
do {
long thisId = musicCursor.getLong(idColumn);
String thisTitle = musicCursor.getString(titleColumn);
String thisArtist = musicCursor.getString(artistColumn);
long thisDuration = musicCursor.getLong(durationColumn);
if (thisDuration>10000){
songList.add(new Song(thisId, thisTitle, thisArtist, thisDuration));}
}
while (musicCursor.moveToNext());
}
}
#Override
public void start() {
musicSrv.go();
}
#Override
public void pause() {
playbackPaused=true;
musicSrv.pausePlayer();
}
#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 void seekTo(int pos) {
musicSrv.seek(pos);
}
#Override
public boolean isPlaying() {
if(musicSrv!=null && musicBound)
return musicSrv.isPng();
return false;
}
#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;
}
public void onNextNotification(){
}
}
Manifest:
<activity
android:name=".MusicPlayer"
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MusicService"
/>
<service android:name=".NotificationService" />
Your notification actions gets received by Notifications service, not the Music service. Edit notification intents to target Music Service
Intent playIntent = new Intent(this, MusicService.class);
//... edit all of them
Move onStartCommand() method to Music Service and assign actions
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(Constant.ACTION.PLAY_ACTION)) {
//call play method
}
//...etc
except STARTFOREGROUND_ACTION and STOPFOREGROUND_ACTION, leave it in Notification Service's onStartCommand(). Also insert null check on Intent in both services.
I have a glass app using LiveCards. As such, I don't explicitly have running activities, but just a background service that interacts with the LiveCard for information. At some point, I'd like to bring up a voice input. The problem is that the code samples tell you to use startActivityForResult, which isn't something I can do from within a service. So - is there a different way to bring this up, or can I not do this in my current configuration?
I was having this problem too, not with speech input but I needed to run an activity to get the information to display in a "low-frequency rendering" live card without the user bringing up a menu first. I think you could use an activity to get your text input then send it back to the service.
Most of the information on how to bind the service came from http://developer.android.com/guide/components/bound-services.html
MainService
This is the service that is started by "ok glass, ..." The layout just has a single TextView with the id text.
public class MainService extends Service {
private static final String LIVE_CARD_TAG = "my_card";
private final IBinder mBinder = new LocalBinder();
LiveCard mLiveCard;
TimelineManager mTimelineManager;
RemoteViews mViews;
#Override
public void onCreate() {
super.onCreate();
mTimelineManager = TimelineManager.from(this);
}
public class LocalBinder extends Binder {
MainService getService() {
return MainService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public int onStartCommand(Intent intent, int flags, int startId) {
mLiveCard = mTimelineManager.createLiveCard(LIVE_CARD_TAG);
mViews = new RemoteViews(this.getPackageName(),R.layout.activity_main);
mLiveCard.setViews(mViews);
Intent mIntent = new Intent(this, MenuActivity.class);
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mLiveCard.setAction(PendingIntent.getActivity(this, 0, mIntent, 0));
mLiveCard.publish(LiveCard.PublishMode.REVEAL);
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
// run the test activity after the initial text has displayed
Intent testIntent = new Intent(getBaseContext(), TestActivity.class);
testIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(testIntent);
}
}, 3000);
return START_STICKY;
}
public void updateText(String textString) {
mViews.setTextViewText(R.id.text,textString);
mLiveCard.setViews(mViews);
}
#Override
public void onDestroy() {
if (mLiveCard != null && mLiveCard.isPublished()) {
Log.d("debug", "Unpublishing LiveCard");
mLiveCard.unpublish();
mLiveCard = null;
}
super.onDestroy();
}
}
TestActivity
This is run after a delay from the MainService and updates the text on the live card automatically with no user input.
public class TestActivity extends Activity {
MainService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, MainService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
#Override
public void onResume() {
super.onResume();
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
// this crashes if run right away, so give it a little time
mService.updateText("Updated from TestActivity");
finish();
}
}, 500);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
MenuActivity
This is the activity that is set as the pending intent for the live card. It brings up a menu to exit or update the text when the touchpad is tapped.
public class MenuActivity extends Activity {
MainService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, MainService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
#Override
public void onResume() {
super.onResume();
openOptionsMenu();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.stop:
stopService(new Intent(this, MainService.class));
return true;
case R.id.update:
mService.updateText("Updated from MenuActivity");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public void onOptionsMenuClosed(Menu menu) {
finish();
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}