broadcast receiver with multiple audio events - android

I have a receiver that listens for headset MEDIA_PAUSE_PLAY and for AUDIO_BECOMING_NOISY they work fine if only one is called. But some Ford Sync systems will send a play/pause command when turning off the car. So this then has 2 receivers active at the same time and it causes a force close because I am stopping the media player in either situation. I have tried using a boolean but from what I have read the on receive gets killed after each event so the boolean value never gets used. So how can I ignore the audio becoming noisy if the media play pause is received at the same time? Thanks in advance.
Here is my code:
package com.joebutt.mouseworldradio;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.view.KeyEvent;
public class RemoteControlReceiver extends BroadcastReceiver
{
//I created stopCounter to try and keep this from running more than 1 time
int stopCounter = 0;
//I created mediaAction to try and keep both receivers from activating
boolean mediaAction = false;
#Override
public void onReceive(Context context, Intent intent)
{
//boolean mediaAction = false;
//int stopCounter = 0;
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()))
{
mediaAction = true;
//stopCounter = 1;
if (stopCounter < 1)
{
//mediaAction = true; force closes here to
KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE == event.getKeyCode())
{
stopCounter = 1;
//mediaAction only works here if you hit the stop button 1 time, then it will work the next time you shut the car off
mediaAction = true;
//stop and release the media player
if (Play.mp.isPlaying())
{
Play playService = new Play();
playService.stopPlaying();
//stop the play service
Intent stopPlayingService = new Intent(context, Play.class);
context.stopService(stopPlayingService);
//switch back to the main screen
Intent showMain = new Intent(context, MouseWorldRadioActivity.class);
showMain.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(showMain);
}
}
}
}
else if (!mediaAction)
{
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction()))
{
if (Play.mp.isPlaying())
{
//stop and release the mediaplayer
Play playService = new Play();
playService.stopPlaying();
//}
//stop the play service
Intent stopPlayingService = new Intent(context, Play.class);
context.stopService(stopPlayingService);
//switch back to the main screen
Intent showMain = new Intent(context, MouseWorldRadioActivity.class);
showMain.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(showMain);
}
}
}
}
}
Here is my method to stop the playback:
public void stopPlaying()
{
if (mp.isPlaying())
{
//stop playback and release everything
mp.setOnBufferingUpdateListener(null);
mp.setOnErrorListener(null);
mp.stop();
mp.release();
mp = null;
}

It should be okay to have two receivers active at the same time. If the issue is that you are trying to stop the media player when it is already stopped try this in your receiver:
if (mp.isPlaying()) {
mp.stop();
}
That way you only stop the media player if it is playing. If that's not the case, can you post code so we can see exactly what you're trying.

To solve the problem I checked to see if the media player was null for the audio becoming noisy listener. This prevented it from trying to stop a media player that no longer existed. Now it works great with my sync system.

Related

Receive callback on all Android media button events all the time (even when another app is playing audio)

Background Info: I need to detect whenever a user presses the play/pause button found on most headsets (KEYCODE_MEDIA_PLAY_PAUSE).
I have it all mostly working using MediaSessions, but when another app starts playing audio, I stop getting callbacks.
It seems like this is because the app that's playing audio created its own MediaSession and Android sends KeyEvents only to the newest MediaSession. To prevent this I create an OnActiveSessionsChangedListener and create a new MediaSession every time it fires.
This does work, but every time I create a new MediaSession, the listener fires again, so I find myself stuck in an inf loop.
My Question: does anyone know how I can do any of the following??:
Prevent other apps from stealing my media button focus
Detect when I've lost media button focus to another app, so I can create a new MediaSession only then, rather then whenever the active
sessions change
Check if I currently already have media button focus so I needlessly create a new MediaSession
What didn't work:
BroadcastReceiver on
AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION didn't work
because apps have to manually trigger that Broadcast, and many apps,
like NPR One do not
AudioManager.OnAudioFocusChangeListener didn't work because it requires I have
audio focus
BroadcastReceiver with max priority on android.intent.action.MEDIA_BUTTON & calling abortBroadcast(), but when other apps were playing audio, my receiver wasn't triggered. Also, other apps can set max priority as well.
My Code:
mMediaSessionManager.addOnActiveSessionsChangedListener(controllers -> {
boolean updateButtonReceiver = false;
// recreate MediaSession if another app handles media buttons
for (MediaController mediaController : controllers) {
if (!TextUtils.equals(getPackageName(), mediaController.getPackageName())) {
if ((mediaController.getFlags() & (MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)) != 0L) {
updateButtonReceiver = true;
}
}
}
if (updateButtonReceiver) {
// using a handler with a delay of about 2 seconds because this listener fires very often.
mAudioFocusHandler.removeCallbacksAndMessages(null);
mAudioFocusHandler.sendEmptyMessageDelayed(0, AUDIO_FOCUS_DELAY_MS);
}
}, ClickAppNotificationListener.getComponentName(this));
Here is the handler that gets triggered:
private final Handler mAudioFocusHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (mShouldBeEnabled) {
updateButtonReceiverEnabled(true);
}
}
};
And finally here is the method that the Handler triggers:
private void updateButtonReceiverEnabled(boolean shouldBeEnabled) {
// clear old session (not sure if this is necessary)
if (mMediaSession != null) {
mMediaSession.setActive(false);
mMediaSession.setFlags(0);
mMediaSession.setCallback(null);
mMediaSession.release();
mMediaSession = null;
}
mMediaSession = new MediaSessionCompat(this, MEDIA_SESSION_TAG);
mMediaSession.setCallback(mMediaButtonCallback);
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setPlaybackToLocal(AudioManager.STREAM_MUSIC);
mMediaSession.setActive(true);
mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE)
.setState(PlaybackStateCompat.STATE_CONNECTING, 0, 0f)
.build());
if (shouldBeEnabled != mShouldBeEnabled) {
getPackageManager().setComponentEnabledSetting(mMediaButtonComponent,
shouldBeEnabled
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
mShouldBeEnabled = shouldBeEnabled;
}
Thanks!
if you just want to capture MediaButton you can register a BroadcastReceiver to get Media Button action all the time .
MediaButtonIntentReceiver class :
public class MediaButtonIntentReceiver extends BroadcastReceiver {
public MediaButtonIntentReceiver() {
super();
}
#Override
public void onReceive(Context context, Intent intent) {
String intentAction = intent.getAction();
if (!Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
return;
}
KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (event == null) {
return;
}
int action = event.getAction();
if (action == KeyEvent.ACTION_DOWN) {
// do something
Toast.makeText(context, "BUTTON PRESSED!", Toast.LENGTH_SHORT).show();
}
abortBroadcast();
}
}
add this to manifest.xml:
<receiver android:name=".MediaButtonIntentReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
and register your BroadcastReceiver like this ( in main activity)
IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
MediaButtonIntentReceiver r = new MediaButtonIntentReceiver();
filter.setPriority(1000);
registerReceiver(r, filter);
also look at :
How to capture key events from bluetooth headset with android
How do I intercept button presses on the headset in Android?
The controllers you get in OnActiveSessionsChangedListener is ordered by priority. You only have to create a new MediaSession if you see that your MediaSessionis not the first one in the list.
Note that you might still run into an infinite loop if there is another app contending the media key events using the same approach.

Continuous Audio Playback despite Orientation Change

This question has been asked a few times, but none of the answers have helped me solve my problem so I'm posting my version of it.
I'm creating an app that plays a list of songs through a service. The problem is that I got a null exception error from the mediaPlayer after I rotate the device. As you can see below, I first start and then bind to my service in the onResume method. Likewise, I unbind and stop the service in the onDestroy method.
protected void onResume() {
if (playIntent == null) {
playIntent = new Intent(MainActivity.this, MediaService.class);
// if (n < 0) {
startService(playIntent);
bindService(playIntent, mediaConnection, Context.BIND_AUTO_CREATE);
Log.d("Check", "Started Service");
// }
}
super.onResume();
}
In my service, I set up the mediaPlayer in the service's onCreate method as below. I have a few things in the onStartCommand method (like creating a notification to show in the action bar). I also return Start_sticky in onStartCommand method.
Unfortunately, like I said, when I rotate the device, I get a null exception from the mediaPlayer.
Please help; I've been trying to fix this for days.
static private MediaPlayer player;
private final IBinder musicBind = new MediaBinder();
Notification notification;
private int playbackDuration;
static Uri paths[][] = new Uri[MainActivity.NUMBER_OF_ARTISTS][MainActivity.NUMBER_OF_TRACKS];
public void onCreate() {
// create the service
super.onCreate();
player = new MediaPlayer();
player.reset();
player.setLooping(true);
initMediaPlayer();
Log.d("Check", "In Service's OnCreate method");

Check service status after the apps is closed in Android

I am working on a music player
The logic is like this:
First, I click on the play button , if there is music playing, stop the service , otherwise , start it.
Play / Pause Button:
playM.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
AudioManager manager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
if (!manager.isMusicActive()) {
playM.setText("Pause");
Intent svc=new Intent(getContext(), Music.class);
svc.putExtra("uri", tt1.getText().toString());
getContext().startService(svc);
} else {
playM.setText("Play");
//if (mPlayer != null && mPlayer.isPlaying()) {
Intent svc=new Intent(getContext(), Music.class);
getContext().stopService(svc);
//}
}
}
});
Service:
public int onStartCommand(Intent intent, int flags, int startId) {
path = (String) intent.getExtras().get("uri");
Uri uriMusic = Uri.parse(path);
player = MediaPlayer.create(this, uriMusic);
player.start();
player.setLooping(true); // Set looping
return 1;
}
#Override
public void onDestroy() {
player.stop();
player.release();
}
The problem is, if the music is playing by another apps, my music player will crash. But I can not find any way to track the service status. For example, if I play the music, close the app , the music is still playing , but when I open the app again , how to know the specific service (play music which is fire by my app only, in this case) is running? Thanks

