Intent.ACTION_HEADSET_PLUG is received when activity starts - android

I am trying to pause music that is playing when the headset is unplugged.
I have created a BroadcastReceiver that listens for ACTION_HEADSET_PLUG intents and acts upon them when the state extra is 0 (for unplugged). My problem is that an ACTION_HEADSET_PLUG intent is received by my BroadcastReceiver whenever the activity is started. This is not the behavior that I would expect. I would expect the Intent to be fired only when the headset is plugged in or unplugged.
Is there a reason that the ACTION_HEADSET_PLUG Intent is caught immediately after registering a receiver with that IntentFilter? Is there a clear way that I can work with this issue?
I would assume that since the default music player implements similar functionality when the headset is unplugged that it would be possible.
What am I missing?
This is the registration code
registerReceiver(new HeadsetConnectionReceiver(),
new IntentFilter(Intent.ACTION_HEADSET_PLUG));
This is the definition of HeadsetConnectionReceiver
public class HeadsetConnectionReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.w(TAG, "ACTION_HEADSET_PLUG Intent received");
}
}

Thanks for the reply Jake. I should have updated the original post to indicate that I discovered the issue that I was having. After a bit of research, I discovered that the ACTION_HEADSET_PLUG Intent is broadcast using the sendStickyBroadcast method in Context.
Sticky Intents are held by the system after being broadcast. That Intent will be caught whenever a new BroadcastReceiver is registered to receive it. It is triggered immediately after registration containing the last updated value. In the case of the headset, this is useful to be able to determine that the headset is already plugged in when you first register your receiver.
This is the code that I used to receive the ACTION_HEADSET_PLUG Intent:
private boolean headsetConnected = false;
public void onReceive(Context context, Intent intent) {
if (intent.hasExtra("state")){
if (headsetConnected && intent.getIntExtra("state", 0) == 0){
headsetConnected = false;
if (isPlaying()){
stopStreaming();
}
} else if (!headsetConnected && intent.getIntExtra("state", 0) == 1){
headsetConnected = true;
}
}
}

I use a different approach to stop playback when headset is unplug. I do not want you to use it since you are already fine, but some other people may find it useful. If you get control of audio focus, then Android will send you an event audio becoming noisy, so if you write a receiver for this event it will look like
public void onReceive(Context context, Intent intent) {
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
if (isPlaying()){
stopStreaming();
}
}
}

I ran into the same issue. I'm not sure what causes it, but at least in my testing it seems to be consistent, which means you can work around it. I did just that by adding a boolean member variable that starts as true, and is set to false on the first onReceive(Context, Intent) call. This flag then controls whether I actually process the unplug event or not.
For your reference, here is the code I use to do just that, which is available in context here.
private boolean isFirst;
public void onReceive(Context context, Intent intent)
{
if(!isFirst)
{
// Do stuff...
}
else
{
Log.d("Hearing Saver", "First run receieved.");
isFirst = false;
}
}

Related

Simplest/shortest/cheapest way to send a signal from service to activity

I have a music player with a MusicService that runs the player and notification, and a Player activity for UI. I want the Player to react to very simple signals sent by the MusicService, like "song ended", or react to notification button presses.
The way I've done this is via Broadcasts in the service:
mplayer = MediaPlayer.create(c, Uri.parse(filename));
mplayer.setOnCompletionListener(mp -> {
sendBroadcast(new Intent("MyXVFPlayer-SongFinished"));
});
and then in Player:
private class DataUpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null) {
if (action.equals("MyXVFPlayer-SongFinished")) {
nextsong();
}
...
}
}
}
This works perfectly, but then I've read that Broadcasts are overkill for this. Better a LocalBroadcast. But now it is deprecated. So LiveData. But maybe a Messenger. Or an EventBus. Or a service as a singleton. Or...??
So I am very confused now. I have no idea which is the cheapest (less resource intensive) way. In September 2021, that is...
Any advice?
There is one more way that is not deprecated: SharedPreferences. You may listen for changes using registerOnSharedPreferenceChangeListener

Detect headphones Android

