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
Related
When using the OnReceiveContentListener I followed along with the guide here. I also looked at the example project they reference. From my understanding the way it works is that if I enter the code
val editTextOnReceiveContentListener =
androidx.core.view.OnReceiveContentListener { editText, contentInfoCompat ->
val split = contentInfoCompat.partition { it.uri != null }
val uriContent = split.first
val remaining = split.second
/** Process uriContent here... **/
//return remaining
remaining
}
ViewCompat.setOnReceiveContentListener(
myEditText,
arrayOf("image/gif", "image/png"),
editTextOnReceiveContentListener
)
Then when 'remaining' is null the app will NOT show the user "AppName doesn't support image insertion here". And most of the time it seems to be work just as I expect.
However if I use the 'Cold Boot Now' option for starting the emulator (and other times occasionally) then the first gif that is entered will still display that image insertion is not supported here. Everything else seems to work fine except it shows the error. This happens even though the log shows that 'remaining' is null. I tested it on emulator devices Nexus 5 and Pixel 2 for API 29 and 31.
My question is, is this something that I should be concerned about and/or can fix in some way?
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'm trying to launch a second activity on a secondary display. This works fine on the emulators, but I need the secondary display to handle touch events.
With Android Pie, launching an activity on some second displays would throw a security exception. Google recently pushed out this new API for Android Q - isActivityStartAllowedOnDisplay() (https://developer.android.com/reference/android/app/ActivityManager.html#isActivityStartAllowedOnDisplay(android.content.Context,%2520int,%2520android.content.Intent)) - to be able to tell if the second display has this security exception or not.
This new API is helpful, BUT, is there any way around it? Maybe I've misunderstood the documentation, but it seems like if the device doesn't support it, then there's no way around it. Does anyone know of any displays that will NOT throw this security exception?
In order to get touch events to register on the secondary display (GeChic Touch Monitor), I had a DisplayLink device connected between the Android device and touch display. At this point, it was mirroring the view on the phone/tablet but would handle touch events. So, I wrote an app that would attempt to launch a second activity on the second display using this code on Android Pie OS:
DisplayManager mgr = (DisplayManager) this.getBaseContext().getSystemService(Context.DISPLAY_SERVICE);
if (mgr != null) {
Display[] displays = mgr.getDisplays();
for (int i = 0; i < displays.length; i++) {
Display display = displays[i];
Point point = new Point();
display.getSize(point);
if (point.y == PX_HEIGHT_OF_SECONDARY_DISPLAY || point.x == PX_HEIGHT_OF_SECONDARY_DISPLAY) {
Context displayContext = createDisplayContext(display);
Intent newIntent = new Intent(displayContext, ActivityCID.class);
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(display.getDisplayId());
newIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent, options.toBundle());
return;
}
}
}
Note that I did not use display.getDisplayId() and did a hacky way with the point.y and point.x values with a pixel width or height that did not match the pixel width or height of the Android phone/tablet. The displayId() was not always a consistent value which "should" be stable in Android Q. This is where the app would crash and the second activity would fail with a security permissions error. So, I used Android Q Beta to test the new isActivityStartAllowedOnDisplay() API. I ran this through Android Studio onto the phone (which was on Android Q Beta OS) to run it and to no surprise, the secondary display came back false. See code below:
public void launchOnSecondaryDisplay(Display display) {
Context displayContext = createDisplayContext(display);
Intent newIntent = new Intent(displayContext, ActivityTest.class);
ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Activity.ACTIVITY_SERVICE);
if (activityManager != null) {
boolean allowsDisplay = activityManager.isActivityStartAllowedOnDisplay(displayContext, display.getDisplayId(), newIntent);
if (allowsDisplay) {
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(display.getDisplayId());
newIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent, options.toBundle());
} else {
Toast.makeText(this, "Cannot start activity on that display as it is not supported.", Toast.LENGTH_LONG).show();
}
}
}
I decided to try this through the command line. After networking the physical device to match my Mac's connected network, I was able to connect to the phone wirelessly and was able to make changes in adb. Using an adb command, I was able to get a secondary activity on the secondary display! It seemed to be working! But no, it was not... Touch events still continued to act like the device was being mirrored so this was still a problem and was not going to work.
I discussed this with the Googler as well and was explained that adb root can override these permissions. However, there was still no way to get the touch events to map to the second activity on the secondary display.
At the moment of writing this, the only supported way to test multi touch displays is to use a physical device running Android Q Beta and follow these steps:
enable developer options,
in developer options, enable these 4 options: Force All Activities to be Resizeable, Freeform Windows, Force Desktop, and Simulate Secondary Display (doesn't matter which option picked for simulate secondary display),
reboot the device,
connect a mouse to the device. The mouse will show up and be stuck inside the overlaying window that is "simulating the secondary display". This will handle touch events.
In the future, there will be emulators that have multiple displays to better test multi display applications but this is not available at the moment.
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 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