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.
Related
When attempting to pair with a Bluetooth device (programatically), in the case where, for example a pin code is incorrect, Android will display a toast notification to the user.
I'd prefer to handle this fully in code without the default system toasts.
Is it possible to hide these system notifications ?
It appears that these toast msgs are coming out of BluetoothEventManager.java in the platform Bluetooth settings package and are triggered based on the reason for the pairing failure, identified by an EXTRA_REASON in the Intent associated with the BOND_NONE state transition. Only specific reasons cause a toast msg to be displayed. If you can manipulate the reason code in that Intent, you'll suppress the toast.
So far, I haven't been successful in doing that though.
As a more heavy-handed alternative, if you can modify and build your Android platform source, you can gut the .showError() method in
./packages/apps/Settings/src/com/android/settings/bluetooth/Utils.java
and prevent the toast messages as well.
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
This question is pretty much a duplicate but the linked issue was never really resolved and the thread is a few months old so I didn't want to resurrect it.
The default behavior of apps running on an ICS device with Android Beam turned on is to push a message with the application Uri that will be processed by Google Play on the receiving end.
I am trying to develop an activity that will push NdefMessage if condition A is true and will otherwise disable pushing messages. The API documentation for setNdefPushMessage(...) seems to indicate that this is possible by passing in a null message:
Pass a null NDEF message to disable foreground NDEF push in the specified activities.
However, trying to simply ban all pushes via the following code still results with the "Touch to Beam" UI coming up and an application Uri being sent...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getNfcAdapter().setNdefPushMessage(null, this);
}
Does anyone know if it is actually possible to disable pushes from an Activity? A few systems apps do it but I have not been able to locate the code that achieves this. Any help is much appreciated.
This seems to be a bug in Android ICS. Although the documentation says you should be able to disable it by setting the NDEF message null, this simply does not work. Good news is that it has been fixed and now does work in Android 4.1 Jellybean.
Is it possible to programatically access the text posted on the android notification area (displayed by an application which is not my own)?
The logic I am after would be something like...
for each (NotificationMessage m in NotificationArea.Notifications)
{
String msg = m.GetMessage()
}
Unfortunately, no. This would create security concerns, possibly hijacking personal information. See This Thread
In Android 4.3 and above, use the NotificationListenerService. Otherwise use Accessibility.
http://developer.android.com/reference/android/service/notification/NotificationListenerService.html
This is possible with the accessibility service, but you have to pick up on notifications as they arrive (ie there isn't a list you can iterate around at any point). You'd need to build that up yourself.
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.