/* * Copyright (C) 2010 The Android Open Source Project * * 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.example.android.apis.accessibility; import com.example.android.apis.R; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.Handler; import android.os.Message; import android.os.Vibrator; import android.speech.tts.TextToSpeech; import android.util.Log; import android.util.SparseArray; import android.view.accessibility.AccessibilityEvent; import java.util.List; /** * This class is an {@link AccessibilityService} that provides custom feedback * for the Clock application that comes by default with Android devices. It * demonstrates the following key features of the Android accessibility APIs: *
* * Note: This code sample will work only on devices shipped with the * default Clock application. * *
*/ // This works with AlarmClock and Clock whose package name changes in different releases private static final String[] PACKAGE_NAMES = new String[] { "com.android.alarmclock", "com.google.android.deskclock", "com.android.deskclock" }; // Message types we are passing around. /** Speak. */ private static final int MESSAGE_SPEAK = 1; /** Stop speaking. */ private static final int MESSAGE_STOP_SPEAK = 2; /** Start the TTS service. */ private static final int MESSAGE_START_TTS = 3; /** Stop the TTS service. */ private static final int MESSAGE_SHUTDOWN_TTS = 4; /** Play an earcon. */ private static final int MESSAGE_PLAY_EARCON = 5; /** Stop playing an earcon. */ private static final int MESSAGE_STOP_PLAY_EARCON = 6; /** Vibrate a pattern. */ private static final int MESSAGE_VIBRATE = 7; /** Stop vibrating. */ private static final int MESSAGE_STOP_VIBRATE = 8; // Screen state broadcast related constants. /** Feedback mapping index used as a key for the screen-on broadcast. */ private static final int INDEX_SCREEN_ON = 0x00000100; /** Feedback mapping index used as a key for the screen-off broadcast. */ private static final int INDEX_SCREEN_OFF = 0x00000200; // Ringer mode change related constants. /** Feedback mapping index used as a key for normal ringer mode. */ private static final int INDEX_RINGER_NORMAL = 0x00000400; /** Feedback mapping index used as a key for vibration ringer mode. */ private static final int INDEX_RINGER_VIBRATE = 0x00000800; /** Feedback mapping index used as a key for silent ringer mode. */ private static final int INDEX_RINGER_SILENT = 0x00001000; // Speech related constants. /** * The queuing mode we are using - interrupt a spoken utterance before * speaking another one. */ private static final int QUEUING_MODE_INTERRUPT = 2; /** The space string constant. */ private static final String SPACE = " "; /** Mapping from integers to vibration patterns for haptic feedback. */ private static final SparseArray
* 1. {@link AudioManager#RINGER_MODE_SILENT}
* Goal: Provide only custom haptic feedback.
* Approach: Take over the haptic feedback by configuring this service to provide
* such and do so. This way the system will not call the default haptic
* feedback service KickBack.
* Take over the audible and spoken feedback by configuring this
* service to provide such feedback but not doing so. This way the system
* will not call the default spoken feedback service TalkBack and the
* default audible feedback service SoundBack.
*
* 2. {@link AudioManager#RINGER_MODE_VIBRATE}
* Goal: Provide custom audible and default haptic feedback.
* Approach: Take over the audible feedback and provide custom one.
* Take over the spoken feedback but do not provide such.
* Let some other service provide haptic feedback (KickBack).
*
* 3. {@link AudioManager#RINGER_MODE_NORMAL}
* Goal: Provide custom spoken, default audible and default haptic feedback.
* Approach: Take over the spoken feedback and provide custom one.
* Let some other services provide audible feedback (SounBack) and haptic
* feedback (KickBack).
*
* Note: The feedbackType parameter is an bitwise or of all * feedback types this service would like to provide. *
*/ private void setServiceInfo(int feedbackType) { AccessibilityServiceInfo info = new AccessibilityServiceInfo(); // We are interested in all types of accessibility events. info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; // We want to provide specific type of feedback. info.feedbackType = feedbackType; // We want to receive events in a certain interval. info.notificationTimeout = EVENT_NOTIFICATION_TIMEOUT_MILLIS; // We want to receive accessibility events only from certain packages. info.packageNames = PACKAGE_NAMES; setServiceInfo(info); } @Override public void onAccessibilityEvent(AccessibilityEvent event) { Log.i(LOG_TAG, mProvidedFeedbackType + " " + event.toString()); // Here we act according to the feedback type we are currently providing. if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) { mHandler.obtainMessage(MESSAGE_SPEAK, formatUtterance(event)).sendToTarget(); } else if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_AUDIBLE) { mHandler.obtainMessage(MESSAGE_PLAY_EARCON, event.getEventType(), 0).sendToTarget(); } else if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_HAPTIC) { mHandler.obtainMessage(MESSAGE_VIBRATE, event.getEventType(), 0).sendToTarget(); } else { throw new IllegalStateException("Unexpected feedback type " + mProvidedFeedbackType); } } @Override public void onInterrupt() { // Here we act according to the feedback type we are currently providing. if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) { mHandler.obtainMessage(MESSAGE_STOP_SPEAK).sendToTarget(); } else if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_AUDIBLE) { mHandler.obtainMessage(MESSAGE_STOP_PLAY_EARCON).sendToTarget(); } else if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_HAPTIC) { mHandler.obtainMessage(MESSAGE_STOP_VIBRATE).sendToTarget(); } else { throw new IllegalStateException("Unexpected feedback type " + mProvidedFeedbackType); } } /** * Formats an utterance from an {@link AccessibilityEvent}. * * @param event The event from which to format an utterance. * @return The formatted utterance. */ private String formatUtterance(AccessibilityEvent event) { StringBuilder utterance = mUtterance; // Clear the utterance before appending the formatted text. utterance.setLength(0); List