"Cannot perform this action on a not sealed instance" java.lang.IllegalStateException exception
Asked Answered
N

1

12

With android AccessibilityService able to paste in other app EditText Field, but with browser testfields (Emulator Default Browser or Samsung deault Browser) its not working, throwing error:

Cannot perform this action on a not sealed instance.

In android chrome browser with some singnup textfield its working but not for all textfields.

 @Override
public void onAccessibilityEvent(AccessibilityEvent event) {

    AccessibilityNodeInfo source = event.getSource();
    if (source != null && ( event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED ) ) {
            // || event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED ) &&
            //event.getClassName().equals("android.widget.EditText")
            //) {
        ctx = getApplicationContext();
        ClipboardManager clipboard = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("label", "XYZ");
        clipboard.setPrimaryClip(clip);
        source.performAction(AccessibilityNodeInfo.ACTION_PASTE); 
        //Not Working, always return false.

        //Tried with other options
        Bundle argumentsTest = new Bundle();
        argumentsTest.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "Bundle Test Data");
        source.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT,argumentsTest )
        // Not Working, throw java.lang.IllegalStateException exception
        //Message: "Cannot perform this action on a not sealed instance"
    }
}   
Niveous answered 26/9, 2015 at 9:32 Comment(0)
M
7

I don't beleive you're trying to do what you think you're trying to do.

When you set the "text" of an accessibilityNodeInfo, what you're chaning is the text property of that object, as it pertains to your accessibility service. THIS DOES NOT mean, that you are changing the text of the EditText box, that the accessibilityNodeInfo object references. By the time your accessibility service gets this object, the two objects are quite separate from each other. Even if your code were to run successfully, you would not be getting the results you are expecting. Now, as for why you cannot perform this action, knowing this it should be obvious. For an accessibility service to be able to modify the nodes it has, doesn't really make sense. So they become sealed (think of this as a run time enforcement of a constant). Accessibility nodes become sealed and unsealed at various points in their lifetime. The parts of the framework that have access to unsealed node infos are View classes and private APIs. Any accessibility service related tasks are going to be dealing with sealed, read-only instances.

As for why your original solution is not working, I believe that we do not have enough information. The "ACTION_PASTE" approach is (approximately) the correct approach, HOWEVER, there are an abundance of issues when doing so with web browsers. Browser version, Android version, device version, website, etc all play a role. Especially if your set up is old enough to not use the new WebView (pure chromium webview, rather than the old 4.+ approach of odd embedded mobile WebViews based on outdated versions of WebKit, which will now never be updated). I recommend testing your code on an up to date Nexus device, using at least Android 5.0 and seeing if your code works. If you cannot do this, report version information for your set up. If you already are, on what website?

Marymarya answered 28/9, 2015 at 18:59 Comment(9)
Do we have this thing working now or any other alternative?Wonacott
How does this have so many upvotes? The rant contained in this answer is incorrect: ACTION_SET_TEXT is, in fact, intended to allow accessibility services to change the text in the original widget -- performAction is a bridge to do that, provided your service has the appropriate permissions. Your answer also does not address the fact the node is not sealed yet/anymore, which is the real issue and prevents most actions (even just getting less trivial info through its API): Either the node is somehow not ready yet or already has been recycled() by something.Glowworm
You're misunderstanding the premise of the answer. Not once did I say that "ACTION_SET_TEXT" or "ACTIO_PASTE" was an incorrect way of doing any of this. But rather that the AccessibilityNodeInfo they had latched onto was the wrong one. An AccessibilityNodeInfo coming from an AccessiblityEvent is no longer a valid thing to perform actions on. Only an AccessibilityNodeInfo coming from a traversal of RootInActiveWindow is a valid Node to perform such actions on. It has so many upvotes because it is correct.Marymarya
As per your comments on the nodes readiness, you also are fundamentally misunderstanding this. It is niether somehow not ready nor has it been recycled. Nodes coming from AccessibilityEvents serve a fundamentally different purpose. When an AccessibilityService gets an event, that event is final. You can't change anything about it. Doing so would be fundamentally wrong. If you want to attempt to modify state of any application you must travers the view heirarchy from the root node, and perform actions on those nodes.Marymarya
Nothing in the question mentions directly editing a "text" member so yes, it sounded like you were saying that using ACTION_SET_TEXT was the wrong way to go. Why would an ANI from an event be "the wrong one"? It hasn't expired; It is loaded when requested with getSource(). If you use any of the find functions to load a child node, it will be valid as well. If the event is no longer valid / too old, you will get null. If an ANI isn't valid (has been recycled), you will get an exception with almost any operation, even reading most members of the ANI.Glowworm
Are these things you are saying specific to dealing with browsers?Glowworm
No. It's for Native Apps and Accessibility Services. When an Accessibility Service receives an AccessibilityEvent. When an Application sends a Service an AccessibilityEvent that event is Sealed. So you're correct, you won't get Null or anything else, and you can certainly perform actions on those nodes (like run functions). But, you cannot perform actions that attempt to manipulate the screen contents (such as manipulating text of EditTexts) from ANIs that you receive from events. You have to grab ANIs from node Heirarchy exploration, otherwise the nodes will be sealed.Marymarya
Sealed is essentially Android Operating System's way of enforcing a "constant" type of relationship. There are two types of ANI you can get. Sealed instances come from AccessibilityEvents and unsealed instances come from getRootInActiveWIndow() and recursively traversing the children therein. There is a much looser relationship between ANIs received from AccessibilityEvents and those received from a traversal of getRootInActiveWindow, particularly in relation to AccessibilityEvents that get sent frequently (picture quickly scrolling content).Marymarya
Modifying the text of these "Unsealed" instances is meaningless, because the virtual ANI is not tightly coupled to their actual on screen View objects. Any actions that you happen to be able to do, because Android doesn't enforce "Unsealed vs Sealed" properly, is coincidental and will not result in deterministically meaningful things occurring as a result.Marymarya

© 2022 - 2024 — McMap. All rights reserved.