Java tutorial
/* * 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.talkback.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.talkback.FeedbackItem; import com.android.talkback.R; import com.google.android.marvin.talkback.TalkBackService; import com.android.talkback.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, TalkBackService 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: Hack from previous versions -- is it still needed? if ((scrollY >= 0) && (itemCount > 0) && (scrollY <= itemCount)) { return (scrollY / (float) itemCount); } return 0.5f; } }