How to stop or pause Pandora and Spotify

I have an app that has a feature to launch an app, Pandora station, or shortcut. That all works fine. Later I want to stop the app I started. This works for most things except Pandora and Spotify don't always close. Sometimes they do but not always. It seems to be related to the current UI state. For instance, it works fine when I have Pandora showing or the home screen showing. When Home Dock or Car Mode is active it does not work. You can see all my source code here: http://code.google.com/p/a2dpvolume/
service.java is the file that has this functionality.
Here is the part of that code that tries to stop the music from playing and then stop the app.
if (bt2.hasIntent()) {
// if music is playing, pause it
if (am2.isMusicActive()) {
// first pause the music so it removes the notify icon
Intent i = new Intent("com.android.music.musicservicecommand");
i.putExtra("command", "pause");
sendBroadcast(i);
// for more stubborn players, try this too...
Intent downIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
KeyEvent downEvent2 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP);
downIntent2.putExtra(Intent.EXTRA_KEY_EVENT, downEvent2);
sendOrderedBroadcast(downIntent2, null);
}
// if we opened a package for this device, try to close it now
if (bt2.getPname().length() > 3 && bt2.isAppkill()) {
// also open the home screen to make music app revert to
// background
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
// now we can kill the app is asked to
final String kpackage = bt2.getPname();
CountDownTimer killTimer = new CountDownTimer(6000, 3000) {
#Override
public void onFinish() {
try {
stopApp(kpackage);
} catch (Exception e) {
e.printStackTrace();
Log.e(LOG_TAG, "Error " + e.getMessage());
}
}
#Override
public void onTick(long arg0) {
if (am2.isMusicActive()) {
// for more stubborn players, try this too...
Intent downIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
KeyEvent downEvent2 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP);
downIntent2.putExtra(Intent.EXTRA_KEY_EVENT, downEvent2);
sendOrderedBroadcast(downIntent2, null);
}
try {
stopApp(kpackage);
} catch (Exception e) {
e.printStackTrace();
Log.e(LOG_TAG, "Error " + e.getMessage());
}
}
};
killTimer.start();
}
}
Here is the function stopApp().
protected void stopApp(String packageName) {
Intent mIntent = getPackageManager().getLaunchIntentForPackage(
packageName);
if (mIntent != null) {
try {
ActivityManager act1 = (ActivityManager) this
.getSystemService(ACTIVITY_SERVICE);
// act1.restartPackage(packageName);
act1.killBackgroundProcesses(packageName);
List<ActivityManager.RunningAppProcessInfo> processes;
processes = act1.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo info : processes) {
for (int i = 0; i < info.pkgList.length; i++) {
if (info.pkgList[i].contains(packageName)) {
android.os.Process.killProcess(info.pid);
}
}
}
} catch (ActivityNotFoundException err) {
err.printStackTrace();
Toast t = Toast.makeText(getApplicationContext(),
R.string.app_not_found, Toast.LENGTH_SHORT);
if (notify)
t.show();
}
}
}
Has someone else run into this problem? How can I reliably stop the launched app? I need to first get it to pause and put it in the background. That is the problem I am having. It works for most situations but not all. Some cases Pandora and Spotify will not respond to the key event being sent and they just keep playing. This keeps the notify icon active and makes the app a foreground activity so I can't stop it.
I finally figured out that Pandora does pause music when it sees a headset disconnect. So, I just had to send that disconnect intent so Pandora would pause. Once paused, it was able to be pushed to background and killed.
//Try telling the system the headset just disconnected to stop other players
Intent j = new Intent("android.intent.action.HEADSET_PLUG");
j.putExtra("state", 0);
sendBroadcast(j);
For anyone else trying this; The android.intent.action.HEADSET_PLUG intent is no longer allowed to be broadcast unless you are running as the system.
As the "HEADSET_PLUG" intent is now only supported if called by a system, I found app specific intents to be the way to go:
Intent pauseSpotify = new Intent("com.spotify.mobile.android.ui.widget.PLAY");
pauseSpotify.setPackage("com.spotify.music");
sendBroadcast(pauseSpotify);
Essentially, what this does, is it calls "PLAY" from the spotify app.
I got the idea from an article and applied it to normal android.

