I have an alarm clock app and some users complain phone vibrates during alarm, while vibrations should be disabled.
After some digging I have found out, that on some devices there's a system option for device to vibrate along alarm music. For example in my test Pixel 4 it is located at Settings->Sound&Vibration->Vibration&Heptics->Alarm vibration.
What this setting, enabled by default, causes, is that vibrations try to "emulate" the music played through MediaPlayer and I cannot find a way to prevent that from happening from within the app or even detect if such setting is present/enabled.
Anyone knows how to get rid of that?
Here's a sample method I used for testing:
private fun startThePlayer(context: Context, playInLoop: Boolean = true) {
try {
mediaPlayer.reset()
mediaPlayer.isLooping = playInLoop
val uri = Settings.System.DEFAULT_RINGTONE_URI
mediaPlayer.setDataSource(context, uri)
mediaPlayer.setOnPreparedListener {
mediaPlayer.start()
}
mediaPlayer.prepareAsync()
} catch (e: Exception) {
log(e.toString())
}
}
VIBRATE permission is necessary for this to work.
Effect on Pixel 4 with Android 13:
Device is vibrating, as if trying to "emulate" the music played. Vibrations strength depends on value set in device's settings, completely ignoring volume set for alarm's music, and also messing up any vibrations set directly in my app.
What's interesting, is that for some reason Android's default clock app ignores this settings and device doesn't vibrate during it's alarms.
First, I suggest that you use android's VibratorManager to control your app vibrations settings (this is how the android development team called this service.. how bizarre). Use the getDefaultVibrator and cancel methods to stop any vibrations produced by your app (official docs to the rescue)
Second, because your app is an alarm clock please consider using the setWakeMode which will allow you to keep your app running in the background regardless to the display (in order to prevent the alarm from stopping if the screen is off). Here's the method documentation and also the PowerManager flags documentation
Hope you will find the right combination to satisfy your needs
Related
I have an issue with Android Vibrator. Basically, I have only one Singletone - scoped class incapsulating the android.os.Vibrator in a way, that it has two methods
startVibrating() {
if (VERSION.SDK_INT < VERSION_CODES.O) {
vibrator.vibrate(PATTERN, 0);
} else {
vibrator.vibrate(VibrationEffect.createWaveform(PATTERN, 0));
}
}
stopVibrating() {
vibrator.cancel();
}
The issue is that on some Samsung devices the Vibrator stops when the phone screen is turned off even I have a WakeLock for CPU.
Recently, we've got into the same situation.
According to Android docs:
If your process exits, any vibration you started will stop.
And you would expect that if you have any CPU-related WakeLock, it should work fine. However, we found that Samsung devices on Android 6 and Android 7 stop the Vibrator in some other cases too. For example, each time when the screen is turned off using PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, even if you take some other wake lock explicitly.
The good news that Samsung stops the Vibrator but it does not prohibit it from the future starts.
We've ended up with the next workaround:
When the screen goes off, the current activity gets onStop() call. So, we check if we should vibrate on that moment, and if we do, we simple start the Vibrator again. Maybe, not the best solution, but it works.
I was having problem whit android media player because when I turn off the screen the system goes to sleep mode and stop it and I didnt want that because nobody wants to listen music and have the screen on (who do that?).
I noticed that the problem was on the battery optimizations because if the mobile was plug to power cable it keeps the music playin even if it goes to sleep mode.
I tried with a lot of posibilities to fix it (reading android documentation and trying every thing I was finding in the net) but what works was this code:
public void doPower() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String packageName = getActivity().getPackageName();
PowerManager pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
try {
//some device doesn't has activity to handle this intent
//so add try catch
Intent intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
} catch (Exception e) {
}
}
}
}
The problem here is: The app at the beginning shows an alert asking the user to disable the battery optimizations and I dont want that, because some users may thinks "this is a bad thing" or "why its asking this?" Because an app like spotify doesnt do it.
How can I avoid the alert message? If I cant, how can I add something else to the message? (something like this: "So you will able to listen the radio with the screen turn off")
Thanks in advance and best regards.
You can't avoid that dialog and asking to avoid battery optimizations is absolutely not needed.
What is needed is a wakelock, as described in the MediaPlayer Using wake locks documentation:
When designing applications that play media in the background, the device may go to sleep while your service is running. Because the Android system tries to conserve battery while the device is sleeping, the system tries to shut off any of the phone's features that are not necessary, including the CPU and the WiFi hardware. However, if your service is playing or streaming music, you want to prevent the system from interfering with your playback.
In order to ensure that your service continues to run under those conditions, you have to use "wake locks." A wake lock is a way to signal to the system that your application is using some feature that should stay available even if the phone is idle.
If you're using MediaPlayer, this only requires you use the setWakeMode() method:
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
(Note the additional note on the documentation page about holding a wifi lock if you're streaming your media from the network.)
I have built an app that plays audio which must not be interrupted during it plays, because it provides timecode-information to other devices, and will not work properly when the device plays something different in between (e.g. a phone call).
So I need a solution to mute notifications while playing audio.
I found out so far:
When setting the permissions previously and guiding the user to allow the app to turn on DND with
startActivityForResult(new
Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS), 0);
I can activate the "do not disturb" (DND) mode via
NotificationManager.setInterruptionFilter(
NotificationManager.INTERRUPTION_FILTER_NONE)
but that will mute the audio-stream of my app also. (see 1)
block phone calls is only possible when the user has root access. I can not ask my users for that and also it will not mute other notifications
I can not use the flight mode, because the app needs WiFi to query the time via NTP from the internet.
Probably a solution is, to set the DND-mode manually and start the app and the stream afterwards. But how is it possible to send the user directly to the DND-menu, so that he/she could enable DND-mode and return to the app by pressing the back button?
I only found the Settings-Actions:
ACTION_NOTIFICATION_LISTENER_SETTINGS
ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS
ACTION_SOUND_SETTINGS
which are not the right screens for that. Maybe it is provided somehow to open the quick settings of the DND mode?
So to shorten it up, what I need to do:
User starts audio -> user is asked if he wants to active dnd for no interuption of the audio-playback ->
audio-playback is started ->
user stops audio -> DND is deactivated
Thanks in advance for ideas on that!
You are basically disabling all sounds with INTERRUPTION_FILTER_NONE. Use INTERRUPTION_FILTER_PRIORITY and configure notification policy to allow media playback like the following:
NotificationManager.setInterruptionFilter(
NotificationManager.INTERRUPTION_FILTER_PRIORITY );
NotificationManager.setNotificationPolicy(
new NotificationManager.Policy( NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA,
0, 0 ) );
You can also suppress some effects like lights by adding additional parameter to setNotificationPolicy – see Android help for details.
In Kotlin this can be done as..
notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val policy =
NotificationManager.Policy(Policy.PRIORITY_CATEGORY_MEDIA, 0, 0)
notificationManager.notificationPolicy = policy
}
NOTE: This is only possible in Android API level 28 and above since the policy PRIORITY_CATEGORY_MEDIA was added in that API.
There are other Policy categories that might be helpful in other cases. like
PRIORITY_CATEGORY_REMINDERS,
PRIORITY_CATEGORY_EVENTS,
PRIORITY_CATEGORY_MESSAGES,
PRIORITY_CATEGORY_CALLS,
PRIORITY_CATEGORY_REPEAT_CALLERS etc.. read more about them here NotificationManager.Policy Official doc
And you also need to add this permission
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
and call this on your Activity
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NotificationManager n = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
if(n.isNotificationPolicyAccessGranted()) {
} else {
startActivityForResult(new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS), 0);
}
}
I used AudioManager.setRingerMode() to handle the device volume in my application using:
AudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT) to turn off vibration and sound.
AudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE) to turn off the sound and turn on the vibration.
AudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL) to turn on the sound and turn on the vibration.
In other words, I was able to get control of the device volume and reach all "modes".
But, it's not possible to handle all modes of device volume in Android 5.0 using only the AudioManager.setRingerMode.
As per the documentation:
Setting the device to RINGER_MODE_SILENT causes the device to enter the new priority mode. The device leaves priority mode if you set it to RINGER_MODE_NORMAL or RINGER_MODE_VIBRATE.
How can I handle all modes (none, vibration in priority, sound in priority, vibration in all, sound in all) of device volume in Android 5.0?
With the new 5.0 API's there are a bunch of methods that can be used for setting the ringer/vibrate.
setVibrate (long[] pattern)
This method controls the vibration of the device. Takes a long in which the first value indicates the number of milliseconds to wait before turning on the vibrator. More details:
http://developer.android.com/reference/android/os/Vibrator.html#vibrate(long[], int)
http://developer.android.com/reference/android/app/Notification.Builder.html#setVibrate(long[])
setDefaults (int defaults)
This one sets the notifications properties i.e. SOUND,VIBRATE,ALL etc etc.
From the docs:
The value should be one or more of the following fields combined with
bitwise-or: DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS.
http://developer.android.com/reference/android/app/Notification.Builder.html#setDefaults (int defaults)
Hope this helps.
What is the difference between calling AudioManager.setRingerMode to calling AudioManager.setStreamMute(AudioManager.STREAM_VOICE_CALL, ...)
What does the documentation mean by "Ringer mode"? I'm pretty sure it is the phone ringer mode. Then how does it differ from calling setStreamMute with STREAM_VOICE_CALL?
If it is not the phone ringer mode, then what is it?
Thanks in advance.
I have never used the audio stream on the android platform, however, based on reading the documentation, I think setRingerMode will affect how the phone reacts to incoming calls. For example, AudioManager.setRingerMode(RINGER_MODE_SILENT) will disable vibrations and sound when an incoming call is received.
However, AudioManager.setStreamMute seems to control more than just the audio stream for phone rings.
From the documentation at http://developer.android.com/reference/android/media/AudioManager.html#STREAM_VOICE_CALL
I think that AudioManager.setRingerMode(RINGER_MODE_SILENT) will act the same way as AudioManager.setStreamMute(STREAM_RING, true).
I think the best way to see what the difference is (nd to see if what I am saying is true) would be to write a small program that tests the two features.