Constantly check for volume change in Android services - android

I wrote this piece of code, it's obviously flawed. How do I go about creating a service which will constantly check for changes in volume? Key listeners cannot be used in services, please don't post an answer with volume key listeners.
My code is wrong because I've given dummy condition for the while loop. What has to be the condition for my service to check for volume change and not crash? It can't be isScreenOn() because volume can be changed while listening to music and the screen is off.
CODE
AudioManager audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int currentVolume = audio.getStreamVolume(AudioManager.STREAM_RING);
int newVolume=0;
while(1==1)
{
newVolume = audio.getStreamVolume(AudioManager.STREAM_RING);
if(currentVolume!=newVolume)
{
Toast.makeText(this, "Volume change detected!", Toast.LENGTH_LONG).show();
}
}

If you want to see when the setting changes, you can register a ContentObserver with the settings provider to monitor it. For example, this is the observer in the settings app:
private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
if (mSeekBar != null && mAudioManager != null) {
int volume = mAudioManager.getStreamVolume(mStreamType);
mSeekBar.setProgress(volume);
}
}
};
And it is registered to observer the settings like this:
import android.provider.Settings;
import android.provider.Settings.System;
mContext.getContentResolver().registerContentObserver(
System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
false, mVolumeObserver);

Related

Automatic ducking not working for text-to-speech (Android 8+)

As described in documentation, the automatic ducking feature has been introduced in Android 8.0. Ducking means that if your music application has been interrupted by some short sound (like a notification, for ex.), your application will continue playing music, but music volume will be temporary lowered while short sound is playing.
I use a text-to-speech engine to read long articles, and want it to behave similar to music player, i.e. I want it to be automatically ducked by the system in android 8.0
I've got ducking to work fine without any additional code on Android 8.0 for music playing, but not for text-to-speech playing.
Here is a sample code
public class PlayerService extends Service {
//private MediaPlayer mp;
TextToSpeech tts;
#Override
public void onCreate() {
super.onCreate();
//...
//create foreground notification stuff omitted...
//...
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
am.requestAudioFocus(listener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
/*
mp = MediaPlayer.create(this, R.raw.music);
mp.setLooping(true);
mp.start();
*/
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
tts.setAudioAttributes(
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
);
}
tts.speak("IF you happen to have read another book about Christopher Robin, you may remember that he once had a swan (or the swan had Christopher Robin, I don't know which) and that he used to call this swan Pooh. That was a long time ago, and when we said good-bye, we took the name with us, as we didn't think the swan would want it any more. Well, when Edward Bear said that he would like an exciting name all to himself, Christopher Robin said at once, without stopping to think, that he was Winnie-the-Pooh. And he was. So, as I have explained the Pooh part, I will now explain the rest of it. ",
TextToSpeech.QUEUE_ADD, null);
}
});
}
#Override
public void onDestroy() {
tts.stop();
/*
mp.stop();
mp.release();
*/
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
am.abandonAudioFocus(listener);
super.onDestroy();
}
private AudioManager.OnAudioFocusChangeListener listener = new AudioManager.OnAudioFocusChangeListener() {
#Override
public void onAudioFocusChange(int focusChange) {
//nothing to do here
}
};
}
If I'd remove text-to-speech stuff and uncomment MediaPlayer lines, ducking will work fine for music playing. But if using text-to-speech as presented, no ducking is happening - during the notification sound text-to-speech continues playing as usually, and no volume change is performed.
I want ducking (volume change) to happen also if I use text-to-speech. What am I doing wrong?

Detect when sound is playing through earpiece speaker

Background
I'm developing an app that monitors the proximity sensor. However, I don't want to monitor the sensor while the user is listening to something through the phone ear speaker. This is because the user will probably have his/her head against the handset to listen, which in turn triggers the proximity sensor, and I'm not trying to detect head proximity.
Question
How can I detect when sound is and isn't playing through the handset ear speaker? Callbacks are preferable, of course, but I'm willing to poll if it's the only way.
You can approximate this by regularly polling and making the following checks:
Check if the audio system is not in normal mode.
Check if the audio is being routed to the earpiece.
I tested this in Android 4.3, and it seemed to work fine with the system phone app, Viber and Skype. However, it doesn't seem to detect music or non-telephony sounds played through the earpiece. I don't think this is much of a problem, because the earpiece generally seems to only be used for telephony anyway.
Example
public class EarpieceSpeakerState {
private AudioManager audioManager;
public EarpieceSpeakerState(AudioManager audioManager) {
this.audioManager = audioManager;
}
public boolean usingEarpieceSpeaker() {
return playingSound()
&& routingToEarpiece();
}
private boolean playingSound() {
return audioManager.getMode() != AudioManager.MODE_NORMAL;
}
private boolean routingToEarpiece() {
return !(
audioManager.isSpeakerphoneOn()
|| audioManager.isBluetoothScoOn()
|| audioManager.isBluetoothA2dpOn()
|| audioManager.isWiredHeadsetOn()
);
}
}
I don't know if it is the right solution but i think you should at least give it a try, check out this link.
Teorically you will not receive focus if somebody else is using the speaker of your phone.
base solution, you can extend it link
private fun initOnAudioFocusChangeListener() {
val afChangeListener: AudioManager.OnAudioFocusChangeListener =
AudioManager.OnAudioFocusChangeListener { focusChange ->
when (focusChange) {
AudioManager.AUDIOFOCUS_GAIN -> {
viewModel.isAppLostAudioFocusLiveData.value = false
}
else -> {
viewModel.isAppLostAudioFocusLiveData.value = true
}
}
}
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
audioManager.requestAudioFocus(
AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setOnAudioFocusChangeListener(afChangeListener).build()
)
} else {
audioManager.requestAudioFocus(
afChangeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN
)
}
}

