view src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibilityService.java @ 0:e0ee6ac3a45f

first import
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Thu, 13 Dec 2012 20:00:21 +0000
parents
children
line wrap: on
line source
/*  
 CCmI Diagram Editor for Android 
  
 Copyright (C) 2012  Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package uk.ac.qmul.eecs.ccmi.accessibility;

import java.util.EnumMap;
import java.util.Locale;

import uk.ac.qmul.eecs.ccmi.activities.R;

import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Vibrator;
import android.speech.tts.TextToSpeech;
import android.util.Log;

/**
 * Encloses all the services needed to implement a custom accessibility system. Text-to-speech synthesis, sound and 
 * device vibration.   
 *
 */
public class AccessibilityService {
	private static final long VIBRATE_TIME = 200;
	private static final float SPEECH_RATE = 2.0f;
	private TextToSpeech tts;
	private SoundPool soundPool;
	private AudioManager audioManager;
	private Vibrator vibrator; 
	private Context context;
	private EnumMap<SoundEvent,Integer> soundIDs;
	private boolean soundLoaded;
	/**
	 * 
	 * A list of events that have a sound associated. The sounds are built in and cannot be modified. 
	 * 
	 *
	 */
	public enum SoundEvent {
		V_CANCEL,
		V_COLLAPSE,
		V_DRAG,
		V_EDITING_MODE,
		V_ENDOFLIST,
		V_ERROR,
		V_EXPAND,
		V_HOOK_OFF,
		V_HOOK_ON,
		V_JUMP,
		V_MAGNET_OFF,
		V_MAGNET_ON,
		V_OK
	}
	
	private static AccessibilityService singleton;
	
	/**
	 * Creates an instance of {@code AccessibilityService}. It implements the singleton pattern, therefore 
	 * successive calls to this method will always return the same object.
	 * 
	 * @param context The context to use. Usually your {@code Application} or {@code Activity} object.
	 * 
	 * @return a singleton instance of {@code AccessibilityService}
	 */
	public static AccessibilityService getInstance(Context context){
		if(singleton == null){
			singleton = new AccessibilityService(context);
		}
		return singleton;
	}
	
	/**
	 * Constructs a new {@code AccessibilityService}.
	 *  
	 * @param context The current context
	 */
	public AccessibilityService(Context context){
		soundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 0);
		this.context = context;
		audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
		soundIDs = new EnumMap<SoundEvent,Integer>(SoundEvent.class);
		tts = new TextToSpeech(context, new TextToSpeech.OnInitListener(){
			@Override
			public void onInit(int status) {
				if (status == TextToSpeech.SUCCESS) {
					int result = tts.setLanguage(Locale.UK);
					if (result == TextToSpeech.LANG_MISSING_DATA
							|| result == TextToSpeech.LANG_NOT_SUPPORTED) {
						Log.w("TEXT TO SPEECH", "Text to Speech UK language not supported");
					}
				}
			}
		});
		tts.setSpeechRate(SPEECH_RATE);
		vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
	}
	
	/**
	 * Loads the built in sound library. This method must be called before any call to 
	 * {@code playSound} or {@code stopSound}.
	 */
	public void loadSounds(){
		if(soundLoaded)
			return;
		soundIDs.put(SoundEvent.V_CANCEL,soundPool.load(context, R.raw.cancel, 1));
		soundIDs.put(SoundEvent.V_COLLAPSE,soundPool.load(context, R.raw.collapse, 1));
		soundIDs.put(SoundEvent.V_EXPAND,soundPool.load(context, R.raw.expand, 1));
		soundIDs.put(SoundEvent.V_ENDOFLIST,soundPool.load(context, R.raw.endoflist, 1));
		soundIDs.put(SoundEvent.V_OK,soundPool.load(context, R.raw.ok, 1));
		soundIDs.put(SoundEvent.V_DRAG,soundPool.load(context, R.raw.drag, 1));
		soundIDs.put(SoundEvent.V_EDITING_MODE,soundPool.load(context, R.raw.editingmode, 1));
		soundIDs.put(SoundEvent.V_ERROR,soundPool.load(context, R.raw.error, 1));
		soundIDs.put(SoundEvent.V_HOOK_OFF,soundPool.load(context, R.raw.hookoff, 1));
		soundIDs.put(SoundEvent.V_HOOK_ON,soundPool.load(context, R.raw.hookon, 1));
		soundIDs.put(SoundEvent.V_JUMP,soundPool.load(context, R.raw.jump, 1));
		soundIDs.put(SoundEvent.V_MAGNET_OFF,soundPool.load(context, R.raw.magnetoff, 1));
		soundLoaded = true;
	}
	
	/**
	 * Plays the sound associated to a {@code SoundEvent}. 
	 * 
	 * @param event the event to play the sound of 
	 * @param loop whether to play the sound in a continuous loop or not 
	 */
	public void playSound(SoundEvent event, boolean loop){
		if(!soundLoaded)
			throw new IllegalStateException("loadSounds() must be called before any call to playSound");
		float streamVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
		streamVolume = streamVolume / audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
		soundPool.play(soundIDs.get(event), streamVolume, streamVolume, 1, loop ? -1 : 0, 1f);
	}
	
	/**
	 * Plays the sound associated to a {@code SoundEvent} once. This call is equivalent to 
	 * {@code playSound(event,false)}. 
	 * 
	 * @param event the event to play the sound of
	 */
	public void playSound(SoundEvent event){
		playSound(event,false);
	}
	
	/**
	 * Stop all the sounds being played at the moment of the call. Useful to stop a sound that is looping 
	 * after a call to  {@code playSound(event,true)}.
	 */
	public void stopSound(){
		if(!soundLoaded)
			throw new IllegalStateException("loadSounds() must be called before  any call to pauseSound");
		soundPool.autoPause();
	}
	
	/**
	 * Utters a text via android text to speech synthesizer 
	 * 
	 *   @param text the string to be uttered
	 **/
	public void speak(String text){
		tts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
	}
	
	
	/**
	 * Make the device vibrate 
	 */
	public void vibrate(){
		vibrator.vibrate(VIBRATE_TIME);
	}
	
	/**
	 * Dispose the resources allocated during initialization. To be called when the class 
	 *  is no longer needed by client classes. 
	 */
	public void dispose(){
		/* dispose sound */
		for(SoundEvent evt : soundIDs.keySet()){
			soundPool.unload(soundIDs.get(evt));			
		}
		/* dispose tts */
		if(tts != null){
			tts.stop();
			tts.shutdown();
		}
	}

}