I need to find out when the last user interaction was, regardless of which application is on top. I don't care about where or what the event was I just need to know when it was. Or alternatively, as it happens, I receive an event.
I've tried multiple things:
Create service with a window and added a touch listener. This ate the touch event and didn't pass it down
Looked for a shell command. getevent works (new line comes in every time a touch is received) however you need root and so it is not an appropriate solution for me.
Looked for "time until lock" but came up with nothing.
Also note: There is no security concern with this as I don't need any identifying information such as touch location. Just a type stamp (or live event).
I'm open to using reflection to figure it out as well.
#user2558882 has a very good solution. As of now, that's the best approach I've come across.
While that's great, it still requires the user to manually enable our application in the accessibility controls. We have customers with thousands of devices and we have a way to automatically update and change settings. We try and keep manual configuration to a minimum, but some things still require user input such as enabling Device Admin mode. So this solution is acceptable however I'm still open to a way that doesn't require any user input to enable.
I ended up implementing #user2558882's idea to use an accessibility service. Though other ideas are welcome.
This is just an idea, and may not be fully transferable.
Here's what an AccessibilityService can do:
An accessibility service runs in the background and receives callbacks
by the system when AccessibilityEvents are fired. Such events denote
some state transition in the user interface, for example, the focus
has changed, a button has been clicked, etc.
You will be informed of the event inside onAccessibilityEvent(AccessibilityEvent):
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
// Some event
timeSinceLastInteraction = System.currentTimeMillis();
}
You could periodically log the updates:
Log.i("LOG_TIME_ELAPSED", "Last user interaction was " +
((System.currentTimeMillis() - timeSinceLastInteraction) / 1000) +
" seconds ago.");
There are two ways in which you can configure your AccessibilityService:
In code, inside onServiceConnected(). (API 4 onwards)
In xml, using the meta-data tag in your service. (API 14 onwards)
In your application's case, you could probably set AccessibilityServiceInfo.eventTypes to:
accessibilitySeviceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
But, TYPES_ALL_MASK will include notifications such as: AccessibilityEvent.TYPE_ANNOUNCEMENT, AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED etc. which I am guessing you do not care to intercept. So, you'll need to choose a subset of AccessibilityEvent.TYPE_X.
Another thing you should be careful about is the notification timeout:
The timeout after the most recent event of a given type before an
AccessibilityService is notified.
The event notification timeout is useful to avoid propagating events
to the client too frequently since this is accomplished via an
expensive interprocess call. One can think of the timeout as a
criteria to determine when event generation has settled down.
So, be generous with the timeout value.
You'll find this page very helpful in case you decide to go with the AccessibilityService option: Developing an Accessibility Service.
From your comments to Chloe's answer, it seems that the device is under your control: meaning, to some extent, you don't have to rely on the user for enabling the service:
The lifecycle of an accessibility service is managed exclusively by
the system and follows the established service life cycle.
Additionally, starting or stopping an accessibility service is
triggered exclusively by an explicit user action through enabling or
disabling it in the device settings.
You can enable the AccessibilityService at time of deployment, and perhaps restrict access to Settings menu using an app like AppLock.
Another option is to check whether your AccessibilityService is enabled from time to time:
AccessibilityManager am = (AccessibilityManager)
getSystemService(ACCESSIBILITY_SERVICE);
List<AccessibilityServiceInfo> listOfServices =
am.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
for (AccessibilityServiceInfo asi : listOfServices) {
// Check if your AccessibilityService is running
}
In case the AccessibilityService has been disabled by a inquisitive/notorious user, you can lock the device by displaying a fullscreen view with text: Device has been locked. Contact a Sales Rep to unlock this device.
I believe you have to return false to indicate that your transparent service window did not consume the touch event. That way the event loop will pass it down the stack to the lower windows.
https://developer.android.com/reference/android/view/View.OnTouchListener.html
Another possibility, is to add a service which listens for motion/accelerometer events.
https://developer.android.com/guide/topics/sensors/sensors_motion.html
Another possibility is to listen for ACTION_USER_PRESENT
https://developer.android.com/reference/android/content/Intent.html#ACTION_USER_PRESENT
or ACTION_SCREEN_ON
https://developer.android.com/reference/android/content/Intent.html#ACTION_SCREEN_ON
Related
Can anyone suggest an Action (intent) which an Android 8.1.0 system should broadcast to registered receivers when changing Notification Channel setting values? Some years back, we did the work to update our notifications system into the required channels and groups and specified a broadcast receiver for the settings changes in the manifest (later on we had to fix that when they took away implicit intents, so now it registers in code too - but that's fixed already). Our receiver sets internal "display" settings which correspond to the notification-channel setting chosen by the user into our sharedpreferences file. That's what controls the display of the content when a user actually opens the app.
I have notes that say we tested this on Android 8, but the actions/intents we registered for don't exist until API28. I'm unclear on how this ever passed on the 8.1 devices, but maybe we were focused on the notifications only and didn't notice the display. So right now, we're stuck with notification settings changes working, but if a user happens to go to the app for something else, they still see messages they expected to be hidden.
Android 9+ notification settings changes work fine all the way through because the broadcast receiver registers for the new-in-API-28 NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED, ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED and ACTION_APP_BLOCK_STATE_CHANGED and these get delivered fine when a user changes settings, since I can see the receiver runs and sets the preferences as we want.
Just to debug this, I registered our BroadcastListener for a few extra intents (basically everything that looked like it might have something to do with notifications!) - like this:
// Register to receive a broadcast whenever notification settings are changed (before API 26 this was done by specifying in AndroidManifest only,):
notificationSettingsReceiver = new NotificationSettingsBroadcastReceiver();
IntentFilter filter = new IntentFilter(NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED);
filter.addAction(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
filter.addAction(NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED);
filter.addAction(NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED);
filter.addAction(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
this.registerReceiver(notificationSettingsReceiver,filter);
This proved that the code in my BroadcastReceiver is working fine in 8.1.0 if it would only get called; I can force it to run by changing a few Notification settings (disabling them, or changing the sounds, etc) and then switching the phone into do-not-disturb mode. The actual notification-settings-changes don't ever cause any intent to arrive at my receiver, but the DND change does, so this triggers the notification settings to get written into sharedpreferences (which is where our display code is expecting to find them).
Pre-android 8 notification settings still seem to work fine too and we write these changes into our sharedpreferences file directly. Something about compatibility must just handle this for us because it all still "just works" on API 23 devices.
If anyone could suggest a way for an API 27 device to listen for notification-channel settings changes, I would be most grateful! There has to be a way to to this in API 27, isn't there?
After a few days of looking around and trying things, I was unable to have the NotificationManager in API 27 send anything at all to our BroadcastReceiver.
I was able to come up with a solution, although at first it seemed a bit heavy-handed.
For Android O+ devices, I simply added the same "syncPreferences" block of code to our app's onCreate, onResume and onDestroy methods which calls the same function as our BroadcastReceiver (which works fine in API 28+) to write the values into our shared_prefs file at that time. I say "heavy-handed" since it does it every time, regardless of whether anything has changed, but it actually works very well. It's actually simpler than all the overhead of building a receiver and listening for notification changes... I could probably eliminate that whole process now!
As an amusing aside, as a trekker, I have to admit that I got quite a chuckle from whoever adds the android VERSION_CODES constants though! Check the comment I found while looking up API numbers-to-buildcode declarations:
/**
* Q.
* <p>
* <em>Why? Why, to give you a taste of your future, a preview of things
* to come. Con permiso, Capitan. The hall is rented, the orchestra
* engaged. It's now time to see if you can dance.</em>
*/
public static final int Q = 29;
Is it possible in react-native to execute a background task with monitoring GPS position (without running the whole app in the background), even if the phone is locked?
I want to show some information (notification) onscreen when the phone will be in some position.
I need to declare the subject of a project in my studies, but I don't know if it's possible to implement this functionality.
It most certainly is... if you have only one given position (Or fewer than 20 positions) what you'd be best using is CoreLocation's geofencing APIs!
You can set up a region like so:
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
// Register the region.
let region = CLCircularRegion(
center: center,
radius: maxDistance, identifier: identifier
)
region.notifyOnEntry = true
locationManager.startMonitoring(for: region)
}
(Assuming you have already requested and checked the user's location permissions)!
Your app will then get launched in the background whenever a region monitoring event occurs, so you must make sure to setup a new CLLocationManager and give it a delegate to receive the updates in your application(_application:didFinishLaunchingWithOptions) function!
You can then use the delegate callbacks to trigger a local notification.
There is a slight caveat here, that this won't work if the user has disabled background app refresh for your app!
Luck, there's already a react-native library that allows you to setup these regions: https://github.com/martijndeh/react-native-region-monitor. Unfortunately I do think it will launch your JavaScript app fully (in the background of course) when a notification comes through, but that's not too big an issue.
There may also be info on the GitHub provided for how to implement this on Android too!
I have an app that reads notifications through TTS. With Google's change in policy for apps using Accessibility, I switched to Notification Listener, though that meant I had to remove the option to read toast messages. I released the update last night and already received feedback from someone who wanted the toast feature back.
Is there any way to read toast messages without using Accessibility? My search for a solution came up empty.
Update (2018-01-16)
After a little digging into the Android source, I found this relevant method in the Toast class (API 27):
private void trySendAccessibilityEvent() {
AccessibilityManager accessibilityManager =
AccessibilityManager.getInstance(mView.getContext());
if (!accessibilityManager.isEnabled()) {
return;
}
// treat toasts as notifications since they are used to
// announce a transient piece of information to the user
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setClassName(getClass().getName());
event.setPackageName(mView.getContext().getPackageName());
mView.dispatchPopulateAccessibilityEvent(event);
accessibilityManager.sendAccessibilityEvent(event);
}
That makes it clear that if no accessibility services are enabled, toasts aren't dispatched to the handler that AccessibilityService uses, so some kind of hack to access toasts the same way Accessibility gets them would be impossible without any services enabled. Of course, I'm trying to avoid creating an accessibility service again and don't want to rely on users having a different one enabled.
It appears that if there is any way to access toasts without Accessibility, it may have to be some fairly complex reflection to reach Toast$TN.mNextView or .mView through com.android.server.NotificationManagerService.mToastQueue and $ToastRecord.callback. It's a bit over my head right now as I'm not very familiar with reflection, but I'm still digging.
My app main usage is overlay, the overlay is running from a service.
Android Security add the nice "Screen Overlay Detected"
I want to avoid "Screen Overlay Detected" when user tries to change permissions. so... I've add an AccessiblityService that detects:
if ( event.getPackageName().equals("com.google.android.packageinstaller") ){
stopService(myServiceIntent);
}
However, even now I see this message popping. (when my service is stopped...).
I saw Twilight does it without problem.
What am I missing?
p.s. - I've also tried building a signed apk but saw exact same behavior.
It seems I've been able to resolve this.
a) stopService isn't assured your service will be stopped.
as described here :
It will not be destroyed until all of these bindings are removed. See > the Service documentation for more details on a service's lifecycle.
b) I was able to kill my service by sending intent that called stopSelf().
However process killing/starting can be slow.
c) Best resolution: so it seems Android checks for view visibility. no need to kill services or do anything more complicated.
Current way I'm doing it:
- AccessibilityService (already used by my app) monitor "com.google.android.packageinstaller" though it can be refined to class: "com.android.packageinstaller.permission.ui.ManagePermissionsActivity"
Once detected in this class, we send Intent to "duck", and when we're out, we send another intent that we're back on.
The service handles those calls by:
[ourView].setVisibility(View.INVISIBLE); // when permission settings shown
[ourView].setVisibility(View.VISIBLE); // when normal flow
As long as Android 6.x is buggy on some devices where this "overlay alert" is displayed without any reason (on 2 to 5% of the devices according to my analytics data), the best solution is to avoid the whole permission process by defining the targetSdk to 22. Take care that you can't downgrade the target sdk for a new version or this will induce a INSTALL_FAILED_PERMISSION_DOWNGRADE error when the user updates requiring an unisntall/install of the app.
I wan to hide/show my caller id from my activity programmatically. I tried to find it in the android documentation but without the luck. Maybe you have any ideas?
I posted a question asking this on the Android Google group and got absolutely no answers at all. I've also seen a couple of other question on SO which also had no answers (or none that work).
I came to the conclusion that it simply isn't possible. My reasoning is this...
If I go to Settings -> Call -> Additional settings, I see an AlertDialog which has a HeaderTitle of 'Call settings' and I see a circular progress indicator and a message saying 'Reading settings...'.
It occurs to me that my phone is, at that point, accessing my phone/network provider. The resulting 'chooser' dialog gives me options for 'Network default', 'Hide number' and 'Show number' and when I make a selection (or even if I just 'Cancel' the dialog), I get another AlertDialog with circular progress indicator with the message 'Updating settings...'.
In short, it seems the Caller ID setting is not entirely 'local' to the phone settings and relies on interaction with the provider and, for whatever reason, as a result of this the Android APIs don't allow this to be manipulated programatically.
I'm not sure if this is something on the 'To Do' list for future versions of Android or if there are legal/security implications in allowing it to be done or some other reason. Whatever the case may be, I haven't found anybody so far who is able to explain why there isn't a method for TelephonyManager (for example) to simply switch this.
EDIT: No luck on getting the Additional Settings AlertDialog with the standard APIs either.
The reason I say that is that it is possible to pull up various parts of the device's 'Settings', e.g., in one of my apps I use android.provider.Settings.ACTION_WIRELESS_SETTINGS in the constructor of an Intent passed to startActivity(). This brings up the Settings page for enabling/disabling wi-fi, mobile internet and bluetooth.
android.provider.Settings has other similar ACTIONs for other Settings pages but there isn't even one for 'Call' never mind Call -> Additional Settings and nothing for the AlertDialog to allow you to choose to Hide/Show the outgoing Caller ID.
If this can be done then it would have to be an undocumented API unless I completely missed it (I spent a long time looking). I suspect examining the Android source-code may be the only way to find an answer and I haven't attempted that yet.
I have managed to get Additional call settings dialog. Explanation below:
Although it looks like it is part of the Settings, in fact it is part of the Native PhoneApp. If you take a look at the AndroidManifest.xml of the PhoneApp you will see that Activity GsmUmtsAdditionalCallOptions has defined IntentFilter for the android.intent.action.MAIN.
So, the code that I checked to work correctly on several phones:
Intent additionalCallSettingsIntent = new Intent("android.intent.action.MAIN");
ComponentName distantActivity = new ComponentName("com.android.phone", "com.android.phone.GsmUmtsAdditionalCallOptions");
additionalCallSettingsIntent.setComponent(distantActivity);
startActivity(additionalCallSettingsIntent);
If the #31# trick works for your needs for a single call then you could add a broadcast receiver that listens for the outgoing call notification and modifies the number to include #31# at the start before it gets dialled. Android allows the number to be changed on the way through like that.
Only works if your default is to enable caller ID and your network support #31# and you want to toggle it off using a widget, say.
The Caller ID is network specific not something that the phone controls. In fact in certain mobile network configurations the phone doesn't even 'know' its own phone number.
Some networks support sending an activate/deactivate caller ID network command. In GSM this is normally #31#. It can be permanent or on a per call basis.
Permanent requests the network to hide the caller ID for all calls.
Per call requests the network to hide the caller ID only for that call. The latter is achieved by prefixing the number being called by #31#, so for example calling #31#85432786426 would call 85432786426 hiding the caller.
Some networks support both, some only support one of them, and some do not enable it. Try your luck and try prefixing the dialed number with #31# and see if it works.
http://www.gsm-security.net/faq/gsm-caller-id-clip-clir.shtml
If you want a shortcut to the additional call settings, you can use App Cut and select GSM settings. It will place a shortcut on your home screen.