I was just wondering if there's a way to check if an Android device (21+) is in Do Not Disturb mode? I know there's AudioManager.RINGER_MODE_SILENT, but I was wondering if that pertains to this situation, or if there's a better way to check?
With pointers from Android how to turn on do not disturb (dnd) programmatically:
In SDK 23, android.app.NotificationManager provides the interface you need, namely NotificationManager.getCurrentInterruptionFilter().
It should return one of:
INTERRUPTION_FILTER_PRIORITY
INTERRUPTION_FILTER_ALARMS
INTERRUPTION_FILTER_NONE
INTERRUPTION_FILTER_ALL
INTERRUPTION_FILTER_UNKNOWN
According to Google Help on Nexus Devices Do not Disturb is a feature of Android >= 6.0, so SDK 23 should be reasonable to ask. If it is not, I think it would be reasonable to ask why, to be able to provide a workaround.
I'm bringing the necro with this post, but was just searching for a solution to the same problem and came past this post as I did, so am leaving a solution for future ref.
I couldn't find an "official" way to do this, but did find a value that can return the DnD state as an integer. The feature is called "zen_mode" internally, so you can check the current value with the following code:
Global.getInt(getContentResolver(), "zen_mode")
That'll return:
0 - If DnD is off.
1 - If DnD is on - Priority Only
2 - If DnD is on - Total Silence
3 - If DnD is on - Alarms Only
When the setting is priority only, it returns nothing, but you can figure out that state by assuming no news = Priority Messages mode. This must have been a bug, as now it's a year on, this now returns a value just like the other states. No idea when it was fixed, but it now works as you'd expect.
I tried with a settings observer too, which works but only for certain state transitions, so I think polling periodically is the best bet, until an "official" way to listen for this state change becomes available.
I've included both observer and polling methods of getting the value in this Gist. Hopefully it'll help/save someone else some time.
Update 15th March 2017: Thanks to David Riha for the comment. Added 1 - Priority Only state to answer, revised the Gist to recognise that state, and to output unknown values to log in case any other devices or future updates decide to return a different value.
You need to first add to the manifest file, permission to change the audio settings. To be able to set ringer mode to silent, you must ask permission to access notification policy
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
and then to detect the "do not disturb" you can do as follows
NotificationManager notificationManager = (NotificationManager) getActivity().getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
// Check if the notification policy access has been granted for the app.
if (!notificationManager.isNotificationPolicyAccessGranted()) {
Intent intent = new
Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
startActivity(intent);
return;
}
ToggleDoNotDisturb(notificationManager);
private void ToggleDoNotDisturb(NotificationManager notificationManager) {
if (notificationManager.getCurrentInterruptionFilter() == NotificationManager.INTERRUPTION_FILTER_ALL) {
notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
audioToggle.setText(R.string.fa_volume_mute);
} else {
notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
audioToggle.setText(R.string.fa_volume_up);
}
}
also you need to check permissions
NotificationManager mNotificationManager = (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
// Check if the notification policy access has been granted for the app.
if (!mNotificationManager.isNotificationPolicyAccessGranted()) {
Intent intent = new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
startActivity(intent);
}
NotificationManager.Policy a = null;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
a = mNotificationManager.getNotificationPolicy();
Log.d(TAG, "onClickDND: "+a.priorityCallSenders);
Log.d(TAG, "onClickDND: "+a.priorityCategories);
}
output
--------------------------------------
FROM ANYONE
call senders : 0
categories : 109
Message : 0
State : 1
FROM CONTACTS ONLY
call senders : 1
categories : 109
Message : 1
State : 1
FROM STARRED CONTACTS ONLY
call senders : 2
categories : 109
Message : 2
State : 1
NONE
call senders : 2
categories : 97
Message : 2
State : 1
Related
I am setting the interruption filter using this code:
NotificationManager myNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
myNotificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
It works very well in every device, but, for some reason it doesn't in Xiaomi devices using Android 9. Do Not Disturb mode is not activated in those devices. It remains unchanged. If I ask the device what is the current interruption it answers with a value of 5. Which is an unknown and not documented value in Android Developer as stated here.
int iCurrentInterruption = oNotificationManager.getCurrentInterruptionFilter();
iCurrentInterruption has a value of 5. None of the following:
INTERRUPTION_FILTER_UNKNOWN = 0
INTERRUPTION_FILTER_ALL = 1
INTERRUPTION_FILTER_PRIORITY = 2
INTERRUPTION_FILTER_NONE = 3
INTERRUPTION_FILTER_ALARMS = 4
I guess in Xiaomi devices the System takes longer to activate the interruption filter, so you cannot ask for the result just right away.
So, I sleep for a second and ask after that.
It also happens that sometimes it doesn't get activated at the first time so I have to call setInterruptionFilter a second time.
Something like this:
setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
wait
If INTERRUPTION_FILTER_PRIORITY==getCurrentInterruptionFilter() then return OK
//Second try:
setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
wait
If INTERRUPTION_FILTER_PRIORITY==getCurrentInterruptionFilter() then return Ok
return error
The NotificationManager class in android lists two methods setInterruptionFilter (int interruptionFilter) and setNotificationPolicy (NotificationManager.Policy policy).
From the docs:
setInterruptionFilter
public final void setInterruptionFilter (int interruptionFilter)
Sets the current notification interruption filter.
The interruption filter defines which notifications are allowed to interrupt the user (e.g. via sound & vibration) and is applied globally.
setNotificationPolicy
public void setNotificationPolicy (NotificationManager.Policy policy)
Sets the current notification policy.
Both were added in API level 23.
From my understanding, both seem to accomplish the same task of setting the Do Not Disturb policy for the android device. What exactly is the difference between the two methods?
with setNotificationPolicy you can set the Do Not Disturb "Allow Interruptions" policies like NotificationManager.Policy.PRIORITY_CATEGORY_ALARM ( allow alarm )
with setInterruptionFilter you set the actual Don Not Disturb on or off where :
NotificationManager.INTERRUPTION_FILTER_PRIORITY = set DND on with the setNotificationPolicy settings
NotificationManager.INTERRUPTION_FILTER_NONE = set DND on for every interuption
NotificationManager.INTERRUPTION_FILTER_ALL = set DND off
TIP. You can get the policy first before changing it with NotificationManager.getNotificationPolicy() to set it back after your done.
I have an app that should show a notification every 2 hours and should stop if user has already acted upon the notif. Since background services are history now, I thought of using WorkManager ("android.arch.work:work-runtime:1.0.0-beta01") for the same.
My problem is that although the work manager is successfully showing the notifications when app is running, but it won't show notification consistently in the following cases(I reduced the time span from 2 hours to 2 minutes to check the consistency):
when app is killed from the background.
device is in screen off.
state device is in unplugged state(i.e not charging).
By consistency , i mean that the notifications show at least once in the given time span. for 2 minutes time span, the freq of notifications went from once every 4 minutes to completely not show any notification at all. for 2 hours timespan( the timespan that i actually want), its been 4 hours and i haven't got a single notification. Here is the Code i am using for calling WorkManger:
public class CurrentStreakActivity extends AppCompatActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
setDailyNotifier();
...
}
private void setDailyNotifier() {
Constraints.Builder constraintsBuilder = new Constraints.Builder();
constraintsBuilder.setRequiresBatteryNotLow(false);
constraintsBuilder.setRequiredNetworkType(NetworkType.NOT_REQUIRED);
constraintsBuilder.setRequiresCharging(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
constraintsBuilder.setRequiresDeviceIdle(false);
}
Constraints constraints =constraintsBuilder.build();
PeriodicWorkRequest.Builder builder = new PeriodicWorkRequest
.Builder(PeriodicNotifyWorker.class, 2, TimeUnit.HOURS);
builder.setConstraints(constraints);
WorkRequest request = builder.build();
WorkManager.getInstance().enqueue(request);
}
....
}
Here is the worker class(i can post showNotif(..) and setNotificationChannel(...) too if they might be erroronous):
public class PeriodicNotifyWorker extends Worker {
private static final String TAG = "PeriodicNotifyWorker";
public PeriodicNotifyWorker(#NonNull Context context, #NonNull WorkerParameters workerParams) {
super(context, workerParams);
Log.e(TAG, "PeriodicNotifyWorker: constructor called" );
}
#NonNull
#Override
public Result doWork() {
// Log.e(TAG, "doWork: called" );
SharedPreferences sp =
getApplicationContext().getSharedPreferences(Statics.SP_FILENAME, Context.MODE_PRIVATE);
String lastcheckin = sp.getString(Statics.LAST_CHECKIN_DATE_str, Statics.getToday());
// Log.e(TAG, "doWork: checking shared preferences for last checkin:"+lastcheckin );
if (Statics.compareDateStrings(lastcheckin, Statics.getToday()) == -1) {
Log.e(TAG, "doWork: last checkin is smaller than today's date, so calling creating notification" );
return createNotificationWithButtons(sp);
}
else {
Log.e(TAG, "doWork: last checkin is bigger than today's date, so no need for notif" );
return Result.success();
}
}
private Result createNotificationWithButtons(SharedPreferences sp) {
NotificationManager manager =
(NotificationManager) getApplicationContext().getSystemService((NOTIFICATION_SERVICE));
String channel_ID = "100DaysOfCode_ID";
if (manager != null) {
setNotificationChannel(manager,channel_ID);
showNotif(manager, channel_ID, sp);
return Result.success();
}
else {
return Result.failure();
}
I am using a xiaomi miA2 androidOne device with Android Pie(SDK 28). There are a few other things that are troubling me:
What can i possibly do to know if my WorkManager is running? Other that just wait for 2 hours and hope for a notification. I actually tried something like that, keeping my phone connected to pc and checking android studio's logcat every now and then. It DOES run all the logs when the worker is actually called, but i don't think that's a correct way to test it, or is it?
In the above Code, the setDailyNotifier() is called from the onCreate() every time the app is opened. Isn't it Wrong? shouldn't there be some unique id for every WorkRequest and a check function like WorkManger.isRequestRunning(request.getID) which could let us check if a worker is already on the given task??If this was a case of AsyncTask, then boy we would have a mess.
I have also checked #commonsware's answer here about wakelock when screen is off, but i remember that work manager does use alarm manager in the inside when available. So what am I missing here?
Few comments:
WorkManager has a minimum periodic interval of 15minutes and does not guarantee to execute your task at a precise time. You can read more about this on this blog.
All the usual background limitation you've on newer Android releases are still relevant when you use WorkManager to schedule your tasks. WorkManager guarantees that the task are executed even if the app is killed or the device is restated, but it cannot guarantee the exact execution.
There's one note about the tasks being rescheduled when your app is killed. Some OEM have done modification to the OS and the Launcher app that prevents WorkManager to be able to accomplish these functionality.
Here's the issuetracker discussion:
Yes, it's true even when the phone is a Chinese phone.
The only issue that we have come across is the case where some Chinese OEMs treat swipe to dismiss from Recents as a force stop. When that happens, WorkManager will reschedule all pending jobs, next time the app starts up. Given that this is a CDD violation, there is not much more that WorkManager can do given its a client library.
To add to this, if a device manufacturer has decided to modify stock Android to force-stop the app, WorkManager will stop working (as will JobScheduler, alarms, broadcast receivers, etc.). There is no way to work around this. Some device manufacturers do this, unfortunately, so in those cases WorkManager will stop working until the next time the app is launched.
As of now , i have this app installed for last 8 days and i can confirm that the code is correct and app is working fine. as said by pfmaggi , the minimum time interval for work manager to schedule the work is 15 minutes, so there is a less chance that the WorkManager would have worked as expected in my testing conditions( of 2 minutes ) . Here are some of my other observations:
Like I said in the question that i was unable to recieve a notification for 4 hours even though i have passed the repeat interval as 2 hours. This was because of Flex Time. I passed in the flex time of 15 minutes and now it shows notifications between correct time interval. so i will be marking pfmaggi's answer as correct.
The problem of repeated work request can be solved by replacing WorkManager.getInstance().enqueue(request) with WorkManager.getInstance().enqueueUniqueWork(request,..)
I was still unable to find a way to test the work manager in the way i have described.
I am trying to programmatically find out for which apps the Do Not Disturb setting is bypassed exceptionally.
So far, I am using the following code to check whether the phone is set in Do not Disturb mode or not :
public static boolean isDnDModeEnabled(Context context)
{
if(Build.VERSION.SDK_INT <23)
return false;
try {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int filterValue = notificationManager.getCurrentInterruptionFilter();
switch(filterValue)
{
case NotificationManager.INTERRUPTION_FILTER_ALL : Log.d("DND","Interruption filter all");
break;
case NotificationManager.INTERRUPTION_FILTER_ALARMS : Log.d("DND","Interruption filter alarms");
break;
case NotificationManager.INTERRUPTION_FILTER_PRIORITY : Log.d("DND","Interruption filter priority");
break;
case NotificationManager.INTERRUPTION_FILTER_UNKNOWN : Log.d("DND","Interruption filter unknown");
break;
case NotificationManager.INTERRUPTION_FILTER_NONE : Log.d("DND","Interruption filter none");
break;
}
if(filterValue == NotificationManager.INTERRUPTION_FILTER_ALL)
return false;
else if(filterValue == NotificationManager.INTERRUPTION_FILTER_PRIORITY)
{
//Logic based on which apps are allowed as priority
return true; //or false
}
else
return true;
}
catch(Exception e)
{
return false;
}
}
When I click on the Priority app notiications tab, I get a list of all installed apps for which I get to choose which apps to allow as priority exceptions.
My question is how to programmatically get the list of apps which are allowed as priority exceptions for Do Not Disturb mode, and thereby define the logic replacing the comment in the above code? Any solutions would be thoroughly appreciated.
You're looking for a way to determine which apps on the system have a notification importance of Notification.IMPORTANCE_MAX and, sorry to say but this is not possible by a non-system app. You need access to the INotificationService so you can call getImportance(packageName). See the Notification Manager source but it's guarded by a check that ensures you're a system app or the app whose package you passed so reflection is out...
Google allows an app to obtain its own notification importance through the NotificationManager with getImportance() (see docs) but you can't call it with an arbitrary package.
The other answer here references checking out the source from the system settings app and that's exactly what I did and, after a while of tracing through the code, I discovered how they determine which apps should show up in the "Overrides Do Not Disturb" menu by this code here which led me down the path of discovering how we could determine the IMPORTANCE_*
Sorry man, but the suggestions made by the other answerer are also not going to work because they are either incorrect (packages.xml doesn't have the info) or are going to require root which isn't reliable on all devices.
You can try following things(some of this things might need privilege access):
Download the source code for settings app and see how settings apps
is getting the list.
Analyse packages.xml.Usually lot of info about the apps is stored there.
Check if the list is stored in settings.db
My app allows the user to access their corporate voice mail. Normally, durring a phone call when the user holds the device up to their ear, the screen shuts off so they wont accidentally push buttons with their face. I would like to make my app do the same thing when the user is listening to their voice mail.
anyone know how to do this?
If you are allowed to look at open source code without causing yourself problems, check the source of the Android Phone Application. Specifically src/com/android/phone/PhoneApp.java and src/com/android/phone/InCallScreen.java.
From src/com/android/phone/PhoneApp.java:
//Around line 519
// Wake lock used to control proximity sensor behavior.
if ((pm.getSupportedWakeLockFlags()
& PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) != 0x0) {
mProximityWakeLock = pm.newWakeLock(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
LOG_TAG);
}
....
// Around line 1334
if (((state == Phone.State.OFFHOOK) || mBeginningCall)&& !screenOnImmediately) {
// Phone is in use! Arrange for the screen to turn off
// automatically when the sensor detects a close object.
if (!mProximityWakeLock.isHeld()) {
if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: acquiring...");
mProximityWakeLock.acquire();
} else {
if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: lock already held.");
}
} else {
// Phone is either idle, or ringing. We don't want any
// special proximity sensor behavior in either case.
if (mProximityWakeLock.isHeld()) {
if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: releasing...");
// Wait until user has moved the phone away from his head if we are
// releasing due to the phone call ending.
// Qtherwise, turn screen on immediately
int flags =
(screenOnImmediately ? 0 : PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE);
mProximityWakeLock.release(flags);
}
}
Additionally, if you look at the code for the PowerManager class, PROXIMITY_SCREEN_OFF_WAKE_LOCK is documented (but hidden) and should do what you want ( I am not sure which API level this works for, however ) -- but not in the table for some reason.
/**
* Wake lock that turns the screen off when the proximity sensor activates.
* Since not all devices have proximity sensors, use
* {#link #getSupportedWakeLockFlags() getSupportedWakeLockFlags()} to determine if
* this wake lock mode is supported.
*
* {#hide}
*/
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = WAKE_BIT_PROXIMITY_SCREEN_OFF;
If you aren't afraid of using a potential undocumented feature, it should do exactly what you need.
as of API level 21 (Lollipop) you can get proximity wake lock this just like that:
if(powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
wakeLock.setReferenceCounted(false);
return wakeLock;
} else {
return null;
}
}
then it is up to you to acquire and release the lock.
PS: PowerManager#getSupportedWakeLockFlags was hidden, but now exists nomore. They have invented isWakeLockLevelSupported instead.
Probably you don't need it anymore but for the ones that are interested in code you could have a look at my SpeakerProximity project at http://code.google.com/p/speakerproximity/
What you are seeing is the use of a proximity sensor. For devices that have one, you access it through SensorManager.