/*
* ChildReader.java
*
* Copyright � 1998-2011 Research In Motion Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.accessibilitydemo.screenreaderdemo;
import net.rim.device.api.ui.accessibility.AccessibleContext;
import net.rim.device.api.ui.accessibility.AccessibleRole;
import net.rim.device.api.ui.accessibility.AccessibleState;
import net.rim.device.api.ui.accessibility.AccessibleTable;
import net.rim.device.api.ui.accessibility.AccessibleText;
/**
* This class contains methods to read accessible child elements
*/
public final class ChildReader {
/**
* Reads an accessible child element
*
* @param context
* The accessible child element to read
*/
static void readChildElement(final AccessibleContext context) {
if (context == null) {
return;
}
String name = context.getAccessibleName();
if (name == null) {
name = "";
}
// Evaluate the states set for the accessible element
final int statesSet = context.getAccessibleStateSet();
final boolean focused = (statesSet & AccessibleState.FOCUSED) != 0;
final boolean expanded = (statesSet & AccessibleState.EXPANDED) != 0;
final boolean collapsed = (statesSet & AccessibleState.COLLAPSED) != 0;
final boolean selected = (statesSet & AccessibleState.SELECTED) != 0;
final boolean selectable =
(statesSet & AccessibleState.SELECTABLE) != 0;
final boolean multiSelectable =
(statesSet & AccessibleState.MULTI_SELECTABLE) != 0;
final boolean editable = (statesSet & AccessibleState.EDITABLE) != 0;
final boolean checked = (statesSet & AccessibleState.CHECKED) != 0;
final boolean busy = (statesSet & AccessibleState.BUSY) != 0;
final boolean expandable =
(statesSet & AccessibleState.EXPANDABLE) != 0;
final boolean focusable = (statesSet & AccessibleState.FOCUSABLE) != 0;
// Create strings representing the accessible element states set
final String focusedText = focused ? " focused" : "";
final String expandedText = expanded ? " expanded" : "";
final String collapsedText = collapsed ? " collapsed" : "";
final String expandableText = expandable ? " expandable" : "";
final String selectedText = selected ? " selected" : "";
final String editableText = editable ? " editable" : "";
final String checkedText = checked ? " checked" : " unchecked";
final String multiSelectableText =
multiSelectable ? " multi selectable" : "";
final String focusableText = focusable ? " focusable" : "";
// This buffer will contain text to be spoken
final StringBuffer toSpeak = new StringBuffer();
// Evaluate the navagational orientation state(s) set for the accessible
// element
final String orientation = Util.getOrientation(statesSet);
final AccessibleText text = context.getAccessibleText();
final int childCount = context.getAccessibleChildCount();
// In cases where there are many components on a
// screen/dialog/menu...most of
// which are not frequently used, it's more practical to set a limit on
// the
// number of components to be read:
// int maxCount = Math.min( childCount, 10);
// and then use maxCount instead of childCount.
switch (context.getAccessibleRole()) {
case AccessibleRole.SCREEN:
if (busy) {
Util.speak("Screen " + name + " loading");
} else {
Util.speak("Screen " + name);
for (int i = 0; i < childCount; i++) {
final AccessibleContext child =
context.getAccessibleChildAt(i);
readChildElement(child);
}
}
break;
case AccessibleRole.TEXT_FIELD:
if (text != null) {
String currentText = text.getWholeText();
currentText = currentText != null ? currentText.trim() : "";
final String textToSpeak =
currentText.length() > 0 ? " with text " + currentText
: " empty";
toSpeak.append(name + " text field " + textToSpeak);
toSpeak.append(focusedText);
toSpeak.append(editableText);
}
break;
case AccessibleRole.LABEL:
toSpeak.append(name);
toSpeak.append(focusableText);
toSpeak.append(focusedText);
toSpeak.append(selectedText);
toSpeak.append(expandedText);
break;
case AccessibleRole.APP_ICON:
toSpeak.append(name + "application icon");
toSpeak.append(focusedText);
break;
case AccessibleRole.ICON:
toSpeak.append(name + " icon ");
toSpeak.append(focusedText);
break;
case AccessibleRole.DATE:
toSpeak.append(name + " date field ");
toSpeak.append(selectedText);
if (text != null) {
toSpeak.append(" with current value " + text.getWholeText());
}
break;
case AccessibleRole.LIST:
if (busy) {
Util.speak(orientation + "list " + name + " loading");
} else {
Util.speak(orientation + name + "list with " + childCount
+ " elements" + focusedText + multiSelectableText);
for (int i = 0; i < childCount; i++) {
final AccessibleContext child =
context.getAccessibleChildAt(i);
readChildElement(child);
}
}
break;
case AccessibleRole.PANEL:
if (busy) {
Util.speak("panel " + name + " loading");
} else {
Util.speak(name + " panel with " + childCount + " items");
for (int i = 0; i < childCount; i++) {
final AccessibleContext child =
context.getAccessibleChildAt(i);
readChildElement(child);
}
}
break;
case AccessibleRole.GAUGE:
if (busy) {
Util.speak("gauge " + name + " loading");
} else {
Util.speak(name
+ " gauge with value "
+ context.getAccessibleValue()
.getCurrentAccessibleValue());
}
break;
case AccessibleRole.PUSH_BUTTON:
toSpeak.append(name + " button");
toSpeak.append(focusedText);
break;
case AccessibleRole.MENU_ITEM:
toSpeak.append(name + " menu item ");
toSpeak.append(selectedText);
break;
case AccessibleRole.CHECKBOX:
toSpeak.append(name);
toSpeak.append(" check box ");
toSpeak.append(focusedText);
toSpeak.append(checkedText);
break;
case AccessibleRole.TABLE:
if (busy) {
Util.speak("table " + name + " loading");
} else {
final AccessibleTable table = context.getAccessibleTable();
if (table != null) {
readTableElement(context);
if (selectable) {
ScreenReaderHandler.handleTableSelection(table);
}
}
}
break;
case AccessibleRole.BITMAP:
Util.speak(name + " image");
break;
case AccessibleRole.COMBO:
toSpeak.append(name + " combobox ");
toSpeak.append(expandedText);
if (expanded) {
Util.speak(toSpeak.toString());
toSpeak.setLength(0);
for (int i = 0; i < childCount; i++) {
final AccessibleContext child =
context.getAccessibleChildAt(i);
readChildElement(child);
}
} else {
final String value = context.getAccessibleText().getWholeText();
if (value != null && value.length() > 0) {
toSpeak.append(" current value " + value);
} else {
toSpeak.append(" empty");
}
}
break;
case AccessibleRole.HYPERLINK:
toSpeak.append(name + " hyperlink ");
break;
case AccessibleRole.SEPARATOR:
toSpeak.append("separator");
break;
case AccessibleRole.TREE_FIELD:
Util.speak(name + " tree field" + expandableText + collapsedText
+ expandedText + selectedText);
for (int i = 0; i < childCount; i++) {
final AccessibleContext child = context.getAccessibleChildAt(i);
readChildElement(child);
}
break;
case AccessibleRole.CHOICE:
toSpeak.append(name + " choice with " + childCount + " elements");
toSpeak.append(focusedText);
toSpeak.append(expandedText);
if (expanded) {
Util.speak(toSpeak.toString());
toSpeak.setLength(0);
for (int i = 0; i < childCount; i++) {
final AccessibleContext child =
context.getAccessibleChildAt(i);
readChildElement(child);
}
} else {
toSpeak.append(" current value "
+ context.getAccessibleSelectionAt(0)
.getAccessibleName());
}
break;
}
Util.speak(toSpeak.toString());
}
/**
* Reads an accessible table element
*
* @param context
* The accessible element to read
*/
static void readTableElement(final AccessibleContext context) {
if (!(context instanceof AccessibleTable)) {
return;
}
final AccessibleTable table = (AccessibleTable) context;
// Evaluate number of columns and rows in the accessible table
final int tableColCount = table.getAccessibleColumnCount();
final int tableRowCount = table.getAccessibleRowCount();
String name = context.getAccessibleName();
if (name == null) {
name = "";
}
// Describe the table
final StringBuffer tableHeader = new StringBuffer();
tableHeader.append(name);
tableHeader.append(" table with ");
tableHeader.append(tableColCount);
tableHeader.append(" columns and ");
tableHeader.append(tableRowCount);
tableHeader.append(" rows");
Util.speak(tableHeader.toString());
// Get the column headers
final AccessibleContext[] tableColumnsNames =
table.getAccessibleColumnHeader();
if (tableColumnsNames != null) {
// Read cells, column by column
for (int i = 0; i < tableColumnsNames.length; i++) {
final AccessibleContext column = tableColumnsNames[i];
Util.speak("column " + (i + 1));
if (column == null) {
Util.speak("empty");
} else {
readChildElement(column);
}
// Read cells in the column
for (int row = 0; row < tableRowCount; row++) {
final AccessibleContext accessibleCell =
table.getAccessibleAt(row, i);
if (accessibleCell == null) {
Util.speak("empty");
} else {
readChildElement(accessibleCell);
}
}
}
} else {
// Read cells, row by row
Util.speak("table data");
for (int row = 0; row < tableRowCount; row++) {
for (int col = 0; col < tableColCount; col++) {
final AccessibleContext accessibleCell =
table.getAccessibleAt(row, col);
if (accessibleCell == null) {
Util.speak("empty");
} else {
readChildElement(accessibleCell);
}
}
}
}
}
}