├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── A11yUtils
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── moba11y
│ │ │ └── androida11yutils
│ │ │ ├── A11yNodeInfo.java
│ │ │ ├── A11yNodeInfoMatcher.java
│ │ │ └── A11yNodeInfoMocked.java
│ └── res
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── moba11y
│ └── androida11yutils
│ ├── A11yNodeInfoMatcherTest.java
│ ├── A11yNodeInfoMockedTest.java
│ └── A11yNodeInfoTest.java
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 1.7
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/A11yUtils/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/A11yUtils/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | def getVersionCodeString = { ->
4 | def stdout = new ByteArrayOutputStream()
5 | exec {
6 | commandLine 'git', 'rev-list', '--all', '--count'
7 | standardOutput = stdout
8 | }
9 | return stdout.toString().trim()
10 | }
11 |
12 | def getVersionCode = { ->
13 | return Integer.parseInt(getVersionCodeString())
14 | }
15 |
16 | def getVersionName = { ->
17 | def stdout = new ByteArrayOutputStream()
18 |
19 | exec {
20 | commandLine 'git', 'describe', '--tags', '--abbrev=0'
21 | standardOutput = stdout
22 | }
23 |
24 | return stdout.toString().trim().concat("." + getVersionCodeString())
25 | }
26 |
27 | android {
28 | compileSdkVersion 25
29 | buildToolsVersion "25.0.1"
30 | defaultConfig {
31 | minSdkVersion 18
32 | targetSdkVersion 25
33 | versionCode getVersionCode()
34 | versionName getVersionName()
35 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
36 | }
37 | buildTypes {
38 | release {
39 | minifyEnabled false
40 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
41 | }
42 | }
43 | }
44 |
45 | dependencies {
46 | androidTestCompile 'com.android.support.test:runner:0.5'
47 | androidTestCompile 'com.android.support.test:rules:0.5'
48 | androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
49 | androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
50 |
51 | compile 'com.android.support:appcompat-v7:25.0.1'
52 |
53 | testCompile 'junit:junit:4.12'
54 | testCompile 'org.robolectric:robolectric:3.1'
55 | }
56 |
--------------------------------------------------------------------------------
/A11yUtils/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/chrismcmeeking/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/A11yUtils/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/A11yUtils/src/main/java/com/moba11y/androida11yutils/A11yNodeInfo.java:
--------------------------------------------------------------------------------
1 | package com.moba11y.androida11yutils;
2 |
3 | import android.graphics.Rect;
4 | import android.os.Build;
5 | import android.support.v4.view.ViewPager;
6 | import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
7 | import android.view.View;
8 | import android.view.accessibility.AccessibilityNodeInfo;
9 | import android.widget.Button;
10 | import android.widget.CheckBox;
11 | import android.widget.EditText;
12 | import android.widget.Switch;
13 |
14 | import java.util.ArrayList;
15 | import java.util.Comparator;
16 | import java.util.Iterator;
17 | import java.util.List;
18 |
19 | /**
20 | * Created by chrismcmeeking on 2/25/17.
21 | */
22 |
23 | public class A11yNodeInfo implements Iterable, Comparator {
24 |
25 | public static A11yNodeInfo wrap(AccessibilityNodeInfo node) {
26 | if (node == null) return null;
27 |
28 | return new A11yNodeInfo(node);
29 | }
30 |
31 | public static A11yNodeInfo wrap(AccessibilityNodeInfoCompat node) {
32 | if (node == null) return null;
33 | return new A11yNodeInfo(node);
34 | }
35 |
36 | private static final ArrayList> ACTIVE_CLASSES;
37 |
38 | static {
39 | ACTIVE_CLASSES = new ArrayList<>();
40 | ACTIVE_CLASSES.add(Button.class);
41 | ACTIVE_CLASSES.add(Switch.class);
42 | ACTIVE_CLASSES.add(CheckBox.class);
43 | ACTIVE_CLASSES.add(EditText.class);
44 | }
45 |
46 | public enum Actions {
47 |
48 | ACCESSIBILITY_FOCUS(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS),
49 | CLEAR_ACCESSIBILITY_FOCUS(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS),
50 | CLEAR_FOCUS(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS),
51 | CLEAR_SELECTION(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION),
52 | CLICK(AccessibilityNodeInfo.ACTION_CLICK),
53 | COLLAPSE(AccessibilityNodeInfo.ACTION_COLLAPSE),
54 | COPY(AccessibilityNodeInfo.ACTION_COPY),
55 | CUT(AccessibilityNodeInfo.ACTION_CUT),
56 | LONG_CLICK(AccessibilityNodeInfo.ACTION_LONG_CLICK),
57 | PASTE(AccessibilityNodeInfo.ACTION_PASTE),
58 | PREVIOUS_AT_MOVEMENT_GRANULARITY(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY),
59 | PREVIOUS_HTML_ELEMENT(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
60 |
61 | private final int mAndroidValue;
62 |
63 | Actions(int androidValue) {
64 | mAndroidValue = androidValue;
65 | }
66 |
67 | int getAndroidValue() {
68 | return mAndroidValue;
69 | }
70 | }
71 |
72 | private final AccessibilityNodeInfoCompat mNodeInfo;
73 |
74 | //A special constructor for testing.
75 | protected A11yNodeInfo() {
76 | mNodeInfo = null;
77 | }
78 |
79 | protected A11yNodeInfo(AccessibilityNodeInfo nodeInfo) {
80 | this(new AccessibilityNodeInfoCompat(nodeInfo));
81 | }
82 |
83 | protected A11yNodeInfo(AccessibilityNodeInfoCompat nodeInfoCompat) {
84 | if (nodeInfoCompat == null) throw new RuntimeException("Wrapping a null node doesn't make sense");
85 | mNodeInfo = nodeInfoCompat;
86 | }
87 |
88 | @Override public int compare(A11yNodeInfo lhs, A11yNodeInfo rhs) {
89 |
90 | int result;
91 |
92 | result = lhs.getSpeakableText().compareTo(rhs.getSpeakableText());
93 |
94 | if (result != 0) return result;
95 |
96 | Rect lhsRect = lhs.getBoundsInScreen();
97 | Rect rhsRect = rhs.getBoundsInScreen();
98 |
99 |
100 | if (result != 0) return result;
101 |
102 | if (lhsRect.top < rhsRect.top) return -1;
103 | else if (lhsRect.top > rhsRect.top) return 1;
104 |
105 | if (lhsRect.left < rhsRect.left) return -1;
106 | else if (lhsRect.left > rhsRect.left) return 1;
107 |
108 | if (lhsRect.right < rhsRect.right) return -1;
109 | else if (lhsRect.right > rhsRect.right) return 1;
110 |
111 | if (lhsRect.bottom < rhsRect.bottom) return -1;
112 | else if (lhsRect.bottom > rhsRect.bottom) return 1;
113 |
114 | if (result != 0) return result;
115 |
116 | return 0;
117 | }
118 |
119 | public List getActionList() {
120 | return mNodeInfo.getActionList();
121 | }
122 |
123 | public int getActions() {
124 | return mNodeInfo.getActions();
125 | }
126 |
127 | /**
128 | * Callbacks for iterating over the A11yNodeInfo heirarchy.
129 | */
130 | public interface OnVisitListener {
131 |
132 | /**
133 | * Called for every node during heirarchy traversals.
134 | * @param nodeInfo The node that work will be doneon.
135 | * @return Return true to stop traversing, false to continue.
136 | */
137 | boolean onVisit(A11yNodeInfo nodeInfo);
138 | }
139 |
140 | public boolean isActiveElement() {
141 | for (Class extends View> clazz : ACTIVE_CLASSES) {
142 | if (this.getClassName().equalsIgnoreCase(clazz.getName())) return true;
143 | }
144 |
145 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
146 | if (getActionList().contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)) return true;
147 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
148 | if (getActionList().contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK)) return true;
149 | }
150 |
151 | if (getActionList().contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)) return true;
152 | if (getActionList().contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_SELECT)) return true;
153 | }
154 |
155 | final int actions = getActions();
156 |
157 | return (actions & AccessibilityNodeInfo.ACTION_CLICK) != 0 ||
158 | (actions & AccessibilityNodeInfo.ACTION_LONG_CLICK) != 0 ||
159 | (actions & AccessibilityNodeInfo.ACTION_SELECT) != 0;
160 | }
161 |
162 | public boolean performAction(Actions action) {
163 | return mNodeInfo.performAction(action.getAndroidValue());
164 | }
165 |
166 | public AccessibilityNodeInfoCompat getAccessibilityNodeInfoCompat() {
167 | return mNodeInfo;
168 | }
169 |
170 | public Rect getBoundsInScreen() {
171 | Rect result = new Rect();
172 | mNodeInfo.getBoundsInScreen(result);
173 | return result;
174 | }
175 |
176 | public A11yNodeInfo getChild(final int i) {
177 |
178 | if (i >= mNodeInfo.getChildCount()) throw new IndexOutOfBoundsException();
179 |
180 | return new A11yNodeInfo(mNodeInfo.getChild(i));
181 | }
182 |
183 | public int getChildCount() {
184 | return mNodeInfo.getChildCount();
185 | }
186 |
187 | public String getClassName() {
188 | return mNodeInfo.getClassName().toString();
189 | }
190 |
191 | public CharSequence getContentDescription() {
192 | return mNodeInfo.getContentDescription();
193 | }
194 |
195 | /**
196 | * I don't often use CharSequence's, and prefer strings. Note: null strings will return as empty strings!
197 | * @return The content descriptiong as a NotNull String.
198 | */
199 | public String getContentDescriptionAsString() {
200 | if (mNodeInfo.getContentDescription() == null) return "";
201 |
202 | return mNodeInfo.getContentDescription().toString();
203 | }
204 |
205 | /**
206 | * Gets the depth of the child in the node info heirarchy.
207 | * @return The depth of the node.
208 | */
209 | public int getDepthInTree() {
210 |
211 | int result = 0;
212 |
213 | A11yNodeInfo parentNode = getParent();
214 |
215 | while (parentNode != null) {
216 | parentNode = parentNode.getParent();
217 | result++;
218 | }
219 |
220 | return result;
221 | }
222 |
223 | public A11yNodeInfo getLabeledBy() {
224 | return A11yNodeInfo.wrap(mNodeInfo.getLabeledBy());
225 | }
226 |
227 | public A11yNodeInfo getParent() {
228 | if (mNodeInfo.getParent() == null) return null;
229 |
230 | return new A11yNodeInfo(mNodeInfo.getParent());
231 | }
232 |
233 | /**
234 | * Attempts to calculate the string that will be read off by TalkBack for a given
235 | * accessibility node. Eventually including role, trait, and value information.
236 | * If null, returns an empty string instead.
237 | *
238 | * @return The string representing the spoken text.
239 | */
240 | public String getSpeakableText() {
241 | //Todo: Use Eyes Free project??? Or make this more advanced.
242 | if (getContentDescription() != null) return getContentDescriptionAsString();
243 | if (getText() != null) return getTextAsString();
244 | return "";
245 | }
246 |
247 | public CharSequence getText() {
248 | return mNodeInfo.getText();
249 | }
250 |
251 | /**
252 | * Don't like CharSequences, and random null string checks. This will get the Text
253 | * as a NotNull String.
254 | * @return The text as a NotNull String.
255 | */
256 | public String getTextAsString() {
257 | if (getText() != null) return getText().toString();
258 | else return "";
259 | }
260 |
261 | public String getViewIdResourceName() {
262 | if (mNodeInfo.getViewIdResourceName() == null) return "";
263 | return mNodeInfo.getViewIdResourceName();
264 | }
265 |
266 | /**
267 | * Implenting the iterable interface to more easily navigate the node infos children.
268 | * @return An itarator over the children of this A11yNodeInfo.
269 | */
270 | @Override public Iterator iterator() {
271 | return new Iterator() {
272 | private int mNextIndex = 0;
273 |
274 | @Override
275 | public boolean hasNext() {
276 | //ChildCount isn't always accurate. Nodes may get recycled depending on the vent.
277 | //So we check the child count AND that the child isn't null.
278 | return mNextIndex < getChildCount() && (mNodeInfo == null || mNodeInfo.getChild(mNextIndex) != null);
279 | }
280 |
281 | @Override
282 | public A11yNodeInfo next() {
283 | return getChild(mNextIndex++);
284 | }
285 |
286 | @Override
287 | public void remove() {
288 |
289 | }
290 | };
291 | }
292 |
293 | /**
294 | * Get the entire node heirarchy as a string.
295 | * @return The node heirarchy.
296 | */
297 | public String toViewHeirarchy() {
298 | final StringBuilder result = new StringBuilder();
299 |
300 | result.append("--------------- Accessibility Node Hierarchy ---------------\n");
301 |
302 | visitNodes(new A11yNodeInfo.OnVisitListener() {
303 | @Override
304 | public boolean onVisit(A11yNodeInfo nodeInfo) {
305 |
306 | for (int i = 0; i < nodeInfo.getDepthInTree(); i++) {
307 | result.append('-');
308 | }
309 |
310 | result.append(nodeInfo.toString());
311 | result.append('\n');
312 |
313 | return false;
314 | }
315 | });
316 |
317 | result.append("--------------- Accessibility Node Hierarchy ---------------");
318 |
319 | return result.toString();
320 | }
321 |
322 | /**
323 | * Get the first {@link A11yNodeInfo node} that matches the given {@link A11yNodeInfoMatcher matcher}
324 | * @param matcher The matcher with props to match.
325 | * @return The first node that matches.
326 | */
327 | public A11yNodeInfo getFirstNodeThatMatches(final A11yNodeInfoMatcher matcher) {
328 | return visitNodes(new OnVisitListener() {
329 | @Override
330 | public boolean onVisit(A11yNodeInfo nodeInfo) {
331 | return matcher.match(nodeInfo);
332 | }
333 | });
334 | }
335 |
336 | public boolean isClassType(Class> clazz) {
337 | return (clazz.getName().equalsIgnoreCase(getClassName()));
338 | }
339 |
340 | public boolean isScrollable() {
341 | return mNodeInfo.isScrollable();
342 | }
343 |
344 |
345 | public boolean isVisibleToUser() {
346 | return mNodeInfo.isVisibleToUser();
347 | }
348 |
349 | public boolean isInVisibleScrollableField() {
350 | A11yNodeInfo tempNode = wrap(mNodeInfo);
351 | A11yNodeInfo scrollableView = null;
352 |
353 | while(tempNode.getParent() != null) {
354 | if(tempNode.isScrollable() && !tempNode.isClassType(ViewPager.class)) {
355 | scrollableView = tempNode;
356 | }
357 | tempNode = tempNode.getParent();
358 | }
359 |
360 | return scrollableView != null && scrollableView.isVisibleToUser();
361 | }
362 |
363 |
364 | @Override public String toString() {
365 | if (mNodeInfo == null) throw new RuntimeException("This shouldn't be null");
366 | return mNodeInfo.toString();
367 | }
368 |
369 | /**
370 | * Loop over children in the node heirarchy, until one of them returns true. Return the
371 | * first element where "onVisit" returns true. This can be used to create a very
372 | * simple "find first" type of method. Though most of the time, you likely want
373 | * to travel all, in which case, just return "false" from your onVisit method, and
374 | * you will visit every node.
375 | * @param onVisitListener {@link A11yNodeInfo.OnVisitListener#onVisit(A11yNodeInfo) onVisit}
376 | * will be alled for every node, until {@link A11yNodeInfo.OnVisitListener#onVisit(A11yNodeInfo) onVisit}
377 | * returns true.
378 | * @return The first node for which {@link A11yNodeInfo.OnVisitListener#onVisit(A11yNodeInfo) onVisit} returns true.
379 | */
380 | public A11yNodeInfo visitNodes(OnVisitListener onVisitListener) {
381 |
382 | if (onVisitListener.onVisit(this)) return this;
383 |
384 | for (A11yNodeInfo child : this) {
385 | A11yNodeInfo result = child.visitNodes(onVisitListener);
386 | if (result != null) return result;
387 | }
388 |
389 | return null;
390 | }
391 | }
392 |
--------------------------------------------------------------------------------
/A11yUtils/src/main/java/com/moba11y/androida11yutils/A11yNodeInfoMatcher.java:
--------------------------------------------------------------------------------
1 | package com.moba11y.androida11yutils;
2 |
3 | import android.graphics.Rect;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by chrismcmeeking on 2/27/17.
8 | */
9 |
10 | public class A11yNodeInfoMatcher {
11 |
12 | private String mContentDescription;
13 | private String mText;
14 | private Class extends View> mClass;
15 |
16 | private Rect mContainedIn = null;
17 | private Rect mPositionEqual = null;
18 |
19 | private String mViewIdResourceName = "";
20 |
21 | public A11yNodeInfoMatcher() {}
22 |
23 | public A11yNodeInfoMatcher setContentDescription(final String contentDescription) {
24 | mContentDescription = contentDescription;
25 | return this;
26 | }
27 |
28 | public A11yNodeInfoMatcher setClass(final Class extends View> clazz) {
29 | mClass = clazz;
30 | return this;
31 | }
32 |
33 | public A11yNodeInfoMatcher setPositionContainedIn(final Rect rect) {
34 | mContainedIn = rect;
35 | return this;
36 | }
37 |
38 | public A11yNodeInfoMatcher setPositionEqualTo(final Rect rect) {
39 | mPositionEqual = rect;
40 | return this;
41 | }
42 |
43 | public A11yNodeInfoMatcher setText(final String text) {
44 | mText = text;
45 | return this;
46 | }
47 |
48 | public A11yNodeInfoMatcher setViewIdResourceName(final String viewIdResourceName) {
49 | mViewIdResourceName = viewIdResourceName;
50 | return this;
51 | }
52 |
53 | public boolean match(A11yNodeInfo nodeInfo) {
54 |
55 | Rect position = nodeInfo.getBoundsInScreen();
56 |
57 | if (mContainedIn != null) {
58 | if (position.top < mContainedIn.top) return false;
59 | if (position.left < mContainedIn.left) return false;
60 | if (position.right > mContainedIn.right) return false;
61 | if (position.bottom > mContainedIn.bottom) return false;
62 | }
63 |
64 | if (mPositionEqual != null) {
65 | if (position.top != mPositionEqual.top) return false;
66 | if (position.bottom != mPositionEqual.bottom) return false;
67 | if (position.left != mPositionEqual.left) return false;
68 | if (position.right != mPositionEqual.right) return false;
69 | }
70 |
71 | if (mContentDescription != null &&
72 | (nodeInfo.getContentDescription() == null
73 | || !mContentDescription.contentEquals(nodeInfo.getContentDescription()))) return false;
74 |
75 | if (mText != null && (nodeInfo.getText() == null || !mText.contentEquals(nodeInfo.getText()))) return false;
76 |
77 | if (mClass != null && !mClass.getName().contentEquals(nodeInfo.getClassName())) return false;
78 |
79 | if (!nodeInfo.getViewIdResourceName().contains(mViewIdResourceName)) return false;
80 |
81 | return true;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/A11yUtils/src/main/java/com/moba11y/androida11yutils/A11yNodeInfoMocked.java:
--------------------------------------------------------------------------------
1 | package com.moba11y.androida11yutils;
2 |
3 | import android.graphics.Rect;
4 | import android.view.View;
5 |
6 | import java.util.ArrayList;
7 |
8 | /**
9 | * Created by chrismcmeeking on 2/27/17.
10 | */
11 |
12 | class A11yNodeInfoMocked extends A11yNodeInfo {
13 |
14 | private ArrayList mChildren = new ArrayList<>();
15 |
16 | private String mContentDescription;
17 |
18 | private A11yNodeInfoMocked mParent = null;
19 |
20 | private String mText;
21 |
22 | private Class extends View> mClass;
23 |
24 | private Rect mBounds = new Rect();
25 |
26 | private String mViewIdResourceName = "";
27 |
28 | public static A11yNodeInfoMocked create() { return new A11yNodeInfoMocked();}
29 |
30 | A11yNodeInfoMocked addChild(A11yNodeInfoMocked child) {
31 | mChildren.add(child);
32 |
33 | child.setParent(this);
34 |
35 | return this;
36 | }
37 |
38 | A11yNodeInfoMocked addChildren(Iterable children) {
39 | for (A11yNodeInfoMocked child : children) {
40 | addChild(child);
41 | }
42 |
43 | return this;
44 | }
45 |
46 | @Override
47 | public Rect getBoundsInScreen() {
48 | return mBounds;
49 | }
50 |
51 | @Override
52 | public A11yNodeInfoMocked getChild(final int i) {
53 |
54 | return mChildren.get(i);
55 | }
56 |
57 |
58 | @Override
59 | public int getChildCount() {
60 | return mChildren.size();
61 | }
62 |
63 | @Override
64 | public String getClassName() {
65 | if (mClass == null) return "";
66 | return mClass.getName();
67 | }
68 |
69 | @Override
70 | public CharSequence getContentDescription() {
71 | return mContentDescription;
72 | }
73 |
74 | @Override
75 | public A11yNodeInfoMocked getParent() {
76 | return mParent;
77 | }
78 |
79 | @Override
80 | public CharSequence getText() { return mText;}
81 |
82 | @Override
83 | public String getViewIdResourceName() {
84 | return mViewIdResourceName;
85 | }
86 |
87 | A11yNodeInfoMocked setBoundsInScreen(Rect bounds) {
88 | mBounds = bounds;
89 | return this;
90 | }
91 |
92 | A11yNodeInfoMocked setClass(Class extends View> clazz) {
93 | mClass = clazz;
94 | return this;
95 | }
96 |
97 | A11yNodeInfoMocked setContentDescription(String contentDescription) {
98 | mContentDescription = contentDescription;
99 | return this;
100 | }
101 |
102 | private A11yNodeInfoMocked setParent(A11yNodeInfoMocked parent) {
103 | mParent = parent;
104 | return this;
105 | }
106 |
107 | A11yNodeInfoMocked setText(String text) {
108 | mText = text;
109 | return this;
110 | }
111 |
112 | A11yNodeInfoMocked setViewIdResourceName(final String viewIdResourceName) {
113 | mViewIdResourceName = viewIdResourceName;
114 | return this;
115 | }
116 |
117 | @Override
118 | public String toString() {
119 | return "Hashcode: " + Integer.toString(this.hashCode());
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/A11yUtils/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/A11yUtils/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidA11yUtils
3 |
4 |
--------------------------------------------------------------------------------
/A11yUtils/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/A11yUtils/src/test/java/com/moba11y/androida11yutils/A11yNodeInfoMatcherTest.java:
--------------------------------------------------------------------------------
1 | package com.moba11y.androida11yutils;
2 |
3 | import android.widget.Button;
4 | import android.widget.TextView;
5 |
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.robolectric.RobolectricGradleTestRunner;
9 | import org.robolectric.annotation.Config;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Created by chrismcmeeking on 2/28/17.
15 | */
16 | @RunWith(RobolectricGradleTestRunner.class)
17 | @Config(constants = com.moba11y.androida11yutils.BuildConfig.class)
18 | public class A11yNodeInfoMatcherTest {
19 | @Test
20 | public void setText() throws Exception {
21 | A11yNodeInfoMocked nodeInfoMocked = new A11yNodeInfoMocked();
22 | A11yNodeInfoMatcher nodeInfoMatcher = new A11yNodeInfoMatcher();
23 |
24 | String text = "Hi there bob!";
25 |
26 | nodeInfoMatcher.setText(text);
27 |
28 | assertFalse("Matcher does not match a null text.", nodeInfoMatcher.match(nodeInfoMocked));
29 | nodeInfoMocked.setText(text);
30 |
31 | assertTrue("Matcher returns a match.", nodeInfoMatcher.match(nodeInfoMocked));
32 |
33 | nodeInfoMocked.setText("Not matching");
34 |
35 | assertFalse("Matcher does not match.", nodeInfoMatcher.match(nodeInfoMocked));
36 | }
37 |
38 | @Test
39 | public void setContentDescription() throws Exception {
40 | A11yNodeInfoMocked nodeInfoMocked = new A11yNodeInfoMocked();
41 | A11yNodeInfoMatcher nodeInfoMatcher = new A11yNodeInfoMatcher();
42 |
43 | String text = "Hi there bob!";
44 |
45 | nodeInfoMatcher.setContentDescription(text);
46 | assertFalse("Matcher does not match a null text.", nodeInfoMatcher.match(nodeInfoMocked));
47 |
48 | nodeInfoMocked.setContentDescription(text);
49 | assertTrue("Matcher returns a match.", nodeInfoMatcher.match(nodeInfoMocked));
50 |
51 | nodeInfoMocked.setContentDescription("Not matching");
52 | assertFalse("Matcher does not match.", nodeInfoMatcher.match(nodeInfoMocked));
53 | }
54 |
55 | @Test
56 | public void setClass() throws Exception {
57 | A11yNodeInfoMocked nodeInfoMocked = new A11yNodeInfoMocked();
58 | A11yNodeInfoMatcher nodeInfoMatcher = new A11yNodeInfoMatcher();
59 |
60 | nodeInfoMatcher.setClass(TextView.class);
61 | nodeInfoMocked.setClass(TextView.class);
62 | assertTrue("Same class matches.", nodeInfoMatcher.match(nodeInfoMocked));
63 |
64 | nodeInfoMatcher.setClass(Button.class);
65 | assertFalse("Different classes don't match.", nodeInfoMatcher.match(nodeInfoMocked));
66 | }
67 |
68 | @Test
69 | public void match() throws Exception {
70 | A11yNodeInfoMocked nodeInfoMocked = new A11yNodeInfoMocked();
71 | A11yNodeInfoMatcher nodeInfoMatcher = new A11yNodeInfoMatcher();
72 |
73 | assertTrue("Null everything matches.", nodeInfoMatcher.match(nodeInfoMocked));
74 |
75 | nodeInfoMocked.setContentDescription("Desc").setText("Text").setClass(Button.class);
76 | nodeInfoMatcher.setContentDescription("Desc").setText("Text").setClass(Button.class);
77 | assertTrue("Everythign set the same matches.", nodeInfoMatcher.match(nodeInfoMocked));
78 |
79 | }
80 | }
--------------------------------------------------------------------------------
/A11yUtils/src/test/java/com/moba11y/androida11yutils/A11yNodeInfoMockedTest.java:
--------------------------------------------------------------------------------
1 | package com.moba11y.androida11yutils;
2 |
3 | import android.widget.Button;
4 |
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.robolectric.RobolectricGradleTestRunner;
8 | import org.robolectric.annotation.Config;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | @RunWith(RobolectricGradleTestRunner.class)
13 | @Config(constants = com.moba11y.androida11yutils.BuildConfig.class)
14 | public class A11yNodeInfoMockedTest {
15 | @Test
16 | public void getClassName() throws Exception {
17 | assertEquals(Button.class.getName(), A11yNodeInfoMocked.create().setClass(Button.class).getClassName());
18 | }
19 | }
--------------------------------------------------------------------------------
/A11yUtils/src/test/java/com/moba11y/androida11yutils/A11yNodeInfoTest.java:
--------------------------------------------------------------------------------
1 | package com.moba11y.androida11yutils;
2 |
3 | import android.widget.Button;
4 | import android.widget.CheckBox;
5 | import android.widget.EditText;
6 | import android.widget.Switch;
7 |
8 | import org.junit.Assert;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.robolectric.RobolectricGradleTestRunner;
12 | import org.robolectric.annotation.Config;
13 |
14 |
15 | import static org.junit.Assert.*;
16 |
17 | /**
18 | * Created by chrismcmeeking on 2/27/17.
19 | */
20 | @RunWith(RobolectricGradleTestRunner.class)
21 | @Config(constants = com.moba11y.androida11yutils.BuildConfig.class)
22 | public class A11yNodeInfoTest {
23 | @Test
24 | public void getFirstNodeThatMatches() throws Exception {
25 | A11yNodeInfoMocked rootNode = A11yNodeInfoMocked.create();
26 | A11yNodeInfoMocked childNode = A11yNodeInfoMocked.create().setClass(Button.class);
27 | rootNode.addChild(childNode);
28 |
29 | assertEquals("It finds the child node.", childNode, rootNode.getFirstNodeThatMatches(new A11yNodeInfoMatcher().setClass(Button.class)));
30 | }
31 |
32 | @Test
33 | public void isActiveElement() throws Exception {
34 |
35 | assertTrue("Buttons are active elements.", A11yNodeInfoMocked.create().setClass(Button.class).isActiveElement());
36 |
37 | assertTrue("Switch views are active elements.", A11yNodeInfoMocked.create().setClass(Switch.class).isActiveElement());
38 |
39 | assertTrue("Buttons are active elements.", A11yNodeInfoMocked.create().setClass(CheckBox.class).isActiveElement());
40 |
41 | assertTrue("Buttons are active elements.", A11yNodeInfoMocked.create().setClass(EditText.class).isActiveElement());
42 |
43 |
44 | }
45 |
46 | @Test
47 | public void visitNodes() throws Exception {
48 | A11yNodeInfoMocked rootNodeInfoMocked = new A11yNodeInfoMocked();
49 |
50 | class CountingListener implements A11yNodeInfo.OnVisitListener {
51 | public int mVisitCount = 0;
52 | @Override
53 | public boolean onVisit(A11yNodeInfo nodeInfo) {
54 | mVisitCount++;
55 | return false;
56 | }
57 | }
58 |
59 | CountingListener listener = new CountingListener();
60 | rootNodeInfoMocked.visitNodes(listener);
61 |
62 | Assert.assertEquals("One node was visited.", 1, listener.mVisitCount);
63 |
64 | rootNodeInfoMocked.addChild(new A11yNodeInfoMocked());
65 | rootNodeInfoMocked.addChild(new A11yNodeInfoMocked());
66 | rootNodeInfoMocked.getChild(0).addChild(new A11yNodeInfoMocked());
67 |
68 | listener.mVisitCount = 0;
69 | rootNodeInfoMocked.visitNodes(listener);
70 |
71 | Assert.assertEquals("Visted the root node and 3 child nodes.", 4, listener.mVisitCount);
72 | }
73 |
74 | @Test
75 | public void getDepthInTree() throws Exception {
76 | A11yNodeInfoMocked rootNodeInfoMocked = new A11yNodeInfoMocked();
77 |
78 | Assert.assertEquals("A root node has depth of 0.", 0, rootNodeInfoMocked.getDepthInTree());
79 |
80 | rootNodeInfoMocked.addChild(new A11yNodeInfoMocked());
81 |
82 | Assert.assertEquals("A child node of the root node has a depth of 1", 1, rootNodeInfoMocked.getChild(0).getDepthInTree());
83 | }
84 |
85 | @Test
86 | public void getParent() throws Exception {
87 | A11yNodeInfoMocked rootNodeInfoMocked = new A11yNodeInfoMocked();
88 |
89 | Assert.assertEquals("The parent of a root node is null.", null, rootNodeInfoMocked.getParent());
90 |
91 | rootNodeInfoMocked.addChild(new A11yNodeInfoMocked());
92 |
93 | Assert.assertEquals("The parent of a child node, is the node itself.",
94 | rootNodeInfoMocked,
95 | rootNodeInfoMocked.getChild(0).getParent());
96 | }
97 |
98 | //The test belwo we include for completeness and to keep linter from complaining about a lack of tests for methods.
99 | @Test
100 | public void getContentDescription() throws Exception {
101 | assertTrue("Can't create realiable node info tests with Robolectric and testing a mocked object is silly!", true);
102 | }
103 |
104 | @Test
105 | public void getText() throws Exception {
106 | assertTrue("Can't create realiable node info tests with Robolectric and testing a mocked object is silly!", true);
107 | }
108 |
109 | @Test
110 | public void iterator() throws Exception {
111 | assertTrue("No tests", true);
112 | }
113 |
114 | @Test
115 | public void getChildAt() throws Exception {
116 | assertTrue("Can't create realiable node info tests with Robolectric and esting a mocked object is silly!", true);
117 | }
118 |
119 | @Test
120 | public void getChildCount() throws Exception {
121 | assertTrue("Can't create realiable node info tests with Robolectric and testing a mocked object is silly!", true);
122 | }
123 |
124 | @Test
125 | public void getClassName() throws Exception {
126 | assertTrue("Can't create realiable node info tests with Robolectric and testing a mocked object is silly!", true);
127 | }
128 |
129 | @Test
130 | public void toViewHeirarchy() throws Exception {
131 | assertTrue("No tests", true);
132 | }
133 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android-Accessibility-Utilities
2 | An Android library that aims to make the accessibility APIs less ambiguous, more streamlined, better documented, and all around better API design.
3 |
4 | The Android Accessibility APIs are a bit confusing. I find myself using a lot of different functions
5 | in a lot of the different accessibility services I work on. This repo contains (or will contain) utilties for
6 | Accessibility Node Infos (/compat), Accessibility Services, and other Android API related classes.
7 |
8 | Eventually this library will also provide the "recomended" spoken string, so that Accessibility services that aim
9 | to provide spoken feedback (like TalkBack) might fetch this information in the same way, despite providing
10 | other features!
11 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chriscm2006/Android-Accessibility-Utilities/4523a55b2a63442c4e90543a919d65c32a64a24c/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':A11yUtils'
2 |
--------------------------------------------------------------------------------