Stop a service when OnAudioFocusChangeListener when =AudioManager.AUDIOFOCUS_LOSS

I am having trouble stopping my media player class, that extends service. I can call stop just fine but am getting a force close when trying to use stopService(Intent). I get nullpointer exceptions in the log related to the context. I cannot find any documentation on the web about this. Here is the part of my code that I have tried:
static OnAudioFocusChangeListener audioFocusChangeListener = new OnAudioFocusChangeListener()
{
public void onAudioFocusChange(int focusChange)
{
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT)
{
mp.setVolume(0.01f, 0.01f);
}
else if (focusChange == AudioManager.AUDIOFOCUS_GAIN)
{
mp.setVolume(1.0f, 1.0f);
}
else if (focusChange == AudioManager.AUDIOFOCUS_LOSS)
{
//stop playback and release everything---------------still need to confirm this is working
Play playService = new Play();
playService.stopPlaying();
//playService.stopAllServices();
//Intent stopPlayingService = new Intent("com.joebutt.mouseworldradio.Play");
//playService.getApplicationContext();
//Context context = (Context) OnAudioFocusChangeListener.this;
Intent stopPlayingService = new Intent(playService.getBaseContext(), Play.class);
//stopPlayingService.setAction(Intent.);
//stopService(stopPlayingService);
playService.stopService(stopPlayingService);
}
}
};
Ok so I could not get the correct Context. So what I did was created a BroadcastReceiver and used sendBroadcast() from inside the AudioFocusListener. Then in my BroadcastReceiver onReceive() method I stopped my Play.class with an intent using stopService. Works great now. Maybe this will help someone in the future.

Categories

Resources