I'm developing a spam call filtering app and I'm trying to silence ringer for incoming (spam) call. The problem is none of AudioManager.setStreamMute nor AudioManager.setRingerMode is working in Lollipop. Here is my code snippet:
public class CallReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
if (stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
audioManager.setStreamMute(AudioManager.STREAM_RING, false);
Log.i(TAG, "unmute");
} else if (stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
audioManager.setStreamMute(AudioManager.STREAM_RING, true);
Log.i(TAG, "mute");
}
}
When there's an incoming call, the mute part always gets executed but it sometimes succeeds and sometimes fails to mute the ringer. I can't find any rule. And audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT) doesn't work either. This seems work fine when tested on emulators < 5, so I guess it's somehow related to Lollipop not having silent mode but interruptions filter. Commercial spam call filters are woking fine, so can somebody let me know how I could silence incoming calls with Lollipop?
I've got the same issue for android 5. AudioManager.setStreamMute with the value false to unmute is never working.
I tried AudioManager.setStreamSolo and it worked for me.
//to mute ringtone
Global.app().getAudioManager().setStreamSolo(AudioManager.STREAM_MUSIC, true);
//unmute ringtone
Global.app().getAudioManager().setStreamSolo(AudioManager.STREAM_MUSIC, false);
It mutes all other streams except one you want to play. In may case I needed to play my own audio instead of ringtone.
But you can try to play a silence audio to hack if you need absolute silence.
Related
The code for handling media buttons from headsets that I use in my Text-to-Speech app works great under Android API 22 through 25 (in older versions of Android they are handled by other, now depreciated means). However under Android 8 "Oreo", both public beta and final release, it does not work. Here is the relevant code:
When the service starts, I create MediaSessionCompact object:
mSession = new MediaSessionCompat(getApplicationContext(), "my.package.name._player_session");
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setActive(true);
mSession.setCallback(myMediaSessionCallback);
PlaybackStateCompat state = new PlaybackStateCompat.Builder()
.setActions(ACTION_PLAY_PAUSE | ACTION_PLAY | ACTION_PAUSE |
ACTION_SKIP_TO_NEXT | ACTION_SKIP_TO_PREVIOUS |
ACTION_FAST_FORWARD | ACTION_REWIND
)
.setState(PlaybackStateCompat.STATE_PAUSED, 0 /*PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN*/, 1f)
.build();
mSession.setPlaybackState(state);
There is of course session media callback defined:
private MediaSessionCompat.Callback myMediaSessionCallback = new MediaSessionCompat.Callback() {
#Override
public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
// The log output below never appears on "Oreo", nothing comes here.
Log.d(TAG, "callback onMediaButtonEvent() Compat");
MediaButtonIntentReceiver.handleIntent(mediaButtonIntent.getAction(), (KeyEvent) mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT));
return true;
}
#Override
public void onSkipToNext() {
//...
}
// etc. other overrides
};
I also experimented with PendingIntent, using MediaButtonReceiver.buildMediaButtonPendingIntent() and set mSession.setMediaButtonReceiver(pendingIntent) for all the actions I'm interested in, then in my service onStartCommand() I call MediaButtonReceiver.handleIntent(mSession, intent):
// still in the same service:
mSession.setMediaButtonReceiver(
MediaButtonReceiver.buildMediaButtonPendingIntent(
this,
mMediaButtonReceiverComponentName,
ACTION_PLAY));
mSession.setMediaButtonReceiver(
MediaButtonReceiver.buildMediaButtonPendingIntent(
this,
mMediaButtonReceiverComponentName,
ACTION_PAUSE));
mSession.setMediaButtonReceiver(
MediaButtonReceiver.buildMediaButtonPendingIntent(
this,
mMediaButtonReceiverComponentName,
ACTION_PLAY_PAUSE));
and in the service onStartCommand():
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// ...
if (intent != null) {
MediaButtonReceiver.handleIntent(mSession, intent);
// ...
}
return START_NOT_STICKY;
}
Nothing, it's completely dumb to media buttons press events. What's wrong with "O" or my code there??? I'm completely baffled.
Update 8/32/2017
I also created a trivial but working app project that demonstrates the problem, please see: https://github.com/gregko/PlayerServiceSample. This project displays LogCat output when a media button is pressed on a headset under Android 5.x to 7.x, but fails completely under Android 8 "Oreo".
Update 9/1/2017
There is now an open issue on Android Issue Tracker about this, which I submitted, at https://issuetracker.google.com/issues/65175978. Still the media buttons work in several music player apps I tested on Oreo, I just can't figure out what do they do differently to make them work... The context of my app is not playing music, but reading aloud text with Text to Speech service, so a lot of code from Music Player examples does not apply.
Solved. On "Android 8.0 Behavior Changes" Google page we find this text:
In Android 8.0 (API level 26) the handling of media button events is different:
The handling of media buttons in a UI activity has not changed: foreground activities still get priority in handling media button events.
If the foreground activity does not handle the media button event, the system routes the event to the app that most recently played audio locally. The active status, flags, and playback state of a media session are not considered when determining which app receives media button events.
If the app's media session has been released, the system sends the media button event to the app's MediaButtonReceiver if it has one.
For every other case, the system discards the media button event.
All I had to do to make my trivial sample work was to play some sound with MediaPlayer. Apparently playing sound with Text-to-Speech API does not qualify, which in my opinion is a bug.
Here is the code I added to my trivial sample to make it work, playing a very brief and silent WAV file from Raw resources directory:
final MediaPlayer mMediaPlayer;
mMediaPlayer = MediaPlayer.create(this, R.raw.silent_sound);
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mMediaPlayer.release();
}
});
mMediaPlayer.start();
Update
Submitted the bug report to Android issue tracker at https://issuetracker.google.com/issues/65344811
Update 2, Oct. 10, 2017
Google now says that Oreo behavior in this respect is "by design" and won't fix it. Read the reply near the end of the issue tracker post above. I must say I'm disappointed.
I am implementing Quickblox WebRTC calling . All is successfully implemented, but I want the audio call through ear speakers of the phone. By default, it is only on loud speakers.
How can I enable calling through ear speakers like any other calling app? I also tried this:
AudioManager m_amAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
m_amAudioManager.setMode(AudioManager.MODE_IN_CALL);
m_amAudioManager.setSpeakerphoneOn(false);
But still didn't worked for me!
To switch the audio socket you need to call the switchAudioOutput(); method on your current version
it switches between the loudspeakers and phone speakers (if the headset is on, then it switches between the headset and the loudspeakers)
If you are using QuickBlox SDK version 2.5.2, you can set the AudioManager like this:
audioManager = AppRTCAudioManager.create(this, new AppRTCAudioManager.OnAudioManagerStateListener() {
#Override
public void onAudioChangedState(AppRTCAudioManager.AudioDevice audioDevice) {
}
});
audioManager.setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE);
audioManager.setOnWiredHeadsetStateListener(new AppRTCAudioManager.OnWiredHeadsetStateListener() {
#Override
public void onWiredHeadsetStateChanged(boolean plugged, boolean hasMicrophone) {
}
});
audioManager.init();
I have an app with an integrated music player. I don't want the music to be interrupted by incoming calls.
I use following function for that:
public static void updateDoNotDisturbMode(boolean enabled, boolean checkPrefs)
{
...
AudioManager audioManager = ((AudioManager) MainApp.get().getSystemService(Context.AUDIO_SERVICE));
NotificationManager notificationManager = (NotificationManager) MainApp.get().getSystemService(Context.NOTIFICATION_SERVICE);
// audioManager.setMode(AudioManager.MODE_IN_CALL);
if (!enabled)
{
audioManager.setStreamSolo(AudioManager.STREAM_MUSIC, false);
audioManager.setStreamMute(AudioManager.STREAM_VOICE_CALL, false);
audioManager.setStreamMute(AudioManager.STREAM_RING, false);
audioManager.setStreamMute(AudioManager.STREAM_NOTIFICATION, false);
...
}
else
{
audioManager.setStreamSolo(AudioManager.STREAM_MUSIC, true);
audioManager.setStreamMute(AudioManager.STREAM_VOICE_CALL, true);
audioManager.setStreamMute(AudioManager.STREAM_RING, true);
audioManager.setStreamMute(AudioManager.STREAM_NOTIFICATION, true);
...
}
}
what works
silent phone if no headphones are connected
internal music player does play on without interruption if someone calls
so without headphones connected, everything works perfectly fine
what does not work
if headphones are connected, the standard beep sound is interrupting my music player and is played in the headphones
How can I avoid that incoming calls are interrupting my music player even if headphones are connected?
TARGET SDK
Only 4.2 and upwarts... (including 5)
What do you mean integrated music player? Can you change code of music app?
A player stops playing because it listens to PhoneStateChanges - void onCallEvent(int state, String number);
If you can change code of the player you should check is mute mode on within this method - if yes then do not stop playback.
If you can't change the code of player - you can use advanced player with preferences of audio focus. For example poweramp has such option. Set Settings/Audio/Audio Focus/Pause in Call to false (it works, tested right now). But you should change it manually all the time.
I'm writing app that should play sound notification even in silent mode. I don't want to temporarily turn off Silent and roll it back late. I'm using ALARM stream for that now. Usually it works proper, because by default ALARM stream not muted in silent. But sometimes it is.
How can I understand if stream currently muted (in silent mode)?
How can I unmute it (without switching to normal mode)?
Answering my own question:
The solution was found in Android OS sources (in standard Alarm app).
I had found way to check and set option "Alarm in Silent":
private static final int ALARM_STREAM_TYPE_BIT =
1 << AudioManager.STREAM_ALARM;
public static void setAlarmInSilent(boolean on) {
int ringerModeStreamTypes = Settings.System.getInt(
context.getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
if (on) {
ringerModeStreamTypes &= ~ALARM_STREAM_TYPE_BIT;
} else {
ringerModeStreamTypes |= ALARM_STREAM_TYPE_BIT;
}
Settings.System.putInt(context.getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
ringerModeStreamTypes );
}
Link to the Android src: https://android.googlesource.com/platform/packages/apps/AlarmClock/+/donut-release2/src/com/android/alarmclock/SettingsActivity.java
In the documentation, it is said:
The mute command is protected against client process death: if a process with an active mute request on a stream dies, this stream will be unmuted automatically.
The mute requests for a given stream are cumulative: the AudioManager can receive several mute requests from one or more clients and the stream will be unmuted only when the same number of unmute requests are received.
Well, the first paragraph is true; Whenever my process dies, all of the streams I muted are automatically unmuted.
However, no matter how many time I call setStreamMute(someStream, false) it NEVER EVER unmutes.
Last time I tried calling it over 1 million times after muting only ONCE and NOTHING happens!
Just to mention - If i unmute it in the same method I mute it - it stays unmuted. But on the next calls to the same method - it never unmutes.
I am muting in a Broadcast Receiver onReceive method, which I start using an alarm manager. So maybe it because my app was killed during the time between the muting call and the unmuting call? (But my app still stays in the RAM)
Can this problem be because I am not keeping a reference to the AlarmManager (Getting different instances each time?)
Did anyone encounter this problem?
Apperantly, there is a bug in these Android versions; Tested for versions 2.2 and 2.3.3 and the bug exists.
Looks like, if you call setStreamMute on an AudioManager object:
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
am.setStreamMute(...., true);
and you lose your reference, then get a new reference:
am = null;
am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
No matter how many times you call am.setStreamMute(..., false) now, it will never unmutes.
I think ill report this bug now..
Lesson: Keep a static reference to your AudioManager.
#Michell Bak, thanks for giving me the idea to check whether its the Android software bug :) I've been stuck on this thing for way too much time, and I never had the idea to see if its not my fault.
I've never used that API before, but a quick Google search, returned a few results with it not working. It seems to be a bug that's still present in Android 2.3:
http://code.google.com/p/android/issues/detail?id=4235
I solve the problem by putting the audio manager variable in the application
public class myApplication extends Application {
static AudioManager am;
public void onCreate() {
super.onCreate();
am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
this.setMute(false);
}
}
Then in my activity class add this function:
private AudioManager getAM() {
return ((elpApplication)getApplication()).am;
}
and here is how I use the getAM();
private void toogleMediaPlayerMute() {
//defaultVolumn = am.getStreamVolume(AudioManager.STREAM_MUSIC);
elpApplication app = getElpApp();
Log.d("appmute", String.valueOf(app.isMute()));
if (!app.isMute()) {
getAM().setStreamVolume(AudioManager.STREAM_MUSIC, 0,
AudioManager.FLAG_PLAY_SOUND);
getAM().setStreamMute(AudioManager.STREAM_MUSIC, true);
ismute = true;
} else {
int maxVolume = getAM().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
Log.d("maxvol", String.valueOf(maxVolume));
getAM().setStreamMute(AudioManager.STREAM_MUSIC, false);
getAM().setStreamVolume(AudioManager.STREAM_MUSIC, maxVolume,
AudioManager.FLAG_SHOW_UI);
ismute = false;
// app.setMute(ismute);
}
app.setMute(ismute);
}
I have been having the same problem w/ newer versions of the API. So, to work [around] this issue, I've implemented a bit of 'old-school' solution. If your design is such that you handle getting the byte stream / sending the byte stream directly - send a byte array filled with zeros if mute is selected:
*
...
byte[] whatToSend = realByteData;
byte [] mutedOutput = new byte[recBuffSize];
Array.setByte( mutedOutput, 0, (byte) 0);
...
if ( muteSet )
whatToSend = mutedOutput;
amountWritten = audioTrack.write(whatToSend, 0, amountRead);
*