-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from google/talkback6
TalkBack 6.1 release
- Loading branch information
Showing
1,814 changed files
with
120,441 additions
and
134,035 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/* For building open-source release of accessibility services. */ | ||
|
||
apply plugin: 'com.android.library' | ||
|
||
android { | ||
compileSdkVersion "android-27" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:tools="http://schemas.android.com/tools" | ||
package="com.google.android.libraries.accessibility.utils"> | ||
</manifest> |
50 changes: 50 additions & 0 deletions
50
accessibility_utils/src/main/java/QualifiedComponentName.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.google.android.libraries.accessibility.utils; | ||
|
||
import android.content.ComponentName; | ||
|
||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* A class to create {@link ComponentName}s that always hold the fully qualified class name. | ||
*/ | ||
public class QualifiedComponentName { | ||
|
||
private static final Pattern QUALIFIED_CLASS_PATTERN = Pattern.compile( | ||
// starts with 1+ lowercase alphanumerics starting with a letter ending with a '.' | ||
"^([a-z][a-z0-9_]*\\.)+" | ||
+ "[A-Z][a-zA-Z0-9_]*$"); // ends with alphanumeric beginning with a capital letter | ||
|
||
private static final Pattern RELATIVE_CLASS_PATTERN = Pattern.compile( | ||
// starts with '.' then 0+ lowercase alphanumerics starting with a letter ending with a '.' | ||
"^\\.([a-z][a-z0-9_]*\\.)*" | ||
+ "[A-Z][a-zA-Z0-9_]*$"); // ends with alphanumeric beginning with a capital letter | ||
|
||
private static final Pattern SIMPLE_CLASS_PATTERN = Pattern.compile( | ||
"^[A-Z][a-zA-Z0-9_]*$"); // ends with alphanumeric beginning with a capital letter | ||
|
||
/** | ||
* Returns a {@link ComponentName} corresponding to the same component as the given | ||
* {@link ComponentName}, but with a fully qualified class name. If the class or package | ||
* name are empty or do not follow java naming scheme, this will return null. | ||
*/ | ||
public static ComponentName fromComponentName(ComponentName componentName) { | ||
String packageName = componentName.getPackageName(); | ||
String className = componentName.getClassName(); | ||
if (QUALIFIED_CLASS_PATTERN.matcher(className).matches()) { | ||
return componentName; | ||
} else if (RELATIVE_CLASS_PATTERN.matcher(className).matches()) { | ||
return new ComponentName(packageName, packageName + className); | ||
} else if (SIMPLE_CLASS_PATTERN.matcher(className).matches()) { | ||
return new ComponentName(packageName, packageName + "." + className); | ||
} | ||
return null; | ||
} | ||
|
||
public static ComponentName unflattenFromString(String str) { | ||
ComponentName nonqualifiedComponentName = ComponentName.unflattenFromString(str); | ||
if (nonqualifiedComponentName == null) { | ||
return null; | ||
} | ||
return fromComponentName(nonqualifiedComponentName); | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
accessibility_utils/src/main/java/ServiceUncaughtExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package com.google.android.libraries.accessibility.utils; | ||
|
||
import android.os.Handler; | ||
import android.os.Looper; | ||
|
||
import android.util.Log; | ||
|
||
import java.lang.Thread.UncaughtExceptionHandler; | ||
import java.util.concurrent.CountDownLatch; | ||
|
||
/** | ||
* An {@link UncaughtExceptionHandler} for use in Android services to allow for removal of UI | ||
* upon an uncaught exception from any thread. This should be set before any UI is displayed. | ||
*/ | ||
public abstract class ServiceUncaughtExceptionHandler implements UncaughtExceptionHandler { | ||
|
||
private static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = | ||
Thread.getDefaultUncaughtExceptionHandler(); | ||
private final Handler mHandler = new Handler(Looper.getMainLooper()); | ||
private final CountDownLatch mUncaughtExceptionLatch = new CountDownLatch(1); | ||
private final String mTag; | ||
|
||
/** | ||
* Create a {@code ServiceUncaughtExceptionHandler} with the default logcat tag. | ||
*/ | ||
public ServiceUncaughtExceptionHandler() { | ||
this(null); | ||
} | ||
|
||
/** | ||
* Create a {@code ServiceUncaughtExceptionHandler} with a custom logcat tag. | ||
* | ||
* @param tag The tag to use to log the uncaught exception to logcat. If {@code null}, this class' | ||
* simple name will be used. | ||
*/ | ||
public ServiceUncaughtExceptionHandler(String tag) { | ||
mTag = (tag == null) | ||
? ServiceUncaughtExceptionHandler.class.getSimpleName() | ||
: tag; | ||
} | ||
|
||
/** | ||
* Logs the caught {@link Throwable} to logcat, calls {@link #shutdown()} on the main thread, and | ||
* then re-throws the exception to the previous default {@link UncaughtExceptionHandler}. | ||
*/ | ||
@Override | ||
public void uncaughtException(Thread thread, Throwable throwable) { | ||
// Not all Android versions will print the stack trace automatically | ||
Log.e(mTag, "Uncaught exception thrown from thread: " + thread.getName(), throwable); | ||
|
||
if (Thread.currentThread() == Looper.getMainLooper().getThread()) { | ||
// Can't post to the main handler, as that looper is no longer looping due to the exception | ||
try { | ||
shutdown(); | ||
} catch (Throwable t) { | ||
// If we don't catch all here, we will just infinitely recurse uncaughtException() | ||
} | ||
} else { | ||
mHandler.post(new Runnable() { | ||
@Override | ||
public void run() { | ||
try { | ||
shutdown(); | ||
} catch (Throwable t) { | ||
// If we don't catch all here, we will just infinitely recurse uncaughtException() | ||
} finally { | ||
mUncaughtExceptionLatch.countDown(); | ||
} | ||
} | ||
}); | ||
while (mUncaughtExceptionLatch.getCount() > 0) { | ||
try { | ||
mUncaughtExceptionLatch.await(); | ||
} catch (InterruptedException e) { | ||
// Spurious wakeup, loop and await() again | ||
} | ||
} | ||
} | ||
|
||
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) { | ||
// re-throw the exception so that the android system knows the app has crashed | ||
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable); | ||
} | ||
} | ||
|
||
/** | ||
* This is called on the main thread before rethrowing the uncaught exception. All UI should be | ||
* removed in this method. | ||
*/ | ||
public abstract void shutdown(); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
package com.google.android.libraries.accessibility.utils; | ||
|
||
import java.util.Collections; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Locale; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* Library for string manipulations. Contains regular expressions and common operation helper | ||
* methods. | ||
*/ | ||
public final class StringUtils { | ||
|
||
/** | ||
* A regex pattern to group a single section of a string based on camel case. This will need to be | ||
* used in a loop to extract all sections of the string. This will work for Unicode character | ||
* blocks. Here are some examples: | ||
* | ||
* <ul> | ||
* <li>"thisIsCamelCase" => ["this", "Is", "Camel", "Case"] | ||
* <li>"30daysInJune" => ["30", "days", "In", "June"] | ||
* <li>"25DaysSinceJune10!!!" => ["25", "Days", "Since", "June", "10", "!!!"] | ||
* </ul> | ||
* | ||
* <p> | ||
* <br> | ||
* This regex has four categories of characters: upper case letters, lower case letters, numbers, | ||
* and symbols (including punctuation). All of these categories are grouped separately from one | ||
* another with the exception of upper case letters. A group cannot have more than one upper case | ||
* letter and can contain any number of lower case letters. | ||
* | ||
* <p> | ||
* <br> | ||
* Here are the sections of the regex: | ||
* | ||
* <br> | ||
* 1. "\\p{Ll}+" -> One or more lower case letters | ||
* | ||
* <br> | ||
* 2. "\\p{N}+" -> One or more numbers | ||
* | ||
* <br> | ||
* 3. "[\\p{S}\\p{P}]+" -> One or more symbols/punctuations | ||
* | ||
* <br> | ||
* 4. "\\p{Lu}\\p{Ll}*" -> Exactly one upper case letter followed by zero or more lower case | ||
* letters | ||
*/ | ||
public static final Pattern CAMEL_CASE_PATTERN = | ||
Pattern.compile("(\\p{Ll}+|\\p{N}+|[\\p{S}\\p{P}]+|\\p{Lu}\\p{Ll}*)"); | ||
|
||
/** | ||
* A regex pattern to match a single whitespace or invisible separator. Supports Unicode character | ||
* blocks. | ||
*/ | ||
private static final Pattern WHITESPACE_BLOCK_PATTERN = Pattern.compile("\\p{Z}+"); | ||
|
||
/** | ||
* A regex pattern to match a group of non-whitespace characters. Supports Unicode blocks. | ||
*/ | ||
private static final Pattern NOT_WHITESPACE_GROUP_PATTERN = Pattern.compile("(\\P{Z}+)"); | ||
|
||
private static final String SPACE_STRING = " "; | ||
|
||
private StringUtils() {} | ||
|
||
/** | ||
* Splits the input {@link String} based on whitespace and camel case. The resulting split | ||
* substrings will not contain any whitespace and will also maintain the original capitalization. | ||
* See {@value #CAMEL_CASE_PATTERN} for specific rules on camel case splitting and | ||
* {@link #splitOnSpace} for specific rules on whitespace splitting. | ||
* | ||
* @param str The {@link String} to be split | ||
* @return The substrings based off camel case and whitespace. If the input is empty (no | ||
* non-whitespace characters) or {@code null} then the result will be an immutable empty | ||
* list | ||
*/ | ||
public static List<String> splitOnCamelCase(String str) { | ||
if (isEmpty(str)) { | ||
return Collections.emptyList(); | ||
} | ||
|
||
List<String> result = new LinkedList<>(); | ||
for (final String word : splitOnSpace(str)) { | ||
final Matcher camelCaseMatcher = CAMEL_CASE_PATTERN.matcher(word); | ||
if (camelCaseMatcher.find()) { | ||
do { | ||
result.add(camelCaseMatcher.group()); | ||
} while (camelCaseMatcher.find()); | ||
} else { | ||
result.add(word); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* Equivalent to {@code splitOnSpace(str, 0)} | ||
*/ | ||
public static List<String> splitOnSpace(String str) { | ||
return splitOnSpace(str, 0); | ||
} | ||
|
||
/** | ||
* Splits the input {@link String} based on whitespace. The resulting split substrings will not | ||
* contain any whitespace and will also maintain the original capitalization. | ||
* | ||
* @param str The {@link String} to be split | ||
* @param limit Determines the maximum number of entries in the resulting array, and the treatment | ||
* of trailing empty strings | ||
* <ul> | ||
* <li>For n > 0, the resulting {@link List} contains at most n entries. If this is fewer | ||
* than the number of matches, the final entry will contain all remaining input | ||
* <li>For n <= 0, the length of the resulting {@link List} is exactly the number of words | ||
* in the input | ||
* </ul> | ||
* @return The substrings based off whitespace. If the input is empty (no non-whitespace | ||
* characters) or {@code null} then the result will be an immutable empty list | ||
*/ | ||
public static List<String> splitOnSpace(String str, int limit) { | ||
if (str == null) { | ||
return Collections.emptyList(); | ||
} | ||
List<String> words = new LinkedList<>(); | ||
Matcher matcher = NOT_WHITESPACE_GROUP_PATTERN.matcher(str); | ||
int count = 0; | ||
while (matcher.find()) { | ||
words.add(matcher.group()); | ||
if (++count == limit) { | ||
break; | ||
} | ||
} | ||
return words; | ||
} | ||
|
||
/** | ||
* Checks the input {@link String} to see if it contains any non-whitespace characters. | ||
* | ||
* @param str The {@link String} to be checked | ||
* @return {@code true} if the input is null or consists of zero or more whitespace characters; | ||
* {@code false} otherwise | ||
*/ | ||
public static boolean isEmpty(CharSequence cs) { | ||
if ((cs == null) || (cs.length() == 0)) { | ||
return true; | ||
} | ||
for (int i = 0; i < cs.length(); i++) { | ||
if (!Character.isWhitespace(cs.charAt(i))) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Collapses multiple spaces into a single space and removes leading/trailing whitespace. | ||
* | ||
* @param str The input {@link String} to normalize spaces | ||
* @return The input {@link String} with unnecessary whitespace removed or {@code null} if the | ||
* input is {@code null} | ||
*/ | ||
public static String normalizeSpaces(CharSequence str) { | ||
return (str == null) | ||
? null | ||
: WHITESPACE_BLOCK_PATTERN.matcher(str).replaceAll(SPACE_STRING).trim(); | ||
} | ||
|
||
/** | ||
* Capitalize the first letter of a string, in the specified locale. Supports Unicode. | ||
* | ||
* @param str The input {@link String} for which to capitalize the first letter | ||
* @param locale The {@link Locale} to use when capitalizing | ||
* @return The input {@link String} with the first letter capitalized | ||
*/ | ||
public static String capitalizeFirstLetter(String str, Locale locale) { | ||
if (isEmpty(str)) { | ||
return str; | ||
} | ||
return Character.isUpperCase(str.charAt(0)) | ||
? str | ||
: str.substring(0, 1).toUpperCase(locale) + str.substring(1); | ||
} | ||
|
||
/** | ||
* Capitalize the first letter of a string, using the default locale. Supports Unicode. | ||
* | ||
* @param str The input {@link String} for which to capitalize the first letter | ||
* @return The input {@link String} with the first letter capitalized | ||
*/ | ||
public static String capitalizeFirstLetter(String str) { | ||
return capitalizeFirstLetter(str, Locale.getDefault()); | ||
} | ||
|
||
/** | ||
* Lowercase the first letter of a string, in the specified locale. Supports Unicode. | ||
* | ||
* @param str The input {@link String} for which to lowercase the first letter | ||
* @param locale The {@link Locale} to use when lowercasing | ||
* @return The input {@link String} with the first letter lowercased | ||
*/ | ||
public static String lowercaseFirstLetter(String str, Locale locale) { | ||
if (isEmpty(str)) { | ||
return str; | ||
} | ||
return Character.isLowerCase(str.charAt(0)) | ||
? str | ||
: str.substring(0, 1).toLowerCase(locale) + str.substring(1); | ||
} | ||
|
||
/** | ||
* Lowercase the first letter of a string, using the default locale. Supports Unicode. | ||
* | ||
* @param str The input {@link String} for which to lowercase the first letter | ||
* @return The input {@link String} with the first letter lowercased | ||
*/ | ||
public static String lowercaseFirstLetter(String str) { | ||
return lowercaseFirstLetter(str, Locale.getDefault()); | ||
} | ||
} |
Oops, something went wrong.