Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add more fields from AccessibilityNodeInfo #682

Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Provide node's a11y actions only is it's enabled in settings
ankahanets committed Jan 28, 2025
commit e77718d15ff6e2aa473841d380ffad1b9034bf2f
22 changes: 22 additions & 0 deletions app/src/main/java/io/appium/uiautomator2/model/BaseElement.java
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
import android.os.Bundle;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;

import androidx.annotation.Nullable;
import androidx.test.uiautomator.Direction;
@@ -269,4 +270,25 @@ public static String getExtrasAsString(AccessibilityNodeInfo nodeInfo) {
}
return TextUtils.join(EXTRAS_SEPARATOR, extras);
}

@Nullable
public static String getA11yActionsAsString(AccessibilityNodeInfo nodeInfo) {
StringBuilder actionsBuilder = new StringBuilder();
List<AccessibilityAction> actionList = nodeInfo.getActionList();
if (actionList.isEmpty()) {
return null;
}
for (AccessibilityAction action : actionList) {
String[] split = action.toString().split(" ");
if (split.length < 2) {
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we continue instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No... actually is not possible, but even if this happens - than means something strange and all the data is broken

}
actionsBuilder.append(split[1]);
Copy link
Contributor

@mykola-mokhnach mykola-mokhnach Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

append returns this, so you could chain these calls

Copy link
Contributor Author

@ankahanets ankahanets Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes... I think current variant looks a bit more clearly, but I can change if it's needed indeed

actionsBuilder.append(",");
}
if (actionsBuilder.length() > 0) {
actionsBuilder.deleteCharAt(actionsBuilder.length() - 1);
}
return actionsBuilder.toString();
}
}
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@

