18

When I run my code on API22 it works just fine, pasting the "Testing Testing" in my required EditText in the app that initiated the AccessibilityEvent. But when I run it on API 17, it does not work. It copies the data to clip but is unable to paste it. I require the mechanism to work on API 16 and above.

This is my code so far:

public class MyAccessibilityService extends AccessibilityService {
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        AccessibilityNodeInfo nodeInf = null;
        AccessibilityNodeInfo nodeInfo = null;
        final int eventType = event.getEventType();
        String eventText = null;
        switch(eventType) {
            case AccessibilityEvent.TYPE_VIEW_CLICKED:
                eventText = "Clicked: ";
                nodeInf = this.getRootInActiveWindow();
                Log.d("AccessibilityNodeInfo", ""+ nodeInf.getChildCount());
                nodeInf.recycle();
                break;
            case AccessibilityEvent.TYPE_VIEW_FOCUSED:
                AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
                AccessibilityNodeInfoCompat source = record.getSource();

                ClipboardManager clipboard = (ClipboardManager) this.getSystemService(Context.CLIPBOARD_SERVICE);
                ClipData clip = ClipData.newPlainText("label", "TESTING TESTING");
                clipboard.setPrimaryClip(clip);

                source.performAction(AccessibilityNodeInfoCompat.ACTION_PASTE);
                //}

                Log.d("AccessibilityNodeInfo", ""+ source.getClassName());
                Intent intent = new Intent(MyAccessibilityService.this, TestActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
                startActivity(intent);
                break;
        }


        eventText = eventText + event.getText();

        // Do something nifty with this text, like speak the composed string
        // back to the user.
        Log.d("Information", eventText);
        Toast.makeText(getApplicationContext(), eventText + " " + android.os.Build.VERSION.SDK_INT,
                Toast.LENGTH_LONG).show();
    }
}
RehanZahoor
  • 322
  • 2
  • 12
  • Have you checked this question [AccessibilityService is started but does not receive AccessibilityEvents on JellyBean](http://stackoverflow.com/questions/12019848/accessibilityservice-is-started-but-does-not-receive-accessibilityevents-on-jell)? – Sufian May 07 '15 at 09:57

2 Answers2

8

Unfortunately AccessibilityNodeInfo.ACTION_PASTE is added in API level 18, for this reason it doesn't work with API 17 and lower. AccessibilityNodeInfoCompat is only a wrapper to existing features, it doesn't provide a custom implementation of missing features.

The sources of the v4 support library are pretty clear:

When you call performAction in AccessibilityNodeInfoCompat the support library calls IMPL.performAction [1]

public boolean performAction(int action) {
    return IMPL.performAction(mInfo, action);
}

IMPL is AccessibilityNodeInfoJellybeanImpl when API level is 16 and 17 [2]

if (Build.VERSION.SDK_INT >= 22) {
    IMPL = new AccessibilityNodeInfoApi22Impl();
} else if (Build.VERSION.SDK_INT >= 21) {
    IMPL = new AccessibilityNodeInfoApi21Impl();
} else if (Build.VERSION.SDK_INT >= 19) { // KitKat
    IMPL = new AccessibilityNodeInfoKitKatImpl();
} else if (Build.VERSION.SDK_INT >= 18) { // JellyBean MR2
    IMPL = new AccessibilityNodeInfoJellybeanMr2Impl();
} else if (Build.VERSION.SDK_INT >= 16) { // JellyBean
    IMPL = new AccessibilityNodeInfoJellybeanImpl();
} else if (Build.VERSION.SDK_INT >= 14) { // ICS
    IMPL = new AccessibilityNodeInfoIcsImpl();
} else {
    IMPL = new AccessibilityNodeInfoStubImpl();
}

This is performAction in AccessibilityNodeInfoJellybeanImpl [3]

public static boolean performAction(Object info, int action, Bundle arguments) {
    return ((AccessibilityNodeInfo) info).performAction(action, arguments);
}

As you can see the the support library call performAction of the standard android.view.accessibility.AccessibilityNodeInfo, so if the system doesn't support ACTION_PASTE also the v4 support library doesn't support ACTION_PASTE.

You can check if ACTION_PASTE is supported with this code:

AccessibilityNodeInfoCompat source = record.getSource();
int supportedActions = source.getActions();
boolean isSupported = (supportedActions & AccessibilityNodeInfoCompat.ACTION_PASTE) == AccessibilityNodeInfoCompat.ACTION_PASTE;
Log.d(TAG, String.format("AccessibilityNodeInfoCompat.ACTION_PASTE %1$s supported", isSupported ? "is" : "is NOT"));
Mattia Maestrini
  • 32,270
  • 15
  • 87
  • 94
  • The purpose of Compatibility Android library, is to allow the developer to produce features in his apps, that are not available in the previous version. So AccessibilityNodeInfoCompat should provide the features available after API18, in API 17 android.[AccessibilityNodeInfoCompat](http://developer.android.com/reference/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.html) documentation confirms to it – RehanZahoor May 07 '15 at 11:35
  • I know what is the purpose of Compatibility Android library, unfortunately this is not the case. As you can see in the link you just shared in the class overview _"Helper for accessing AccessibilityNodeInfo introduced after API level 4 in a backwards compatible fashion."_. AccessibilityNodeInfoCompat doesn't provide any extra functionality, helps you to access to existing functionality. – Mattia Maestrini May 07 '15 at 12:44
  • The code you provided to check whether ACTION_PASTE is supported does not work. Android Studio flags supportedActions. – RehanZahoor May 07 '15 at 17:13
  • I have checked by using getActions method of AccessibilityNodeInfoCompat. and it turns out that you are right, AccessibilityNodeInfoCompat just does not support the action for API less than 18. Thanks.. – RehanZahoor May 07 '15 at 17:36
2

Perhaps you should improve your answer and add some more details about your imports.


Pasting: As you mentioned in your comment, it's more about getting the thing from the clipboard again?

As of the Android Copy and Paste Documentation you can get the copied content as text like this:

// Examines the item on the clipboard. If getText() does not return null, the clip item contains the
// text. Assumes that this application can only handle one item at a time.
 ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);

// Gets the clipboard as text.
pasteData = item.getText();

// If the string contains data, then the paste operation is done
if (pasteData != null) {
    return;

// The clipboard does not contain text. If it contains a URI, attempts to get data from it
} else {
    Uri pasteUri = item.getUri();

    // If the URI contains something, try to get text from it
    if (pasteUri != null) {

        // calls a routine to resolve the URI and get data from it. This routine is not
        // presented here.
        pasteData = resolveUri(Uri);
        return;
    } else {

    // Something is wrong. The MIME type was plain text, but the clipboard does not contain either
    // text or a Uri. Report an error.
    Log.e("Clipboard contains an invalid data type");
    return;
    }
}

Copying:

There are two different variants of the ClipBoardManager. The new one was introduced with Honeycomb. You have to make sure that your code is using the correct variant.

Look at this code sample:

int sdk = android.os.Build.VERSION.SDK_INT;
if(sdk < android.os.Build.VERSION_CODES.HONEYCOMB) {
    android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    clipboard.setText("text to clip");
} else {
    android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); 
    android.content.ClipData clip = android.content.ClipData.newPlainText("text label","text to clip");
    clipboard.setPrimaryClip(clip);
}

This is just an assumption as the question misses some information.

mikepenz
  • 12,708
  • 14
  • 77
  • 117
  • Not a problem related with copy method. I want my code to work on API16 and above. HoneyComb is API11. Besides when I run my app and try pasting in say Browser Address field, it pastes what is pasted by the AccessibilityService. That is the AccessibilityService has copied required text on clipboard. and it is just a matter of 'source.performAction(AccessibilityNodeInfoCompat.ACTION_PASTE);' not working. – RehanZahoor May 07 '15 at 04:49
  • @RehanZahoor can you provide a small sample app which allows some testing? – mikepenz May 07 '15 at 09:34