I have an Android app that plays a notification ringtone (RingtoneManager.TYPE_NOTIFICATION) when certain events are sent to a BroadcastReceiver.
The code that plays the ringtone basically does:
onReceive(Context context, Intent intent)
{
...
Uri ringtoneUri = someFunctionToLookupAValidNotificationRingtoneUri();
...
Ringtone tone = RingtoneManager.getRingtone(context, uri);
Log.v(TAG, "About to play ringtone");
tone.play();
}
Every so often when this code is run, the ringtone starts playing over and over again infinitely. Sometimes it happens when a large number of events are bunched close together, but it has also happened when only one event came in. The log message (and debugging) verifies that the tone.play() call is happening only once per event, and there isn't an infinite stream of events.
The only way stop the infinite looping is to kill my app.
It's almost as if every so often, Android forgets to flush the sound output buffer and so it keeps looping through playing whatever is inside.
Any ideas how to debug and/or fix this issue?
I had a similar problem. It turned out that when a ringtone is played, it will repeat indefinitely until stopped, whereas when a notification sound is played, it will play only once. So my guess is that the difference in your case lies in whether a ringtone or a notification sound was selected in someFunctionToLookupAValidNotificationRingtoneUri(). As you do not supply the code for someFunctionToLookupAValidNotificationRingtoneUri(), I cannot know what happens there.
Picking a notification sound
If you use a ringtone picker for the user to select a notification sound, this code will start the intent to pick a notification sound as opposed to a ringtone:
private void PickANotificationSound() {
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
// We want a notification sound picked. If we don't add this to the
// intent, a ringtone is picked; this means that when it is played,
// it will keep on playing until it is explicitly stopped. A
// notification sound, however, plays only once.
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,
RingtoneManager.TYPE_NOTIFICATION);
// Start the intent to pick a notification sound. The result will show
// up later when onActivityResult() is called.
startActivityForResult(intent, REQUESTCODE_NOTIFICATION_SOUND);
}
where REQUESTCODE_NOTIFICATION_SOUND is just a local constant with any name and value, identifying the request:
private static final int REQUESTCODE_NOTIFICATION_SOUND = 1;
An onActivityResult() callback function like this will then pick up the notification sound URI and play it:
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == REQUESTCODE_NOTIFICATION_SOUND) {
try {
if (resultCode == RESULT_OK) {
Uri ringtoneUri = data.getParcelableExtra(
RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
if (ringtoneUri != null) {
PlayRingtoneOrNotificationSoundFromUri(ringtoneUri);
}
}
} catch (Exception e) {
e.printStackTrace();
}
} else
super.onActivityResult(requestCode, resultCode, data);
}
private void PlayRingtoneOrNotificationSoundFromUri(Uri ringtoneUri) {
Ringtone ringtone = RingtoneManager.getRingtone(
getApplicationContext(), ringtoneUri);
if (ringtone != null) {
ringtone.play();
}
}
Because we said in the intent that we wanted to pick a notification sound, the resulting sound is a notification sound and is therefore only played once after the call to ringtone.play().
If we had said in the intent that we wanted to pick a ringtone, like this:
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,
RingtoneManager.TYPE_RINGTONE);
the picker would return a ringtone that would play indefinitely after the ringtone.play() call – until stopped by ringtone.stop() or the application was killed.
Two meanings of 'ringtone'
Note that the terminology in the Android API adds to the confusion, as the word "ringtone" is used with two different meanings (cf. the documentation of RingtoneManager):
Any sound meant to catch the user's attention, such as a sound to play repeatedly when the phone rings, a notification sound, or a similar sound. This meaning is used in the name RingtoneManager.
A sound to play repeatedly when the phone rings, as opposed to a notification sound or a similar sound. This meaning is used in the name TYPE_RINGTONE in RingtoneManager.TYPE_RINGTONE.
Related
I'm building an Android app that unmute phone when an Incoming call came in phone.
I use BroadcastReceiver to receive incoming call events. I switch phone from mute mode to Ring Mode when BroadcastReceiver receive incoming call events.
And expect Phone will vibrate and Play ringtone.
But Phone only vibrate, can't play ringtone though phone ring set to max sound.
I found many apps on play store those can unmute before call and play ringtone and vibrate both. Example: One App Link
My code Below:
public void onReceive(Context context, Intent intent) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
if (intent.getAction()!=null && intent.getAction().equals("android.intent.action.PHONE_STATE")){
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)){
audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
audioManager.setStreamVolume(AudioManager.STREAM_RING, maxVolume/2, AudioManager.FLAG_PLAY_SOUND);
}
}
}
I'm working on something very similar. You actually have to play a ringtone after overriding the audio settings. But you only need to do this on Android 6.0+ Get an instance of the ringtone and play it when phone state is ringing else just stop it.
public void onReceive(Context context, Intent intent) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
Ringtone r = RingtoneManager.getRingtone(context, uri);
if (intent.getAction()!=null && intent.getAction().equals("android.intent.action.PHONE_STATE")){
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)){
audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
audioManager.setStreamVolume(AudioManager.STREAM_RING, maxVolume/2, AudioManager.FLAG_PLAY_SOUND);
r.play();
}else {
r.stop();
}
}
}
Problems you'll run into with this implementation. You will get multiple ringtones playing because the onReceive method is called many times during Phone State changes. The provides a different context each time onReceive is called and creates a different instance of Ringtone each time. I launched a background service to fix this issue so I could hold a single reference to a context and thus a single reference to Ringtone.
Some devices may not have volume control and may operate at a fixed volume, and may not enable muting or changing the volume of audio streams.
isVolumeFixed()
This method will return true on such devices.
I have a music player which shows a media notification while audio is playing. I want the user to be able to dismiss the notification when music is paused.
I have to use startForeground so that the service will continue running AND it will stay attached to the activity. I've tried using a notification channel, once I kill the activity playback stops as well which isn't what I want (the code is still in there, commented out).
When the notification is shown, I call startForeground. setOngoing is set to playbackState == STATE_PLAYING. I call stopForeground when the service is destroyed or when playbackState == null || STOPPED.
Pastebin: https://pastebin.com/FNTSSzjS
Code snippet (because you need to include code with pastebin)
private void addPlayPauseAction(NotificationCompat.Builder builder) {
String label;
int icon;
PendingIntent intent;
if (playbackState.getState() == PlaybackStateCompat.STATE_PLAYING) {
label = service.getString(R.string.play_pause);
icon = android.R.drawable.ic_media_pause;
intent = intentPause;
builder.setOngoing(true);
} else {
label = service.getString(R.string.play_pause);
icon = android.R.drawable.ic_media_play;
intent = intentPlay;
builder.setOngoing(false);
}
builder.addAction(new NotificationCompat.Action(icon, label, intent));
}
relevant methods in PasteBin are startNotification, stopNotification, createNotification, addPlayPauseAction, and setNotificationPlaybackState
Use stopForeground(false) and rebuild notification. You don't have to use setOngoing while using foreground service. For more information refer to this page
and search for Running a service in the foreground.
On onReceive method when i get a new message, after i do some checkups and see that the address number is the one i want.
Is it possible to play my notification sound instead of the default message sound? And if a normal message comes then the default sound will play.
The thing is that the phone plays the sound first, and then my application, so there is a conflict there.
Ive searched for an answer but i couldnt find anything.
What i do is kind of unefficient. I play my sound 6 seconds after the default notifications plays like this:
private Runnable task = new Runnable() {
public void run() {
Uri alarmSound = RingtoneManager
.getDefaultUri(RingtoneManager.TYPE_ALARM);
Ringtone r = RingtoneManager.getRingtone(
getApplicationContext(), alarmSound);
r.play();
}
};
private void delayNotification() {
Handler handler = new Handler();
int millisDelay = 6000;
handler.postDelayed(task, millisDelay);
}
I know its not a good way to do it so thats y i came here:P
notification.sound =Uri.parse("android.resource://"+context.getPackageName()+"/"
+R.raw.tone);
So I'm working on a simple music player. The music player as it names states, can play a song, pause playback, forward to next song, go back to previous song, and stop playback completely. When you play a song, there will be a notification displayed with the Artist Name, and the Song Name; this notification also has three buttons (Actions): Stop, Pause an Next.
What I'm having issues with is making sure that when either action is clicked, the playback control related to the Action is triggered, and well, I have absolutely no idea on how to do that. I searched the Android Notification: http://developer.android.com/guide/topics/ui/notifiers/notifications.html but It does not clarifies, or dedicates too much info on Notification Actions.
Here is a simple action for instance (which should be associated with the "Next" button click on the notification: Note: All of the code described below is written under the following package: com.deadpixels.light.player and the Activity is called: PlayerActivity
public void nextSong() {
if (position == songs.size() -1) {
Toast.makeText(this, "No more songs on queue", Toast.LENGTH_SHORT).show();
if(mPlayer.isPlaying()) {
return;
}
else {
buttonPlay.setImageResource(R.drawable.button_play);
}
return;
}
else {
position++;
playSong(songs.get(position));
}
}
Here is what I tried to do:
Intent nextIntent = new Intent(KEY_NEXT);
PendingIntent nextPendingIntent = createPendingResult(0, nextIntent, 0);
Where the action for the NextIntent is:
public static final String KEY_NEXT = "com.deadpixels.light.player.PlayerActivity.nextSong";
and then I add that to the Notification via addAction():
mBuilder.addAction(R.drawable.not_action_next, "Next", nextPendingIntent);
Do you guys know what else I could try? Using what I explained above does nothing at all, the notification shows up, and has three action buttons, but clicking on them does nothing for me.
At one point I thought maybe if I added the intent filters with the action names, but then I thought, well, they are all on the same namespace, why should I?
I've solved this problem using broadcasts. For example, to send a broadcast that advances to the next song, you can use the following:
Intent nextIntent = new Intent(KEY_NEXT);
PendingIntent nextPendingIntent = PendingIntent.getBroadcast(this, 0, nextIntent, 0);
Then, register a BroadcastReceiver within your Activity:
IntentFilter filter = new IntentFilter();
filter.addAction(KEY_NEXT);
// Add other actions as needed
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(KEY_NEXT)) {
nextSong();
} else if (...) {
...
}
...
}
}
registerReceiver(receiver, filter);
If you're using a service to manage background playback, then you'll want to register the BroadcastReceiver in the service instead of the activity. Be sure to store the receiver instance somewhere so that you can unregister it when the activity or service is shut down.
I have this method in my main activity
private void beep()
{
AudioManager manager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
manager.setStreamVolume(AudioManager.STREAM_NOTIFICATION, 0,
AudioManager.FLAG_SHOW_UI + AudioManager.FLAG_PLAY_SOUND);
Uri notification = RingtoneManager
.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(),
notification);
r.play();
}
As I understand, notification sound volume should be regulated by STREAM_NOTIFICATION. But notification always plays with the same volume despite that volume number in setStreamVolume method. Why is that?
I went another way. It's not exactly answer to my question but appropriate one. When I play notification in STREAM_MUSIC everything is fine. So notification plays exactly with volume I pass as parameter to the function
private void beep(int volume)
{
AudioManager manager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
manager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
Uri notification = RingtoneManager
.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
MediaPlayer player = MediaPlayer.create(getApplicationContext(), notification);
player.start();
}
First of all, I hope you realize that you are attempting to play two notifications right after each other, so there might be a conflict about that. AudioManager.FLAG_PLAY_SOUND and r.play() will both try playing the sound. It is usually enough to give user one type of information, either by UI or by playing the beep with new volume. I suggest you delete one of the flags. If you don't need any, just give it 0.
Coming to the main question, I am not sure if you can set volume level to 0. Try putting 1, like
manager.setStreamVolume(AudioManager.STREAM_NOTIFICATION, 1, AudioManager.FLAG_SHOW_UI);
If you want to mute the notification, try using setStreamMute, which is equivalent to setting the volume to 0.
For my case eventually this seemed to be working.
It would make your audio volume relative to the stream current volume but it was useful for my need.
MediaPlayer player = MediaPlayer.create(getApplicationContext(), notificationUri);
player.setVolume(toneLevel, toneLevel);
toneLevel is between 0.0 - 1.0 so you don't even need to find the range of your stream..
Write a function as follow, or copy and paste the following,
public void ringtone() {
try {
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
r.play();
} catch (Exception e) {}
}
and call ringtone() when you want to make a beep notification.