com.googlecode.eyesfree.brailleback.DisplaySpans.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.eyesfree.brailleback.DisplaySpans.java

Source

/*
 * Copyright (C) 2012 Google Inc.
 *
 * 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.
 */

package com.googlecode.eyesfree.brailleback;

import com.googlecode.eyesfree.utils.LogUtils;

import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.text.Spannable;
import android.text.Spanned;
import android.util.Log;

/**
 * Static utilities for text spans that control how text is displayed on the
 * braille display.
 */
public class DisplaySpans {
    /**
     * Marks a part of the content that has focus.
     */
    public static class FocusSpan {
    }

    /**
     * Marks a text selection or cursor on the display.
     */
    public static class SelectionSpan {
    }

    /**
     * Marks a region of {@code spanned} as having focus.
     */
    public static void addFocus(Spannable spannable, int start, int end) {
        spannable.setSpan(new FocusSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    /**
     * Marks a portion of {@code spannable} as containing text selection.  If
     * {@code start} and {@code end} are equal, then then added span marks a
     * cursor.
     */
    public static void addSelection(Spannable spannable, int start, int end) {
        int flags;
        if (start == end) {
            flags = Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
        } else {
            flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
        }
        spannable.setSpan(new SelectionSpan(), start, end, flags);
    }

    /**
     * Marks the whole of {@code spannable} as containing the content
     * coming from {@code node}.  A copy of {@code node} is stored.
     *
     * @see #recycleSpans
     */
    public static void setAccessibilityNode(Spannable spannable, AccessibilityNodeInfoCompat node) {
        spannable.setSpan(AccessibilityNodeInfoCompat.obtain(node), 0, spannable.length(),
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    /**
     * Finds the shortest accessibility node span that overlaps {@code position}
     * in {@code chars}.  If a node is found, it is returned, otherwise
     * {@code null} is returned.  If a node is returned, it is still owned
     * by {@code chars} for the purpose of recycling.
     */
    public static AccessibilityNodeInfoCompat getAccessibilityNodeFromPosition(int position, CharSequence chars) {
        if (!(chars instanceof Spanned)) {
            return null;
        }
        Spanned spanned = (Spanned) chars;
        AccessibilityNodeInfoCompat[] spans = spanned.getSpans(position, position,
                AccessibilityNodeInfoCompat.class);
        if (spans.length == 0) {
            return null;
        }
        AccessibilityNodeInfoCompat found = spans[0];
        int foundLength = spanned.getSpanEnd(found) - spanned.getSpanStart(found);
        for (int i = 1; i < spans.length; ++i) {
            AccessibilityNodeInfoCompat span = spans[i];
            int length = spanned.getSpanEnd(span) - spanned.getSpanStart(span);
            if (length < foundLength) {
                found = span;
                foundLength = length;
            }
        }
        return found;
    }

    /**
     * Utility function to log what accessibiility nodes are attached
     * to what parts of the character sequence.
     */
    public static void logNodes(CharSequence chars) {
        if (!(chars instanceof Spanned)) {
            LogUtils.log(DisplaySpans.class, Log.VERBOSE, "Not a Spanned");
            return;
        }
        Spanned spanned = (Spanned) chars;
        AccessibilityNodeInfoCompat spans[] = spanned.getSpans(0, spanned.length(),
                AccessibilityNodeInfoCompat.class);
        for (AccessibilityNodeInfoCompat node : spans) {
            LogUtils.log(DisplaySpans.class, Log.VERBOSE,
                    chars.subSequence(spanned.getSpanStart(node), spanned.getSpanEnd(node)).toString());
            LogUtils.log(DisplaySpans.class, Log.VERBOSE, node.getInfo().toString());
        }
    }

    /**
     * Recycles objects owned by the spannable.  In particular, any
     * accessibility nodes that have been associated with {@code spannable}
     * are recycled and removed.
     */
    public static void recycleSpans(CharSequence chars) {
        if (!(chars instanceof Spannable)) {
            return;
        }
        Spannable spannable = (Spannable) chars;
        AccessibilityNodeInfoCompat[] nodes = spannable.getSpans(0, spannable.length(),
                AccessibilityNodeInfoCompat.class);
        for (AccessibilityNodeInfoCompat node : nodes) {
            node.recycle();
            spannable.removeSpan(node);
        }
    }

    /**
     * Returns a span in {@code spanned} that is {@link Object#equals}
     * to {@code obj}.
     */
    public static Object getEqualSpan(Spanned spanned, Object obj) {
        Object[] spans = spanned.getSpans(0, spanned.length(), obj.getClass());
        for (Object span : spans) {
            if (obj.equals(span)) {
                return span;
            }
        }
        return null;
    }
}