I have been looking at the new methods available for Accessibility in Android O. I ran across this new method called getAccessibilityButtonController, I am unsure precisely what it does and an intended use. I know that in Android O there is a navigation button that can be used for an accessibility service. Does this accessibility button only launch the accessibility service, or could it have other functionality within the service such as to do specific tasks? I am curious possible uses for the accessibility and the getAccessibilityButtonController methods. Thank you for your time.
It can do pretty much anything you want it to. From the android accessibility doc, the button allows you to register a callback that has an onClicked method. If you enable the button and provide said callback you can execute whatever you'd like in the context of that callback.
Edit: The android documentation has been updated so the following should no longer be necessary.
Note that if you read the doc there's currently an example that has a call to getAccessibilityButtonController() within onCreate(). This is incorrect because the controller isn't valid until onServiceConnected is called. I've modified the example below to show something that should work.
private AccessibilityButtonController mAccessibilityButtonController;
private AccessibilityButtonController
.AccessibilityButtonCallback mAccessibilityButtonCallback;
private boolean mIsAccessibilityButtonAvailable;
#Override
protected void onServiceConnected() {
mAccessibilityButtonController = getAccessibilityButtonController();
mIsAccessibilityButtonAvailable =
mAccessibilityButtonController.isAccessibilityButtonAvailable();
if (!mIsAccessibilityButtonAvailable) {
return;
}
AccessibilityServiceInfo serviceInfo = getServiceInfo();
serviceInfo.flags
|= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
setServiceInfo(serviceInfo);
mAccessibilityButtonCallback =
new AccessibilityButtonController.AccessibilityButtonCallback() {
#Override
public void onClicked(AccessibilityButtonController controller) {
Log.d("MY_APP_TAG", "Accessibility button pressed!");
// Add custom logic for a service to react to the
// accessibility button being pressed.
}
#Override
public void onAvailabilityChanged(
AccessibilityButtonController controller, boolean available) {
if (controller.equals(mAccessibilityButtonController)) {
mIsAccessibilityButtonAvailable = available;
}
}
};
if (mAccessibilityButtonCallback != null) {
mAccessibilityButtonController.registerAccessibilityButtonCallback(
mAccessibilityButtonCallback, null);
}
}
Related
I've just started coding my app which uses Accessibility Service. I'll explain my problem in detail.
Below is my onServiceConnected method of MyAccessibilityService class
protected void onServiceConnected() {
super.onServiceConnected();
AccessibilityServiceInfo info = getServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.WINDOWS_CHANGE_ADDED;
info.packageNames = new String[]
{THIRD_PARTY_APP_PACKAGE};
info.notificationTimeout = 100;
this.setServiceInfo(info);
}
The app is detecting events in onAccessibilityEvent() method
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
Toast.makeText(this, "Event Occured", Toast.LENGTH_SHORT).show();
}
Now when I open the third party app, I'm getting the Toast "Event occured". Now I close the app and when I open it again, the method is not called and I don't get any Toast. To make it working again, I have to disable the accessibility service of my app in my phone's Settings and again enable it.
I know I'm missing something and my only question is what should be the additional part of code or what modifications I need in order to detect the event every time I open the third party app?
Have you tried getting rid of the notification timeout? You probably don't need it, and it isn't the best-tested API.
I am aware about the changes introduced in Android 6.0/SDKVersion 23 regarding the run-time permission. There is already discussion and talks on this topic in the below post which talks about the various aspects of new permission model.
"How to request permissions from a Service in Android Marshmallow"
Google I/O 2015 - Android M Permissions
"Mother, May I?" Asking for Permissions (Android Dev Summit 2015)
After going thorough these article,I believe below is suggested to "workaround" (as Service doesnot have UI support)this problem.
Check permission checkSelfPermission() in the context of
Service.
Notify it to status bar in case the permission is denied.
User would now click on status bar which would launch a new
DialogActivity to request for permission when users press on the
notification.
I am not able to make the workable version of the suggestions provided to achieve this task. When we launch DialogActivity to ask for permission, it would be assigned and available for that activity. It would not be applicable for background service who had put it on status bar and even after this service would not be granted this permission(instead the permission would be given to DialogActivity).
Could somebody provide the input(possibly workable logic/code) so that I can make it workable(.i.e. execute the code in the context of Service which would be dependent on whether permission is granted or not).
My Scenario
Develop application which would send the SMS at regular interval
regarding the my current location.
I have designed the UI for this application as mentioned below. It had some settings parameters and also buttons which would start the main logic which is:
Start monitoring the current location and
Send the SMS to configured contact numbers.
I believe user would minimize this application( as it does not have any interesting logic apart from sending the SMS) and it should continue to work in background(.i.e. monitoring the location and sending the SMS). Hence I thought to use move this logic in Service instead of keeping it the Activity itself. If I keep these logic in Activity itself, it works as expected however for that user is suppose to keep the application in foreground all the time.
So far I have been able to achieve the following:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Define START/STOP button handles.
mStartApp = (ImageButton)findViewById(R.id.startApp);
mStopApp = (ImageButton)findViewById(R.id.stopApp);
//Write event handlers for above grabbed buttons.
mStartApp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent startService = new Intent(getApplicationContext(),
CurrentLocationTrackerService.class);
startService(startService);
}
});
mStopApp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent stopService = new Intent(getApplicationContext(),
CurrentLocationTrackerService.class);
stopService(stopService);
}
});
// Support for the ActionBar widgets menu in current activity.
Toolbar mToolBar = (Toolbar)findViewById(R.id.myToolbar);
if(mToolBar != null) {
setSupportActionBar(mToolBar);
}
}
//Other methods.....
}
public class CurrentLocationTrackerService extends Service {
public CurrentLocationTrackerService() { }
#Override
public void onCreate() { }
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int permissionLocationAccess = ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.ACCESS_FINE_LOCATION);
// If permissionLocationAccess is true execute further
// If permissionLocationAccess false(which would always as we are in Service and it is dangerous
// permission) we need to put some information to status bar. Post that user would click on that
// which would launch some new activity where UI of asking permission would be shown. But still
// Service would not get the permission( only new activity would have that permission but it is
// of no use here as I am planning to put these logic iside the Service as my app may not be always
// be in foreground)
}
}
Hope I have provided all detail regarding the problem and also my application context regarding why I require to model in this way.The real point over here is how to achieve this.
I am really stuck at this point and any help would be highly appreciated. Kindly let me know in case anything else is required from my side.
I am writing a Spell Check client using the sample code in the SDK as an example. I have the following code (not actual implementation, but an accurate sample representation):
public class HelloSpellChecker implements SpellCheckerSessionListener {
private SpellCheckerSession mSpellCheckService;
private void bindService() {
final TextServicesManager tsm = (TextServicesManager) getSystemService(
Context.TEXT_SERVICES_MANAGER_SERVICE);
mSpellCheckService = tsm.newSpellCheckerSession(null, null, this, true);
}
public void getSuggestions(String word) {
mSpellCheckService.getSuggestions(new TextInfo("tgis"), 3);
}
#Override
public void onGetSentenceSuggestions(final SentenceSuggestionsInfo[] arg0) {
Log.d(TAG, "onGetSentenceSuggestions");
// Process suggestions
}
}
What I want to know is will onGetSentenceSuggestions only be fired when my application calls getSuggestions, or will it be fired any time the system service receives a request to getSuggestions?
If it is the latter, what is the best way to ensure my app only processes suggestions which it requested?
I would say Android system service listener is localized through the session in this case.
onGetSentenceSuggestions method is fired by any request to getSuggestions method. However, you don't have to worry about processing suggestions which your app requested since the spellchecker session takes care of it. Your app only gets the suggestions requested by the session your app created to interact with the spell checker service.
Hope this helps.
References:
http://developer.android.com/reference/android/view/textservice/SpellCheckerSession.html
http://developer.android.com/guide/topics/text/spell-checker-framework.html
I am trying to build an application which uses NFC. The goal is to display a DialogFragment containing a button link to go the settings and change it manually and when the feature is enabled, disable the DialogFragment.
Problem: If the user enables/disables NFC using the icon in the pull down notifications tray , then the onPause/onResume doesn't get called and misses the condition entirely.
I am sure there is a receiver that I can register to instead and respond appropriately in real time. Any ideas, thoughts or reference will be greatly appreciated!
The following code checks if the state is enabled/disabled. I am also responding to it appropriately in the onResume event.
NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
NfcAdapter adapter = manager.getDefaultAdapter();
if(adapter != null && adapter.isEnabled()) {
detector = new NfcDetector(this);
detector.setListener(this);
onNfcFeatureFound();
}
else {
onNfcFeatureNotFound();
}
For others looking at this post, the code below will take the user directly into settings to enable/disable NFC:
startActivity(new Intent(android.provider.Settings.ACTION_NFC_SETTINGS));
Thought I should post the answer for other people looking for the same problem, since I wasn't able to find one easily.
Add the following code to your activities onCreate() method:
IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
this.registerReceiver(mReceiver, filter);
Inner private class declared within your activity (or anywhere else you like):
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) {
final int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,
NfcAdapter.STATE_OFF);
switch (state) {
case NfcAdapter.STATE_OFF:
break;
case NfcAdapter.STATE_TURNING_OFF:
break;
case NfcAdapter.STATE_ON:
break;
case NfcAdapter.STATE_TURNING_ON:
break;
}
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
// Remove the broadcast listener
this.unregisterReceiver(mReceiver);
}
// The following check needs to also be added to the onResume
#Override
protected void onResume()
super.onResume();
// Check for available NFC Adapter
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
if(adapter != null && adapter.isEnabled()) {
createNfcDetector();
//NFC is available on device, but disabled
}
else {
//NFC Is available and enabled
}
}
You can use ACTION_ADAPTER_STATE_CHANGED to receive a broadcast message when the state of the adapter changes, but that option is only available in API 18 and above. See this for the documentation.
For prior to 18, I don't know of a way to do this unfortunately.
Also, as an aside, the android.provider.Settings.ACTION_NFC_SETTINGS will work on API levels 16 and above. For prior versions, the NFC settings are under "wireless settings". Take a look at the ensureSensorIsOn method at the bottom of this blog post for a code sample that checks against the API level and redirects to the correct settings pane.
In the Android app that I'm working on, I'd like to be able to detect when a new status bar notification appears, regardless of if it was caused by my app. To be more specific, I want to count the number of notifications in a given time frame.
Is this even possible, and if so, how?
Actually, it is possible, I use it in my app.
For Android 4.2 and below:
You need to register an AccessibilityService and make sure the user enables the service.
Example for a service:
public class InstantMessenger extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
//Do something, eg getting packagename
final String packagename = String.valueOf(event.getPackageName());
}
}
#Override
protected void onServiceConnected() {
if (isInit) {
return;
}
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
setServiceInfo(info);
isInit = true;
}
#Override
public void onInterrupt() {
isInit = false;
}
}
Example for checking if your Service is activated
For Android 4.3 and above:
Use the Notification Listener API
The new Notification Listener API in Android 4.3 enables you to do this.
With this there is less need for the accessibility hack. It also allows you to dismiss notifications.