/*
* IconToolbarComponent.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.customcomponentsdemo;
import java.util.Vector;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.AccessibleEventDispatcher;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Manager;
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;
import net.rim.device.api.ui.accessibility.AccessibleValue;
/**
* IconToolbarComponent is a customized accessible UI field implementation. The
* field contains icons laid out in a horizontal configuration. The icons are
* themselves accessible and are represented by the inner class Icon.
*/
public final class IconToolbarComponent extends Field implements
AccessibleContext {
private final Vector _icons;
private int[] _iconX;
private int _currentSelection;
private int _iconHeight;
private int _state;
/**
* Creates a new IconToolbarComponent object
*/
public IconToolbarComponent() {
super(Field.FOCUSABLE);
_icons = new Vector();
_currentSelection = -1;
// The component can recieve focus
_state = AccessibleState.FOCUSABLE;
}
/**
* Adds an accessible icon to the icons vector
*
* @param image
* The icon image
* @param label
* The icon label
*/
void addIcon(final Bitmap image, final String label) {
final Icon icon = new Icon(label, image);
_icons.addElement(icon);
final int iconHeight = image.getHeight();
if (_iconHeight < iconHeight) {
_iconHeight = iconHeight;
}
// The first icon will be selected by default
if (_currentSelection == -1) {
_currentSelection = 0;
icon._state |= AccessibleState.FOCUSED;
}
// Causes the component to be repainted
invalidate();
}
/**
* @see Field#layout(int, int)
*/
protected void layout(final int width, final int height) {
final int iconCount = _icons.size();
// Layout icons horizontally
if (iconCount != 0) {
final int xStep = width / iconCount;
int x = 0;
_iconX = new int[iconCount];
for (int i = 0; i < iconCount; i++) {
_iconX[i] = x;
x += xStep;
}
setExtent(width, Math.min(_iconHeight, height));
}
}
/**
* @see Field#navigationMovement(int, int, int, int)
*/
protected boolean navigationMovement(final int dx, final int dy,
final int status, final int time) {
// Move to next or prev icon
if (dx > 0) {
if (_currentSelection < _icons.size() - 1) {
// Update accessible state and notify screen reader
Icon currentIcon = (Icon) _icons.elementAt(_currentSelection);
currentIcon.focusChanged(false);
// Update current selected icon in the toolbar
_currentSelection++;
// Update accessible state and notify screen reader
currentIcon = (Icon) _icons.elementAt(_currentSelection);
currentIcon.focusChanged(true);
}
// Force a repaint of the component
invalidate();
return true;
} else if (dx < 0) {
if (_currentSelection > 0) {
// Update accessible state and notify screen reader
Icon currentIcon = (Icon) _icons.elementAt(_currentSelection);
currentIcon.focusChanged(false);
_currentSelection--;
// Update accessible state and notify screen reader
currentIcon = (Icon) _icons.elementAt(_currentSelection);
currentIcon.focusChanged(true);
}
// Force a repaint of the component
invalidate();
return true;
}
return super.navigationMovement(dx, dy, status, time);
}
/**
* @see Field#paint(Graphics)
*/
protected void paint(final Graphics graphics) {
// Paint all the icons at their locations.
for (int i = 0; i < _icons.size(); i++) {
final Icon icon = (Icon) _icons.elementAt(i);
icon.paint(graphics, _iconX[i], 0);
}
}
/**
* @see Field#drawFocus(Graphics, boolean)
*/
protected void drawFocus(final Graphics graphics, final boolean on) {
drawHighlightRegion(graphics, Field.HIGHLIGHT_FOCUS, on,
_iconX[_currentSelection], 0, 48, _iconHeight);
}
/**
* @see Field#onFocus(int)
*/
protected void onFocus(final int direction) {
super.onFocus(direction);
// Update accessible state and notify screen reader
final int oldState = _state;
_state = _state | AccessibleState.FOCUSED;
// Notify screen reader if the panel is visible
if (isVisible()) {
AccessibleEventDispatcher.dispatchAccessibleEvent(
AccessibleContext.ACCESSIBLE_STATE_CHANGED, new Integer(
oldState), new Integer(_state), this);
}
}
/**
* @see Field#onUnfocus()
*/
protected void onUnfocus() {
super.onUnfocus();
// Update accessible state and notify screen reader
final int oldState = _state;
_state = _state & ~AccessibleState.FOCUSED;
// Notify screen reader if the panel is visible
if (isVisible()) {
AccessibleEventDispatcher.dispatchAccessibleEvent(
AccessibleContext.ACCESSIBLE_STATE_CHANGED, new Integer(
oldState), new Integer(_state), this);
}
}
/**
* @see Field#getAccessibleContext()
*/
public AccessibleContext getAccessibleContext() {
return this;
}
// *********** AccessibleContext implementation ****************************
/**
* @see AccessibleContext#getAccessibleChildAt(int)
*/
public AccessibleContext getAccessibleChildAt(final int index) {
// Return icon child at the requested index
return (Icon) _icons.elementAt(index);
}
/**
* @see AccessibleContext#getAccessibleChildCount()
*/
public int getAccessibleChildCount() {
// Return total number of children
return _icons.size();
}
/**
* @see AccessibleContext#getAccessibleName()
*/
public String getAccessibleName() {
return " My Icon Toolbar ";
}
/**
* @see AccessibleContext#getAccessibleParent()
*/
public AccessibleContext getAccessibleParent() {
// Return manager where the toolbar component is added
final Manager manager = getManager();
return manager != null ? manager.getAccessibleContext() : null;
}
/**
* @see AccessibleContext#getAccessibleRole()
*/
public int getAccessibleRole() {
// This is a panel with icons
return AccessibleRole.PANEL;
}
/**
* @see AccessibleContext#getAccessibleSelectionCount()
*/
public int getAccessibleSelectionCount() {
// Only one icon selected at a time
return 1;
}
/**
* @see AccessibleContext#getAccessibleSelectionAt(int)
*/
public AccessibleContext getAccessibleSelectionAt(final int index) {
if (index == 0) {
return (Icon) _icons.elementAt(_currentSelection);
}
// Wrong selection index
return null;
}
/**
* @see AccessibleContext#getAccessibleStateSet()
*/
public int getAccessibleStateSet() {
return _state;
}
/**
* @see AccessibleContext#isAccessibleStateSet(int)
*/
public boolean isAccessibleStateSet(final int state) {
return (getAccessibleStateSet() & state) != 0;
}
/**
* @see AccessibleContext#isAccessibleChildSelected(int)
*/
public boolean isAccessibleChildSelected(final int index) {
// Check whether child icon at the specified index is selected
return _currentSelection == index;
}
/**
* @see AccessibleContext#getAccessibleTable()
*/
public AccessibleTable getAccessibleTable() {
// This is a panel, not a table
return null;
}
/**
* @see AccessibleContext#getAccessibleText()
*/
public AccessibleText getAccessibleText() {
// This is a panel, not text
return null;
}
/**
* @see AccessibleContext#getAccessibleValue()
*/
public AccessibleValue getAccessibleValue() {
// This is a panel, not a numerical value
return null;
}
// ********************** IconToolbarComponent child ***********************
/**
* The Icon class, serves as a child to the icon toolbar component
*/
public class Icon implements AccessibleContext {
private final String _label;
private final Bitmap _image;
private int _state;
/**
* Creates a new Icon object
*
* @param label
* Label for the icon
* @param image
* Bitmap image for the icon
*/
public Icon(final String label, final Bitmap image) {
_label = label;
_image = image;
// Icon can be focused
_state = AccessibleState.FOCUSABLE;
}
/**
* Paints this icon using the provided Graphics object
*
* @param graphics
* Graphics object used for drawing
* @param x
* Horizontal position within the IconToolbarComponent at
* which to draw the icon
* @param y
* Vertical position within the IconToolbarComponent at which
* to draw the icon
*/
public void paint(final Graphics graphics, final int x, final int y) {
graphics.drawBitmap(x, y, _image.getWidth(), _image.getHeight(),
_image, 0, 0);
}
// *********** AccessibleContext implementation
// ****************************
/**
* @see AccessibleContext#getAccessibleName()
*/
public String getAccessibleName() {
// Icon has a name and it will be read by the reader
return _label;
}
/**
* @see AccessibleContext#getAccessibleParent()
*/
public AccessibleContext getAccessibleParent() {
// Icon toolbar contains the icon
return IconToolbarComponent.this;
}
/**
* @see AccessibleContext#getAccessibleRole()
*/
public int getAccessibleRole() {
return AccessibleRole.ICON;
}
/**
* @see AccessibleContext#getAccessibleChildAt(int)
*/
public AccessibleContext getAccessibleChildAt(final int index) {
// Icon contains no children
return null;
}
/**
* @see AccessibleContext#getAccessibleChildCount()
*/
public int getAccessibleChildCount() {
// Icon contains no children
return 0;
}
/**
* @see AccessibleContext#getAccessibleSelectionAt(int)
*/
public AccessibleContext getAccessibleSelectionAt(final int index) {
// Icon contains no children
return null;
}
/**
* @see AccessibleContext#getAccessibleSelectionCount()
*/
public int getAccessibleSelectionCount() {
// Icon contains no children.
return 0;
}
/**
* @see AccessibleContext#getAccessibleStateSet()
*/
public int getAccessibleStateSet() {
return _state;
}
/**
* @see AccessibleContext#getAccessibleTable()
*/
public AccessibleTable getAccessibleTable() {
// This is an icon, not a table
return null;
}
/**
* @see AccessibleContext#getAccessibleText()
*/
public AccessibleText getAccessibleText() {
// This is an icon, not text
return null;
}
/**
* @see AccessibleContext#getAccessibleValue()
*/
public AccessibleValue getAccessibleValue() {
// Icon contains no numerical values
return null;
}
/**
* @see AccessibleContext#isAccessibleChildSelected(int)
*/
public boolean isAccessibleChildSelected(final int index) {
// Icon contains no children
return false;
}
/**
* @see AccessibleContext#isAccessibleStateSet(int)
*/
public boolean isAccessibleStateSet(final int state) {
return (getAccessibleStateSet() & state) != 0;
}
/**
* Called when the icon gains or loses focus
*
* @param focusGained
* True if focus gained, false if focus lost
*/
private void focusChanged(final boolean focusGained) {
// Update state
final int initialState = _state;
if (focusGained) {
_state |= AccessibleState.FOCUSED;
} else {
_state &= ~AccessibleState.FOCUSED;
}
// Notify screen reader if component is visible
final int currentState = _state;
if (initialState != currentState && isVisible()) {
AccessibleEventDispatcher.dispatchAccessibleEvent(
AccessibleContext.ACCESSIBLE_STATE_CHANGED,
new Integer(initialState), new Integer(currentState),
this);
}
}
}
}