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 new file mode 100644 index 0000000..d17066e --- /dev/null +++ b/src/main/java/ru/touchin/roboswag/components/utils/audio/HeadsetStateObserver.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * + * This file is part of RoboSwag library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ru.touchin.roboswag.components.utils.audio; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.support.annotation.NonNull; + +import rx.Observable; +import rx.subjects.PublishSubject; + +/** + * Created by Gavriil Sitnikov on 02/11/2015. + * Simple observer of wired headset state (plugged in or not). + */ +public final class HeadsetStateObserver { + + @NonNull + private final AudioManager audioManager; + @NonNull + private final Observable isPluggedInObservable; + + public HeadsetStateObserver(@NonNull final Context context) { + audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + isPluggedInObservable = Observable + .create(subscriber -> { + subscriber.onNext(new IsPluggedInReceiver()); + subscriber.onCompleted(); + }) + .switchMap(isPluggedInReceiver -> Observable + .just(isPluggedIn()) + .concatWith(isPluggedInReceiver.isPluggedInChangedEvent + .doOnSubscribe(() -> context.registerReceiver(isPluggedInReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG))) + .doOnUnsubscribe(() -> context.unregisterReceiver(isPluggedInReceiver)))) + .replay(1) + .refCount(); + } + + /** + * Returns if wired headset plugged in. + * + * @return True if headset is plugged in. + */ + @SuppressWarnings("deprecation") + public boolean isPluggedIn() { + return audioManager.isWiredHeadsetOn(); + } + + /** + * Observes plugged in state of wired headset. + * + * @return Returns observable which will provide current plugged in state and any of it's udpdate. + */ + @NonNull + public Observable observeIsPluggedIn() { + return isPluggedInObservable; + } + + private static class IsPluggedInReceiver extends BroadcastReceiver { + + @NonNull + private final PublishSubject isPluggedInChangedEvent = PublishSubject.create(); + + @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); + } + } + } + + } + +} diff --git a/src/main/java/ru/touchin/roboswag/components/utils/audio/VolumeController.java b/src/main/java/ru/touchin/roboswag/components/utils/audio/VolumeController.java new file mode 100644 index 0000000..7d873f2 --- /dev/null +++ b/src/main/java/ru/touchin/roboswag/components/utils/audio/VolumeController.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * + * This file is part of RoboSwag library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ru.touchin.roboswag.components.utils.audio; + +import android.content.Context; +import android.database.ContentObserver; +import android.media.AudioManager; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; +import android.support.annotation.NonNull; + +import ru.touchin.roboswag.core.log.Lc; +import ru.touchin.roboswag.core.utils.ShouldNotHappenException; +import rx.Observable; +import rx.subjects.PublishSubject; + +/** + * Created by Gavriil Sitnikov on 02/11/2015. + * Simple class to control and observe volume of specific stream type (phone call, music etc.). + */ +public final class VolumeController { + + @NonNull + private final AudioManager audioManager; + private final int maxVolume; + @NonNull + private final Observable volumeObservable; + @NonNull + private final PublishSubject selfVolumeChangedEvent = PublishSubject.create(); + + public VolumeController(@NonNull final Context context) { + this(context, AudioManager.STREAM_MUSIC); + } + + public VolumeController(@NonNull final Context context, final int streamType) { + audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + maxVolume = audioManager.getStreamMaxVolume(streamType); + volumeObservable = Observable + .create(subscriber -> { + subscriber.onNext(new VolumeObserver()); + subscriber.onCompleted(); + }).switchMap(volumeObserver -> Observable + .just(getVolume()) + .concatWith(Observable.merge(selfVolumeChangedEvent, + volumeObserver.systemVolumeChangedEvent + .map(event -> getVolume()) + .doOnSubscribe(() -> context.getContentResolver() + .registerContentObserver(Settings.System.CONTENT_URI, true, volumeObserver)) + .doOnUnsubscribe(() -> context.getContentResolver() + .unregisterContentObserver(volumeObserver))))) + .distinctUntilChanged() + .replay(1) + .refCount(); + } + + /** + * Max volume amount to set. + * + * @return max volume. + */ + public int getMaxVolume() { + return maxVolume; + } + + /** + * Sets volume. + * + * @param volume Volume value to set from 0 to {@link #getMaxVolume()}. + */ + public void setVolume(final int volume) { + if (volume < 0 || volume > maxVolume) { + Lc.assertion(new ShouldNotHappenException("Volume: " + volume + " out of bounds [0," + maxVolume + ']')); + return; + } + if (getVolume() != volume) { + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + selfVolumeChangedEvent.onNext(volume); + } + } + + /** + * Returns volume. + * + * @return Returns volume value from 0 to {@link #getMaxVolume()}. + */ + public int getVolume() { + return audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + } + + /** + * Observes current volume. + * + * @return Observable which will provide current volume and then it's updates. + */ + @NonNull + public Observable observeVolume() { + return volumeObservable; + } + + private static class VolumeObserver extends ContentObserver { + + @NonNull + private final PublishSubject systemVolumeChangedEvent = PublishSubject.create(); + + public VolumeObserver() { + super(new Handler(Looper.getMainLooper())); + } + + @Override + public void onChange(final boolean selfChange) { + super.onChange(selfChange); + systemVolumeChangedEvent.onNext(null); + } + + } + +}