Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Paste from AccessibilityService works in API22 does not work in API17

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();
    }
}
like image 673
RehanZahoor Avatar asked May 03 '15 22:05

RehanZahoor


2 Answers

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"));
like image 167
Mattia Maestrini Avatar answered Sep 20 '22 15:09

Mattia Maestrini


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.

like image 38
mikepenz Avatar answered Sep 21 '22 15:09

mikepenz