I program in recent years to Android and I wonder something:
How to detect the presence of headphones?
There is a method: isWiredHeadsetOn() but it doesn't work.
I've tried that but it doesn't work:
AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
Log.i("am.isWiredHeadsetOn()", am.isWiredHeadsetOn() + "");
if (am.isWiredHeadsetOn()) {
//code
}
Thank you (and sorry if I made spelling mistakes, I am French)
#Phil answer at https://stackoverflow.com/a/19102661/4758255 is a good implementation to detect the headphone.
Here I'm quoting from his answer. Please upvoted his answer not this!
Upvote the answer: https://stackoverflow.com/a/19102661/4758255
I've had an app in the store for three years that monitors both the wired headset and bluetooth state and nobody has ever complained about battery drain. But that is because I am successfully using a single service and broadcast receiver for detecting events from both. Here's the two key classes:
public class HeadsetStateBroadcastReceiver extends BroadcastReceiver {
public static final String[] HEADPHONE_ACTIONS = {
Intent.ACTION_HEADSET_PLUG,
"android.bluetooth.headset.action.STATE_CHANGED",
"android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"
};
#Override
public void onReceive(final Context context, final Intent intent) {
boolean broadcast = false;
// Wired headset monitoring
if (intent.getAction().equals(HEADPHONE_ACTIONS[0]) {
final int state = intent.getIntExtra("state", 0);
AudioPreferences.setWiredHeadphoneState(context, state > 0);
broadcast = true;
}
// Bluetooth monitoring
// Works up to and including Honeycomb
if (intent.getAction().equals(HEADPHONE_ACTIONS[1])) {
int state = intent.getIntExtra("android.bluetooth.headset.extra.STATE", 0);
AudioPreferences.setBluetoothHeadsetState(context, state == 2);
broadcast = true;
}
// Works for Ice Cream Sandwich
if (intent.getAction().equals(HEADPHONE_ACTIONS[2])) {
int state = intent.getIntExtra("android.bluetooth.profile.extra.STATE", 0);
AudioPreferences.setBluetoothHeadsetState(context, state == 2);
broadcast = true;
}
// Used to inform interested activities that the headset state has changed
if (broadcast) {
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("headsetStateChange"));
}
}
}
Here is the service I use to register the broadcast receiver:
public class HeadsetMonitoringService extends Service {
HeadsetStateBroadcastReceiver headsetStateReceiver;
#Override
public void onCreate() {
headsetStateReceiver = new HeadsetStateBroadcastReceiver();
final IntentFilter filter = new IntentFilter();
for (String action: HeadsetStateBroadcastReceiver.HEADPHONE_ACTIONS) {
filter.addAction(action);
}
registerReceiver(headsetStateReceiver, filter);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
return START_STICKY;
}
#Override
public void onDestroy() {
unregisterReceiver(headsetStateReceiver);
}
#Override
public IBinder onBind(final Intent intent) {
return null;
}
}
And here is my manifest entry:
<service
android:name=".services.HeadsetMonitoringService"
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name="initialiseHeadsetService" />
</intent-filter>
</service>
How it works is as follows:
I use an on boot broadcast receiver to send a start service message to the HeadsetMonitoringService (you don't have to do it this way, you could just do this when your application starts instead). The HeadsetMonitoringService in turn registers an instance of a broadcast listener that listens to all the headset events I am interested in - they are held in the HEADPHONE_ACTIONS array. Because the service is sticky it hangs around - and therefore so does the broadcast listener. But because both the service and the broadcast listener are event driven they do not consume any power until a headset state change occurs. Additionally, because the service is sticky, it will be restarted by the OS if it dies unexpectedly.
Whenever I receive a headset state change event I also fire a local broadcast so that interested activities can check the new state and take action if required.
For completeness, I should point out that I use another class (not shown here), AudioPreferences, to store as preferences both the Bluetooth and wired headset state, which can then be accessed whenever I need to know the headset state.
Your application will need the android.permission.BLUETOOTH permission if you are interested in the state of a Bluetooth headset. If not, just take out the Bluetooth related actions from the HEADPHONE_ACTIONS array and delete the associated if blocks from the onReceive method.
If you are OK with Marshmallow and up the AudioDeviceCallback might be what you are looking for. It works with an AudioManager and tells you when anything connects and disconnects.
AudioManager.isWiredHeadsetOn() appeared to be the right thing to do. According to the Android developer doc :
Checks whether a wired headset is connected or not.
This is not a valid indication that audio playback is actually over the wired headset as audio routing depends on other conditions.
Returns
true if a wired headset is connected. false if otherwise
But :
you have to add the associated permission to your manifest (MODIFY_AUDIO_SETTINGS)
according to this post, it doesn't work well with Bluetooth headset.

Way to ensure delivery of Action HEADSET_PLUG for ZTE T815 Android phone

I have registered a BroadcastReceiver to receive ACTION_HEADSET_PLUG which works fine for most devices, ie it is called whenever the headset is plugged or unplugged. But on others eg the ZTE T815, the Intent is never sent/received when the headset is plugged/unplugged.
For reference here is the code for the receiver registration:
private final BroadcastReceiver headsetPlugReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent=" + intent);
if (intent.getAction().equalsIgnoreCase(Intent.ACTION_HEADSET_PLUG)) {
// do stuff
}
}
};
public void onCreate(Bundle savedState) {
super.onCeate(savedState);
// ...
registerReceiver(headsetPlugReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
}
Further info:
The Intent is dispatched but only after the HEADSET_HOOK command is fired on the headset.
And when the Intent is dispatched
final int microphone = intent.getIntExtra("microphone", 0);
always returns 0 (ie no microphone).
So
Is there some config/code that can force the delivery of this
Intent?
How do I get the Intent to correctly report whether a
microphone exists or not?
It turns out the ZTE T815 has an OMTP TRRS config for its audio socket instead of CTIA/AHJ like every other modern Android device.
See http://en.wikipedia.org/wiki/Phone_connector_%28audio%29
A sad state of affairs, especially when trying to use audio feed inpout across products.

As I can register an intent to listen when the NFC switch to on / off?

I am making a widget that lets go NFC settings and activate it, now I just need to know how to register an intent in the NFC onReceive whether a change on / off.
Any help would be highly appreciated.
#Override
public void onReceive(Context context, Intent intent)
{
manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
adapter = manager.getDefaultAdapter();
//OR
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()))
{
actualizar(context);
}
//OR
if (intent.getAction().equals("ADAPTER_STATE_CHANGE"))
{
actualizar(context);
}
}
You can detect changes to NFC settings once your app is created and resumed. See this example.
There isn't such a broadcast event. Try reading the logs, this would work but is very unreliable and fragmented. Alternatively you can check the state of things using NfcAdapter.isEnabled() call at specific points in your app.

