diff --git a/src/main/java/ru/touchin/roboswag/components/utils/audio/HeadsetStateObserver.java b/src/main/java/ru/touchin/roboswag/components/utils/audio/HeadsetStateObserver.java index d17066e..69ebcb1 100644 --- a/src/main/java/ru/touchin/roboswag/components/utils/audio/HeadsetStateObserver.java +++ b/src/main/java/ru/touchin/roboswag/components/utils/audio/HeadsetStateObserver.java @@ -19,6 +19,7 @@ package ru.touchin.roboswag.components.utils.audio; +import android.bluetooth.BluetoothA2dp; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -27,67 +28,93 @@ import android.media.AudioManager; import android.support.annotation.NonNull; import rx.Observable; -import rx.subjects.PublishSubject; +import rx.subjects.BehaviorSubject; /** * Created by Gavriil Sitnikov on 02/11/2015. - * Simple observer of wired headset state (plugged in or not). + * Simple observer of wired or wireless (bluetooth A2DP) headsets state (connected or not). + *
You require android.permission.BLUETOOTH and API level >= 11 if want to observe wireless headset state */ public final class HeadsetStateObserver { @NonNull private final AudioManager audioManager; @NonNull - private final Observable isPluggedInObservable; + private final Observable isConnectedObservable; public HeadsetStateObserver(@NonNull final Context context) { audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - isPluggedInObservable = Observable - .create(subscriber -> { - subscriber.onNext(new IsPluggedInReceiver()); + isConnectedObservable = Observable + .create(subscriber -> { + subscriber.onNext(new IsConnectedReceiver(audioManager)); subscriber.onCompleted(); }) - .switchMap(isPluggedInReceiver -> Observable - .just(isPluggedIn()) - .concatWith(isPluggedInReceiver.isPluggedInChangedEvent - .doOnSubscribe(() -> context.registerReceiver(isPluggedInReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG))) - .doOnUnsubscribe(() -> context.unregisterReceiver(isPluggedInReceiver)))) + .switchMap(isConnectedReceiver -> Observable.combineLatest(isConnectedReceiver.isWiredConnectedChangedEvent, + isConnectedReceiver.isWirelessConnectedChangedEvent, + (isWiredConnected, isWirelessConnected) -> isWiredConnected || isWirelessConnected) + .distinctUntilChanged() + .doOnSubscribe(() -> { + final IntentFilter headsetStateIntentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { + headsetStateIntentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); + } + context.registerReceiver(isConnectedReceiver, headsetStateIntentFilter); + }) + .doOnUnsubscribe(() -> context.unregisterReceiver(isConnectedReceiver))) .replay(1) .refCount(); } /** - * Returns if wired headset plugged in. + * Returns if wired or wireless headset is connected. * - * @return True if headset is plugged in. + * @return True if headset is connected. */ @SuppressWarnings("deprecation") - public boolean isPluggedIn() { - return audioManager.isWiredHeadsetOn(); + public boolean isConnected() { + return audioManager.isWiredHeadsetOn() || audioManager.isBluetoothA2dpOn(); } /** - * Observes plugged in state of wired headset. + * Observes connection state of headset. * - * @return Returns observable which will provide current plugged in state and any of it's udpdate. + * @return Returns observable which will provide current connection state and any of it's udpdate. */ @NonNull - public Observable observeIsPluggedIn() { - return isPluggedInObservable; + public Observable observeIsConnected() { + return isConnectedObservable; } - private static class IsPluggedInReceiver extends BroadcastReceiver { + private static class IsConnectedReceiver extends BroadcastReceiver { @NonNull - private final PublishSubject isPluggedInChangedEvent = PublishSubject.create(); + private final BehaviorSubject isWiredConnectedChangedEvent; + @NonNull + private final BehaviorSubject isWirelessConnectedChangedEvent; + + @SuppressWarnings("deprecation") + public IsConnectedReceiver(@NonNull final AudioManager audioManager) { + isWiredConnectedChangedEvent = BehaviorSubject.create(audioManager.isWiredHeadsetOn()); + isWirelessConnectedChangedEvent = BehaviorSubject.create(audioManager.isBluetoothA2dpOn()); + } @Override public void onReceive(final Context context, final Intent intent) { - if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) { - if (intent.getExtras() == null) { - isPluggedInChangedEvent.onNext(false); - } else { - isPluggedInChangedEvent.onNext(intent.getExtras().getInt("state") != 0); + if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction()) && !isInitialStickyBroadcast()) { + isWiredConnectedChangedEvent.onNext(intent.getIntExtra("state", 0) != 0); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB && + BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { + final int bluetoothState = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_DISCONNECTED); + switch (bluetoothState) { + case BluetoothA2dp.STATE_DISCONNECTED: + isWirelessConnectedChangedEvent.onNext(false); + break; + case BluetoothA2dp.STATE_CONNECTED: + isWirelessConnectedChangedEvent.onNext(true); + break; + default: + break; } } }