import io.appium.uiautomator2.core.AxNodeInfoHelper;
import io.appium.uiautomator2.model.settings.AllowInvisibleElements;
import io.appium.uiautomator2.model.settings.IncludeA11yActionsInPageSource;
import io.appium.uiautomator2.model.settings.IncludeExtrasInPageSource;
import io.appium.uiautomator2.model.settings.SnapshotMaxDepth;
import io.appium.uiautomator2.model.settings.Settings;
@@ -166,25 +167,21 @@ private static void putAttribute(Map<Attribute, Object> attribs, Attribute key,
case HINT:
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? node.getHintText() : null;
case IMPORTANT_FOR_ACCESSIBILITY:
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? node.isImportantForAccessibility() : null;
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? node.isImportantForAccessibility()
: null;
case SCREEN_READER_FOCUSABLE:
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? node.isScreenReaderFocusable() : null;
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
? node.isScreenReaderFocusable()
: null;
case INPUT_TYPE:
return node.getInputType() != 0 ? node.getInputType() : null;
case DRAWING_ORDER:
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? node.getDrawingOrder() : null;
case SHOWING_HINT_TEXT:
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? node.isShowingHintText() : null;
case ACTIONS:
StringBuilder actionsBuilder = new StringBuilder();
List<AccessibilityNodeInfo.AccessibilityAction> actionList = node.getActionList();
for (AccessibilityNodeInfo.AccessibilityAction action : actionList) {
actionsBuilder.append(action.toString().split(" ")[1]);
actionsBuilder.append(",");
}
if(actionsBuilder.length() > 0)
actionsBuilder.deleteCharAt(actionsBuilder.length()-1);
return actionsBuilder.toString();
return BaseElement.getA11yActionsAsString(node);
case TEXT_ENTRY_KEY:
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? node.isTextEntryKey() : null;
case MULTI_LINE:
@@ -198,15 +195,19 @@ private static void putAttribute(Map<Attribute, Object> attribs, Attribute key,
case LIVE_REGION:
return node.getLiveRegion();
case CONTEXT_CLICKABLE:
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? node.isContextClickable() : null;
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
? node.isContextClickable()
: null;
case MAX_TEXT_LENGTH:
return node.getMaxTextLength() != -1 ? node.getMaxTextLength() : null;
case CONTENT_INVALID:
return node.isContentInvalid();
case ERROR_TEXT:
return charSequenceToNullableString(node.getError());
case PANE_TITLE:
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? charSequenceToNullableString(node.getPaneTitle()) : null;
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
? charSequenceToNullableString(node.getPaneTitle())
: null;
case EXTRAS:
return BaseElement.getExtrasAsString(node);
case ORIGINAL_TEXT:
@@ -229,6 +230,10 @@ private Map<Attribute, Object> collectAttributes() {
!Settings.get(IncludeExtrasInPageSource.class).getValue()) {
continue;
}
if (attr.equals(Attribute.ACTIONS) &&
!Settings.get(IncludeA11yActionsInPageSource.class).getValue()) {
continue;
}
if (includedAttributes.isEmpty() || includedAttributes.contains(attr)) {
putAttribute(result, attr, getNodeAttributeValue(attr));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.uiautomator2.model.settings;

public class IncludeA11yActionsInPageSource extends AbstractSetting<Boolean> {
private static final String SETTING_NAME = "includeA11yActionsInPageSource";
Copy link
Contributor

@mykola-mokhnach mykola-mokhnach Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also do not forget this new setting into UIA2 settings documentation after this PR is released

private static final boolean DEFAULT_VALUE = false;
private boolean includeA11yActionsInPageSource = DEFAULT_VALUE;

public IncludeA11yActionsInPageSource() {
super(Boolean.class, SETTING_NAME);
}

@Override
public Boolean getValue() {
return includeA11yActionsInPageSource;
}

@Override
public Boolean getDefaultValue() {
return DEFAULT_VALUE;
}

@Override
protected void apply(Boolean value) {
this.includeA11yActionsInPageSource = value;
}
}
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ public enum Settings {
DISABLE_IDS_AUTOCOMPLETION(new DisableIdLocatorAutocompletion()),
LIMIT_XPATH_CONTEXT_SCOPE(new LimitXpathContextScope()),
INCLUDE_EXTRAS_IN_PAGE_SOURCE(new IncludeExtrasInPageSource()),
INCLUDE_A11Y_ACTIONS_IN_PAGE_SOURCE(new IncludeA11yActionsInPageSource()),
SHUTDOWN_ON_POWER_DISCONNECT(new ShutdownOnPowerDisconnect()),
SIMPLE_BOUNDS_CALCULATION(new SimpleBoundsCalculation()),
TRACK_SCROLL_EVENTS(new TrackScrollEvents()),
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ public enum Attribute {
CONTENT_SIZE(new String[]{"contentSize"}, true, false),

IMPORTANT_FOR_ACCESSIBILITY(new String[]{"a11y-important", "importantForAccessibility"}),
SCREEN_READER_FOCUSABLE(new String[]{"screenReaderFocusable"}),
SCREEN_READER_FOCUSABLE(new String[]{"screen-reader-focusable", "screenReaderFocusable"}),
INPUT_TYPE(new String[]{"input-type", "inputType"}),
DRAWING_ORDER(new String[]{"drawing-order", "drawingOrder"}),
SHOWING_HINT_TEXT(new String[]{"showing-hint", "showingHintText"}),
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@
import io.appium.uiautomator2.model.settings.CompressedLayoutHierarchy;
import io.appium.uiautomator2.model.settings.ElementResponseAttributes;
import io.appium.uiautomator2.model.settings.EnableNotificationListener;
import io.appium.uiautomator2.model.settings.IncludeA11yActionsInPageSource;
import io.appium.uiautomator2.model.settings.IncludeExtrasInPageSource;
import io.appium.uiautomator2.model.settings.KeyInjectionDelay;
import io.appium.uiautomator2.model.settings.MjpegBilinearFiltering;
@@ -62,6 +63,7 @@
import static io.appium.uiautomator2.model.settings.Settings.COMPRESSED_LAYOUT_HIERARCHY;
import static io.appium.uiautomator2.model.settings.Settings.ELEMENT_RESPONSE_ATTRIBUTES;
import static io.appium.uiautomator2.model.settings.Settings.ENABLE_NOTIFICATION_LISTENER;
import static io.appium.uiautomator2.model.settings.Settings.INCLUDE_A11Y_ACTIONS_IN_PAGE_SOURCE;
import static io.appium.uiautomator2.model.settings.Settings.INCLUDE_EXTRAS_IN_PAGE_SOURCE;
import static io.appium.uiautomator2.model.settings.Settings.KEY_INJECTION_DELAY;
import static io.appium.uiautomator2.model.settings.Settings.MJPEG_BILINEAR_FILTERING;
@@ -230,6 +232,11 @@ public void shouldBeAbleToReturnIncludeExtrasInPageSourceSetting() {
verifySettingIsAvailable(INCLUDE_EXTRAS_IN_PAGE_SOURCE, IncludeExtrasInPageSource.class);
}

@Test
public void shouldBeAbleToReturnIncludeA11yActionsInPageSourceSetting() {
verifySettingIsAvailable(INCLUDE_A11Y_ACTIONS_IN_PAGE_SOURCE, IncludeA11yActionsInPageSource.class);
}

private void verifySettingIsAvailable(Settings setting, Class<? extends AbstractSetting<?>> clazz) {
assertThat(updateSettings.getSetting(setting.toString()), instanceOf(clazz));
}