How to stop service when ACTION_SCREEN_OFF

I am trying to make my UpdateService for my digital clock widget stop when the screen is turned off to conserve battery, and then back on when the screen is activated. I currently have it in my onReceive() in my AppWidgetProvider, but I have also tried it in a BroadcastReciever.
My current code is:
public static boolean wasScreenOn = true;
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Log.d("Screen switched on. ", "Starting DigiClock UpdateService.");
context.startService(new Intent(UpdateService2by2.ACTION_UPDATE));
wasScreenOn = false;
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Log.d("Screen switched off. ", "Stopping DigiClock UpdateService.");
context.stopService(new Intent(context, UpdateService2by2.class));
wasScreenOn = true;
}
Can anyone help me out here? I am stumped.
I'm fairly sure that you have to register your receiver in code for ACTION_SCREEN_OFF/ON. I don't think registering them in the manifest will work.
It seems you cannot register for the ACTION_SCREEN_ON/OFF intents with a filter in the manifest. You have to register your BroadcastReceiver in code. See here for an examples for an activity and a service.
You'll only receive the intent if your service or activity is running. In contrast to other broadcast events, the system will not start your process to handle the SCREEN_ON intent. It is similar to ACTION_BATTERY_CHANGED in this regard.
To handle this intent with a widget, I think you have to start a service that listens for the intent and then notifies your widget.
The article linked from Jens' answer to this same question provides a great presentation on this topic. I used it to implement a solution that worked for me.
The key insight is that your BroadcastReceiver can only be registered in code; you neither want nor need an entry in your manifest.
I recommend the article, but for those in a hurry, the simplest possible functioning approach would be to just paste something like this into your activity's onCreate() method:
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver screenoffReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Log.v("screenoffReceiver", "SCREEN OFF");
}
else if(intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Log.v("screenoffReceiver", "SCREEN ON");
}
return;
}
};
registerReceiver(screenoffReceiver, filter);
As the article points out, for a running activity, onPause() / onResume() are always called when the power button is used to blank/unblank the display, so if you have something that you don't mind doing even in those cases for which the power button was not the reason for the onPause() or onResume() call, you can do it in those methods and thereby catch every instance of the power button being used to blank or unblank the screen (along with calls due to other causes), without the overhead of creating a BroadcastReceiver.
That was the case for me; I had a button that made my activity's main view invisible when pressed and visible when released, and if the user held the button down and then tapped the power button while the app was hidden, it would stay hidden permanently when the screen was unblanked by another tap of the power button. So in my case I only had to put a setVisibility(VISIBLE) call for my main view in my activity's onResume() override.
In fact, as the article shows, even if you only want to respond to the power button events, the proper way to do that is to set a flag in the BroadcastReceiver and then test that flag in onPause() and/or onResume() to see if the power button was the specific cause of the call to those methods.
Maybe you should use
public class MyReceiver extends BroadcastReceiver {..}
And then use this classname in the manifest

Categories

Resources