I have a unique situation here. I am testing an app using Robotium and I am doing this under "black box" conditions. In my test, I have a tab titled 'all' and it is at the top of the screen, and I want to test that when it is clicked, all the available items are listed. What happens though, is instead of clicking the 'all' tab, the app titled "Advanced Call Manager" is being clicked. I assume that this is because 'all' is part of 'call' and due to the the way Robotium works, it clicks on 'all' even though it is part of 'call'. After you look at my code, you might understand what my issue is.
So my question is:
Is there a way to "reset" Robotium so that when it does its search for text, it starts at the top of the page? Here is my code:
solo.waitForText("all");
bw.write("Verify presence of 'all' filter on " + BUSINESS + "-COMMUNICATION page\",\"");
if(solo.searchText("all")==false){
bw.write("FAILED \",\" \'all\' filter not found \"\n\"");
}else if(solo.searchText("all")==true){
bw.write("PASSED \",\" \'all\' filter present\"\n\"");
bw.write("Verify functionality of 'all' filter\",\"");
solo.clickOnText("all");
solo.sleep(5000);
solo.takeScreenshot();
bw.write("Screenshot taken\",\"" +"See photo: "+ photoFormat.format(new Date()).toString()+"\"\n\"");
}
Any help would be appreciated!
The string passed to solo.clickOnText() is actually a RegEx. So I think you can solve your problem by passing a RegEx that matches 'all' but not 'call'.
ViewGroup class will be helpful in this case as you are using tabs in your code.Now find the view using getChildAt() method.
ViewGroup tabs = (ViewGroup) solo.getView(android.R.id.tabs);
View viewYouWantToDoStuffWith = tabs.getChildAt(x); //change x to the index you want.
Now you can perform the click event on tab like below.
solo.clickOnView(viewYouWantToDoStuffWith);
Related
I am able to detect which URL is being loaded in Chrome Custom Tab with help of accessibility service, and now i want to find id of back button so that i can close the tab if the url is in the block list, following is the code for click action:
List<AccessibilityNodeInfo> list = nodeInfo.
findAccessibilityNodeInfosByViewId("com.android.chrome:"id for back button"");
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: back_button " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
Everything about your code is reasonably accurate. The problem with the findAccessibilityNodeInfosByViewId function is not all views have a viewId. You're going to have to do your own crawl through the view hierarchy in order to accomplish this, and look for properties other than view ID.
If you want to see what the viewID is, you should use the Android Device Monitor Hierarchy Dump to get access to this, and also help understand other properties you might look at to see what else you can find that might be more consistent and not rely on an ID.
Here we see a typical screen dump from Android Device Monitor. As you can see the view ID for the highlighted view shows up in the information below IF it has one. Note also, that multiple views can share this ID! It is the ViewIdResourceName, NOT a unique ID.
I'm using a custom dynamic contentDescription for my textview, so it has been implemented in my java class and not in my xml file.
private void enableButton(TextView textView, boolean enabled) {
if (textView == null) {
return;
}
if (!enabled) {
textView.setContentDescription(textView.getResources().getString(R.string.install_disabled));
} else {
textView.setContentDescription(textView.getResources().getString(R.string.install));
}
textView.setEnabled(enabled);
}
After I'm enabling my textview to be clickable, and when talkback is enabled, focusing on my textview is announcing the state of my textview which is "disabled". Is there a way to not announce that state?
I do not want to set the accessibility to be not important because I still want my dynamic contentDescription to be recited when talkback users focus on the textview.
Suggestion:
I believe the culprit is the "setEnabled" method that is somehow triggering and announcing the state of the textview, but I'm still not able to stop it from reciting that last.
My first answer is: LEAVE IT ALONE! The "disabled" announcement tells a TalkBack user that there is a user interface control there, that under some circumstances can be interacted with, but is not currently active. Given your description, this is exactly what you have. To remove the announcement is actually going to make things WORSE from an accessibility perspective, the explanations for why this is the case are covered in WCAG 1.3.1.
Definitions:
Button = android.widget.Button
button = a user interface component that does something when you click it.
Text = a user interface component that conveys information, but is not active
Long story short, the fact that the control is ever in a state that it can be active and "not disabled" is significant information on its own, and SHOULD be shared with the user. ESPECIALLY since you're using a "TextView" to implement this. This isn't a super uncommon practice, but one of the ways TalkBack calculates roles (button, link, image, text, etc) is by looking at the Class/Type of object. So, when it sees a TextView, it is going to assume Text, unless you inform it otherwise. Now, since you have added click listeners to your TextView (or Gesture Recognizers, or whatever) TalkBack may be able to figure out that the thing you're dealing with is actually a "button", and it may not. REGARDLESS, the fact that this "button" (lower case B!) is not active is important state to share with the user, and communicates to them the fact that they can somehow enable it and come back and interact with it later. This is immensely important information! Imagine if every button/link on a WebPage looked exactly like plane text? How would you know what to interact with?
Now, I will show you the different pieces of this puzzle, as information, but I really do encourage you to leave the announcement alone. This is coming from someone who routinely speaks at Accessibility conferences on Native Android development, PLEASE LEAVE THIS ANNOUNCEMENT IN. To not do so shows a misunderstanding of how users with sight impairments want to perceive controls within your application, and the information that is important to them.
The setEnabled() function on a TextView corresponds directly with the isEnabled() property of AccessibilityNodeInfo.
In order to implement the behavior you want, you want the AccessibilityNodeInfo representation of your TextView to be different from that of the actual representation of your TextView. In order to do this you can use AccessibilityDelegate, I'm actually not sure which callback you want to use. In one of these the node is likely to be "sealed" and in one of them it might not be sealed yet. You obviously want the one where the node is not yet sealed! Regardless the general approach is:
textView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
#Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
// Let the default implementation populate the info.
super.onInitializeAccessibilityNodeInfo(host, info);
// Override this particular property
info.setEnabled(whateverYouThinkItShouldBe);
}
});
Use setClickable(false) to replace setEnabled(false) will solve this problem.
I am automating test cases for android app using Appium (java).
I am stuck on an interesting scenario:
My test case is to change the view of products by clicking on an icon.
That's done.
Now I need to verify that view has been changed or not, here is where the actual problem comes.
In both the cases the parent class is android.widget.GridView and subclass is android.widget.RelativeLayout - only the the way they appear on screen is changed (First they appeared in list type and later in grid type).
I am using UIAutomator for UI elements.How to detect this change?
Any help/ suggestion /work around would be greatful
UPDATE I am attaching the screenshots of UI Automator.
You know the result view what you going to get. Just verify if that element is visible or not.
List<WebElement> view = driver.findElements(By.className("android.view.View"));
System.out.println(view);
if (view.size() > 0 || view != null) {
viewFound = true;
}
I'm trying to implement an UIAutomator testcase with a general method to perform a click on a ListView item (regardless of the type of viewgroup holding the listitem).
Currently I have following code, but it keeps on clicking the first item.
public void clickListViewItem(int index) throws UiObjectNotFoundException {
UiObject listview = new UiObject(new UiSelector().className("android.widget.ListView"));
if(index <= listview.getChildCount()){
listview.getChild(new UiSelector().index(index)).click();
}else{
throw new UIObjectNotFoundException("Index is greater than listSize");
}
}
I got it to work with following code, it is based on the clickable attribute of an UISelector:
listview.getChild(new UiSelector().clickable(true).index(index)).click();
The developer page implements a similar scenario, found here - although this assumes there is some identifying feature that exists in the child by which to select (like in example below, a string "Apps"):
If more than one matching element is found, the first matching element in the layout hierarchy is returned as the target UiObject. When constructing a UiSelector, you can chain together multiple properties to refine your search. If no matching UI element is found, a UiAutomatorObjectNotFoundException is thrown.
You can use the childSelector() method to nest multiple UiSelector instances. For example, the following code example shows how your test might specify a search to find the first ListView in the currently displayed UI, then search within that ListView to find a UI element with the text property Apps.
val appItem: UiObject = device.findObject(
UiSelector().className("android.widget.ListView")
.instance(0)
.childSelector(
UiSelector().text("Apps")
)
)
I've got what I thought was a simple android UI design problem but I've been going around in circles for a couple of days. I have a REST service that I'm downloading XML from and displaying the XML in a form in an android app. I have a web page built and am mimicking this with android, same options, same URLs being sent to the REST service whether from android or the web pages. With HTML I can easily create checkbox groups and radiobutton/dropdowns for various id/display items, so for instance, I can display a planet option as:
<select name="planet"><option value="0">Mercury</option></select>
I wanted to do something similar in android where I had a pair of values, one an id and the other the user-friendly text to display. So I decided to create an adapter using android.util.Pair:
public class PairView extends Pair<String, String> {
public PairView(String first, String second) {
super(first, second);
}
public String toString() {
return second;
}
}
public class PairAdapter extends ArrayAdapter<PairView> {
}
So now I can put my id in pair.first and what to display to the user in pair.second.
My problem comes in that some of these options will be single-selects and some will be multi-selects. In html, that's not an issue, just use a checkbox group for multi, and radio buttons/dropdowns for single selects. In android however, it seems it's not so straight forward. I tried using Spinners for the adapters, but Spinner seems to only allow single selection. AlertDialog.Builder allows for single and multi-selections, but curiously I don't see an option for using an adapter for the multi-selection, just for single selections.
I guess what I really want is a consistent look for all my options, with radio buttons displayed for single selections and checkboxes displayed for multi selections, via an adapter so I can get the id's from the Pair for the items selected.
What approach should I use? A custom spinner with code added for multi-selections? AlertDialog.Builder and somehow make it use an adapter for multi-selections? Just create a plain Alert and wrap a ListView in it? Another option that is (hopefully) simpler?
I feel like I'm missing something very basic here.
I had a similar situation in an app I was making so would share what I opted for. I had different type of questions and depending on that I removed and added things in my activity. For radio buttons I used with elements in it. For multiple choice questions I wanted a checkbox based view so I added an empty within my layout and in code added CheckBox(s) to it.
As for the caption and value, for radio buttons and checkboxes you can set display text by setText and add any object/value as a tag. So what I used to do was something like this:
CheckBox option = new CheckBox(MyActivity.this);
option.setText("Option 1");
option.setTag(10);
Later on when you get the selected option, you can simply get its tag and use its value.
This is just one way of doing it which I found simple. Hope this helps