com.android.screenspeak.formatter.ScrollFormatter.java Source code

Java tutorial

Introduction

Here is the source code for com.android.screenspeak.formatter.ScrollFormatter.java

Source

/*
 * Copyright (C) 2011 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.android.screenspeak.formatter;

import android.os.SystemClock;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
import android.view.accessibility.AccessibilityEvent;

import com.android.screenspeak.FeedbackItem;
import com.android.screenspeak.R;
import com.google.android.marvin.screenspeak.ScreenSpeakService;
import com.android.screenspeak.Utterance;

/**
 * Formatter that returns an utterance to announce scrolling.
 */
public class ScrollFormatter implements EventSpeechRule.AccessibilityEventFormatter {
    /** The minimum interval (in milliseconds) between scroll feedback events. */
    private static final long MIN_SCROLL_INTERVAL = 250;

    private static long mLastScrollEvent = -1;

    @Override
    public boolean format(AccessibilityEvent event, ScreenSpeakService context, Utterance utterance) {
        final long currentTime = SystemClock.uptimeMillis();

        if ((currentTime - mLastScrollEvent) < MIN_SCROLL_INTERVAL) {
            // TODO: We shouldn't just reject events, since we'll get weird
            // rhythms when scrolling, e.g. if we're getting an event every
            // 300ms and we reject at the 250ms mark.
            return false;
        }

        mLastScrollEvent = currentTime;

        final float percent = getScrollPercent(event);
        final float rate = (float) Math.pow(2.0, (percent / 50.0) - 1);

        utterance.addAuditory(R.raw.scroll_tone);
        utterance.getMetadata().putFloat(Utterance.KEY_METADATA_EARCON_RATE, rate);
        utterance.addSpokenFlag(FeedbackItem.FLAG_NO_SPEECH);

        return true;
    }

    /**
     * Returns the percentage scrolled within a scrollable view. The value will
     * be in the range {0..100} where 100 is the maximum scroll amount.
     *
     * @param event The event from which to obtain the scroll position.
     * @return The percentage scrolled within a scrollable view.
     */
    private float getScrollPercent(AccessibilityEvent event) {
        final float position = getScrollPosition(event);

        return (100.0f * Math.max(0.0f, Math.min(1.0f, position)));
    }

    /**
     * Returns a floating point value representing the scroll position of an
     * {@link AccessibilityEvent}. This value may be outside the range {0..1}.
     * If there's no valid way to obtain a position, this method returns 0.5.
     *
     * @param event The event from which to obtain the scroll position.
     * @return A floating point value representing the scroll position.
     */
    private float getScrollPosition(AccessibilityEvent event) {
        final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
        final int itemCount = event.getItemCount();
        final int fromIndex = event.getFromIndex();

        // First, attempt to use (fromIndex / itemCount).
        if ((fromIndex >= 0) && (itemCount > 0)) {
            return (fromIndex / (float) itemCount);
        }

        final int scrollY = record.getScrollY();
        final int maxScrollY = record.getMaxScrollY();

        // Next, attempt to use (scrollY / maxScrollY). This will fail if the
        // getMaxScrollX() method is not available.
        if ((scrollY >= 0) && (maxScrollY > 0)) {
            return (scrollY / (float) maxScrollY);
        }

        // Finally, attempt to use (scrollY / itemCount).
        // TODO(AV): Hack from previous versions -- is it still needed?
        if ((scrollY >= 0) && (itemCount > 0) && (scrollY <= itemCount)) {
            return (scrollY / (float) itemCount);
        }

        return 0.5f;
    }
}