Double logs while getting the volume of System ring using ContentObserver

I am trying to get System ring volume change using the content Observer. Using this volume integer number I would like trigger another java file.
I succeeded using the below code but the problem is I am getting two logs always. I am trying to differentiate the previousVolume and ChangedVolume. I am not why am I getting to logs inside change method? Good thing about this is that the values are not changing?
Here is the code I have tried:
public class VolumeChecker extends ContentObserver
{
int previousVolume;
Context context;
public VolumeChecker(Context c, Handler handler)
{
super(handler);
context=c;
AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
previousVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
}
#Override
public boolean deliverSelfNotifications()
{
return super.deliverSelfNotifications();
}
#Override
public void onChange(boolean selfChange)
{
super.onChange(selfChange);
AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
int currentVolume = audio.getStreamVolume(AudioManager.STREAM_RING);
Log.e("changed", "Volume"+currentVolume);
}
}
And registering the obersever -from service as:
Volume = new VolumeChecker(this,new handler());
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, Volume );
Does anybody know why it is logging twice inside onChange part?

Automatically silence the sound volume of Android Phone programmatically?

I'm developing an application that can turn off the sound of Android Phone automatically. How can I detect the volume of the sound and turn it off programmatically?
if (hour == myTime.getHour() && minute == myTime.getMinute()) {
if (Settings.getSetMyTime(this))
showNotificationAlarm(R.drawable.icon,
"It's time to work");
///so, i want to add the silet function here..help me, please?
}
Thanks in advance.
Have a look at the AudioManager, especially the getStreamVolume and setStreamVolume methods
EDIT
You can also use the method Nikola Despotoski provided with setRingerMode
A Service is a child of a Context so you can call directly getSystemService
See the updated code below (untested):
if (hour == myTime.getHour() && minute == myTime.getMinute()) {
if (Settings.getSetMyTime(this))
showNotificationAlarm(R.drawable.icon,"It's time to work");
AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
am.setRingerMode(AudioManager.RINGER_MODE_SILENT);
}
Register for AUDIO_SERVICE and then use the AudioManager to control the volume up/down or set profiles.
Or if you want to listen for changes in the Audio focus then make your Activity implements AudioManager.OnAudioFocusChangeListener. Override unimplemented method. Create switch that will take care of types of changes.
Like:
#Override
public void onAudioFocusChange(int focusChange) {
switch(focusChange)
{
case AudioManager.AUDIOFOCUS_GAIN:
//do something
break;
case AudioManager.AUDIOFOCUS_LOSS:
break;
}
Or if you want to listen for changes on the audio output, like unplugging the headphones (switching to phone speaker) use ACTION_AUDIO_BECOMING_NOISY sticky broadcast
http://developer.android.com/reference/android/media/AudioManager.html#ACTION_AUDIO_BECOMING_NOISY
http://developer.android.com/reference/android/media/AudioManager.html#RINGER_MODE_SILENT
See here
Edit: This is the solution. There was no need to handle AudioFocus but just set different ringer profile or adjusting volume
if (hour == myTime.getHour() && minute == myTime.getMinute()) {
if (Settings.getSetMyTime(this))
showNotificationAlarm(R.drawable.icon,
"It's time to work");
AudioManager audiomanager =(AudioManager)YourActivityName.this.getSystemService(Context.AUDIO_SERVICE);
audiomanager.setRingerMode(AudioManager.RINGER_MODE_SILENT); //or adjust volume here instead setting silent profile for the ringer
}

Is ToneGenerator sound smooth?

Does anyone have the same problem?
On Android's Phone app, whenever a number is pressed, the tone is always smooth.
But, when I used very similar code as the Phone app, the tone I get regularly isn't smooth...there are gaps. Is there a way to resolve this?
Some of my theories are that the emulator causes these breaks in sound as there are time lags in processing. The Phone app on the emulator is also more "compiled"/native than my code. Etc. Don't know what is the reason for these tones not being continuous.
Here's the code (literally the same as Phone app):
...
playTone(ToneGenerator.TONE_DTMF_1,150);
...
void playTone(int tone) {
// if local tone playback is disabled, just return.
if (!mDTMFToneEnabled) {
return;
}
// Also do nothing if the phone is in silent mode.
// We need to re-check the ringer mode for *every* playTone()
// call, rather than keeping a local flag that's updated in
// onResume(), since it's possible to toggle silent mode without
// leaving the current activity (via the ENDCALL-longpress menu.)
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int ringerMode = audioManager.getRingerMode();
if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
|| (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
return;
}
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
Log.w("test", "playTone: mToneGenerator == null, tone: " + tone);
return;
}
// Start the new tone (will stop any playing tone)
mToneGenerator.startTone(tone, TONE_LENGTH_MS);

Categories

Resources