15

I have an application that targets API Level 19+. I need to check if headset is connected (either of wired or bluetooth).

I see that AudioManager.isWiredHeadsetOn() is deprecated starting from API Level 16 and the documentation offers to use AudioManager.getDevices() which was introduced only starting from API Level 23.

Question: What is the proper way to check if headset is connected for API levels 16 - 22?

Note: I know that I can still use the deprecated method, but I don't want to. If they deprecated it they should have introduced a replacement API (which I though can't find).

Sasha Shpota
  • 9,436
  • 14
  • 75
  • 148
  • A possible workaround might be to use the `NotificationListenerService.getActiveNotifications`, since connecting headset produces a notification, but this is available only from API 18. – TDG Sep 23 '18 at 11:15
  • 1
    For properly listening to wired headset plugged in/out broadcasts, please see https://stackoverflow.com/questions/14708636 – ozbek Sep 26 '18 at 16:03

2 Answers2

3

You need to listen for ACTION_HEADSET_PLUG which fires on headset events and also apparently fires periodically even when nothing changes.

From my existing code I have this in onCreate of my main service:

    // listen for headphone events so we can auto switch the audio output when headphones are plugged in
    myHeadphoneMonitor = new HeadphoneMonitor();
    android.content.IntentFilter headphone_filter = new 
    android.content.IntentFilter(Intent.ACTION_HEADSET_PLUG);
    registerReceiver(myHeadphoneMonitor, headphone_filter);

And my monitor looks like this:

  package com.myname.foo;

  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import android.util.Log;
  import android.os.Message;


  public class HeadphoneMonitor extends BroadcastReceiver
  {
    public boolean headphonesActive=false;

    @Override
    public void onReceive(final Context context, final Intent intent)
    {
        if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG))
        {
            int state = intent.getIntExtra("state", -1);
            switch (state) {
                    case 0:
                        Log.d("HeadphoneMonitor", "Headset is unplugged");
                        headphonesActive=false;
                        break;
                    case 1:
                        Log.d("HeadphoneMonitor", "Headset is plugged in");
                        headphonesActive=true;
                        break;
                    default:
                        Log.d("HeadphoneMonitor", "I have no idea what the headset state is");
                        break;
            }

            // push this event onto the queue to be processed by the Handler
            Message msg = MyApp.uiHandler.obtainMessage(MyApp.HEADPHONE_EVENT);
            MyApp.uiHandler.sendMessage(msg);
        }
    }
  }

Nothing is needed in the manifest to listen for this broadcast event.

spartygw
  • 3,289
  • 2
  • 27
  • 51
  • How do I know wether it is connected or not on application start? The receiver will be initialized with `headphonesActive = false` which might not be correct if headset is connected. Am I missing something? – Sasha Shpota Sep 26 '18 at 14:59
  • 2
    @OleksandrShpota it's an event sent by the system with a _sticky_ broadcast so that means as soon as you register for the intent it will fire the event so you'll get the current state. – spartygw Sep 26 '18 at 16:05
  • This will not handle Bluetooth headphone connections. – ozbek Sep 30 '18 at 19:29
1

Since AndroidStudio warns that AudioManager.getDevices requires VersionCode M the best way is making version check like this:

    // ... some code
    boolean isWiredHeadsetOn=false;
    AudioManager mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    Log.d(TAG, "onCreate:::: retrieved AudioManager instance");
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        Log.d(TAG, "onCreate:::: BuildVersion>=M");
        AudioDeviceInfo[] mAudioDeviceInfos = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
        Log.d(TAG, "onCreate:::: got AudioDeviceInfo[]");
        for (int i = 0; i < mAudioDeviceInfos.length; i++) {
            if (mAudioDeviceInfos[i].getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
                Log.d(TAG, "onCreate:::: \n\nfind wiredHeadset!!!\n\n");
                isWiredHeadsetOn=true;
            } else {
                Log.d(TAG, "onCreate:::: find device type: " + mAudioDeviceInfos[i].getType() + ", id: " + mAudioDeviceInfos[i].getProductName());
            }
        }
    } else {
        Log.d(TAG, "onCreate:::: BuildVersion<M");
        isWiredHeadsetOn=mAudioManager.isWiredHeadsetOn();
    }
    // ... continue code

Hope I could help. Best regards, Cs

Csongi77
  • 329
  • 3
  • 13
  • Please read the question more accurately. You posted pretty the same what I'm actually doing right now. But as I mentioned `AudioManager.isWiredHeadsetOn()` **is deprecated and I don't want to use it**. And since it was deprecated starting from API 16 and the new stuff was introduced only in API 23 there must be something in-between and that's what I'm looking for. – Sasha Shpota Sep 01 '18 at 12:02
  • Checked API level diff logs at https://developer.android.com/guide/topics/manifest/uses-sdk-element#ApiLevels. As you said AudioManager.isWiredHeadsetOn() was deprecated at API level 14 without any suggestion of new method. I didn't find any better method... :( – Csongi77 Sep 01 '18 at 15:47