PageRenderTime 38ms CodeModel.GetById 2ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 1ms

/TalkBack/src/com/google/android/marvin/talkback/FeedbackController.java

http://eyes-free.googlecode.com/
Java | 234 lines | 97 code | 42 blank | 95 comment | 14 complexity | 02e6f6ac4ade2f7265f4c65d4c00a17e MD5 | raw file
  1/*
  2 * Copyright (C) 2011 Google Inc.
  3 *
  4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5 * use this file except in compliance with the License. You may obtain a copy of
  6 * the License at
  7 *
  8 * http://www.apache.org/licenses/LICENSE-2.0
  9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 13 * License for the specific language governing permissions and limitations under
 14 * the License.
 15 */
 16
 17package com.google.android.marvin.talkback;
 18
 19import android.content.Context;
 20import android.content.res.Resources;
 21import android.media.AudioManager;
 22import android.media.SoundPool;
 23import android.os.Vibrator;
 24
 25import java.util.Map;
 26import java.util.TreeMap;
 27
 28/**
 29 * Provides auditory and haptic feedback.
 30 * 
 31 * @author alanv@google.com (Alan Viverette)
 32 */
 33class FeedbackController {
 34    private static FeedbackController sInstance;
 35
 36    /**
 37     * @return The shared instance of FeedbackController.
 38     */
 39    public static FeedbackController getInstance(Context context) {
 40        if (sInstance == null) {
 41            sInstance = new FeedbackController(context);
 42        }
 43
 44        return sInstance;
 45    }
 46
 47    /**
 48     * Default stream for audio feedback.
 49     */
 50    private static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
 51
 52    /**
 53     * Default volume for sound playback. Use 1.0f to match the current stream
 54     * volume.
 55     */
 56    private static final float DEFAULT_VOLUME = 1.0f;
 57
 58    /**
 59     * Default rate for sound playback.
 60     */
 61    private static final float DEFAULT_RATE = 1.0f;
 62
 63    /**
 64     * Number of channels to use in SoundPool for auditory icon feedback.
 65     */
 66    private static final int NUMBER_OF_CHANNELS = 10;
 67
 68    /**
 69     * The parent context. Required for mapping resource identifiers to
 70     * resources.
 71     */
 72    private final Context mContext;
 73
 74    /**
 75     * Vibration service used to play vibration patterns.
 76     */
 77    private final Vibrator mVibrator;
 78
 79    /**
 80     * Sound pool used to play auditory icons.
 81     */
 82    private final SoundPool mSoundPool;
 83
 84    /**
 85     * Whether haptic feedback is enabled.
 86     */
 87    private boolean mHapticEnabled = true;
 88
 89    /**
 90     * Whether auditory feedback is enabled.
 91     */
 92    private boolean mAuditoryEnabled = true;
 93
 94    /** Current volume (range 0..1). */
 95    private float mVolume = DEFAULT_VOLUME;
 96
 97    private final Map<Integer, long[]> mResourceIdToVibrationPatternMap;
 98    private final Map<Integer, Integer> mResourceIdToSoundMap;
 99
100    /**
101     * Constructs a new feedback controller.
102     * 
103     * @param context The parent context.
104     */
105    private FeedbackController(Context context) {
106        mContext = context;
107        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
108        mSoundPool = new SoundPool(NUMBER_OF_CHANNELS, DEFAULT_STREAM, 1);
109        mResourceIdToSoundMap = new TreeMap<Integer, Integer>();
110        mResourceIdToVibrationPatternMap = new TreeMap<Integer, long[]>();
111
112        // Since the sound pool loads resources asynchronously, we need to play
113        // resources immediately after they load.
114        mSoundPool.setOnLoadCompleteListener(mOnLoadCompleteListener);
115    }
116
117    /**
118     * Sets the current volume for auditory feedback.
119     * 
120     * @param volume Volume value (range 0..100).
121     */
122    public void setVolume(int volume) {
123        mVolume = (Math.min(100, Math.max(0, volume)) / 100.0f);
124    }
125
126    /**
127     * @param enabled Whether haptic feedback should be enabled.
128     */
129    public void setHapticEnabled(boolean enabled) {
130        mHapticEnabled = enabled;
131    }
132
133    /**
134     * @param enabled Whether auditory feedback should be enabled.
135     */
136    public void setAuditoryEnabled(boolean enabled) {
137        mAuditoryEnabled = enabled;
138    }
139
140    /**
141     * Stops all active feedback.
142     */
143    public void interrupt() {
144        mVibrator.cancel();
145        mSoundPool.autoPause();
146    }
147
148    /**
149     * Releases resources associated with this feedback controller. It is good
150     * practice to call this method when you're done using the controller.
151     */
152    public void shutdown() {
153        mVibrator.cancel();
154        mSoundPool.release();
155    }
156
157    /**
158     * Plays the sound file specified by the given resource identifier at the
159     * specified rate.
160     * 
161     * @param resId The sound file's resource identifier.
162     * @param rate The rate at which to play back the sound, where 1.0 is normal
163     *            speed and 0.5 is half-speed.
164     * @return {@code true} if successful
165     */
166    public boolean playSound(int resId, float rate) {
167        if (!mAuditoryEnabled) {
168            return false;
169        }
170
171        Integer soundId = mResourceIdToSoundMap.get(resId);
172
173        if (soundId == null) {
174            soundId = mSoundPool.load(mContext, resId, 1);
175
176            mResourceIdToSoundMap.put(resId, soundId);
177
178            // Since we need to play the sound immediately after it loads, we
179            // should just return true.
180            return true;
181        }
182
183        final int stream = mSoundPool.play(soundId, mVolume, mVolume, 1, 0, rate);
184
185        return (stream != 0);
186    }
187
188    /**
189     * Plays the vibration pattern specified by the given resource identifier.
190     * 
191     * @param resId The vibration pattern's resource identifier.
192     * @return {@code true} if successful
193     */
194    public boolean playVibration(int resId) {
195        if (!mHapticEnabled) {
196            return false;
197        }
198
199        long[] pattern = mResourceIdToVibrationPatternMap.get(resId);
200
201        if (pattern == null) {
202            final Resources resources = mContext.getResources();
203            final int[] intPattern = resources.getIntArray(resId);
204
205            if (intPattern == null) {
206                return false;
207            }
208
209            pattern = new long[intPattern.length];
210
211            for (int i = 0; i < pattern.length; i++) {
212                pattern[i] = intPattern[i];
213            }
214
215            mResourceIdToVibrationPatternMap.put(resId, pattern);
216        }
217
218        mVibrator.vibrate(pattern, -1);
219
220        return true;
221    }
222
223    private final SoundPool.OnLoadCompleteListener mOnLoadCompleteListener =
224            new SoundPool.OnLoadCompleteListener() {
225                @Override
226                public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
227                    if (status != 0) {
228                        return;
229                    }
230
231                    soundPool.play(sampleId, mVolume, mVolume, 1, 0, DEFAULT_RATE);
232                }
233            };
234}