/* * Copyright (C) 2013 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.embeddedlog.LightUpDroid.alarms; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnErrorListener; import android.media.RingtoneManager; import android.net.Uri; import android.os.Vibrator; import com.embeddedlog.LightUpDroid.Log; import com.embeddedlog.LightUpDroid.R; import com.embeddedlog.LightUpDroid.provider.AlarmInstance; import java.io.IOException; /** * Manages playing ringtone and vibrating the device. */ public class AlarmKlaxon { private static final long[] sVibratePattern = new long[] { 500, 500 }; // Volume suggested by media team for in-call alarms. private static final float IN_CALL_VOLUME = 0.125f; private static boolean sStarted = false; private static MediaPlayer sMediaPlayer = null; public static void stop(Context context) { Log.v("AlarmKlaxon.stop()"); if (sStarted) { sStarted = false; // Stop audio playing if (sMediaPlayer != null) { sMediaPlayer.stop(); AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); audioManager.abandonAudioFocus(null); sMediaPlayer.release(); sMediaPlayer = null; } ((Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE)).cancel(); } } public static void start(final Context context, AlarmInstance instance, boolean inTelephoneCall) { Log.v("AlarmKlaxon.start()"); // Make sure we are stop before starting stop(context); if (!AlarmInstance.NO_RINGTONE_URI.equals(instance.mRingtone)) { Uri alarmNoise = instance.mRingtone; // Fall back on the default alarm if the database does not have an // alarm stored. if (alarmNoise == null) { alarmNoise = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); if (Log.LOGV) { Log.v("Using default alarm: " + alarmNoise.toString()); } } // TODO: Reuse mMediaPlayer instead of creating a new one and/or use RingtoneManager. sMediaPlayer = new MediaPlayer(); sMediaPlayer.setOnErrorListener(new OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Log.e("Error occurred while playing audio. Stopping AlarmKlaxon."); AlarmKlaxon.stop(context); return true; } }); try { // Check if we are in a call. If we are, use the in-call alarm // resource at a low volume to not disrupt the call. if (inTelephoneCall) { Log.v("Using the in-call alarm"); sMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME); setDataSourceFromResource(context, sMediaPlayer, R.raw.in_call_alarm); } else { sMediaPlayer.setDataSource(context, alarmNoise); } startAlarm(context, sMediaPlayer); } catch (Exception ex) { Log.v("Using the fallback ringtone"); // The alarmNoise may be on the sd card which could be busy right // now. Use the fallback ringtone. try { // Must reset the media player to clear the error state. sMediaPlayer.reset(); setDataSourceFromResource(context, sMediaPlayer, R.raw.fallbackring); startAlarm(context, sMediaPlayer); } catch (Exception ex2) { // At this point we just don't play anything. Log.e("Failed to play fallback ringtone", ex2); } } } if (instance.mVibrate && !inTelephoneCall) { Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(sVibratePattern, 0); } sStarted = true; } // Do the common stuff when starting the alarm. private static void startAlarm(Context context, MediaPlayer player) throws IOException { AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // do not play alarms if stream volume is 0 (typically because ringer mode is silent). if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { player.setAudioStreamType(AudioManager.STREAM_ALARM); player.setLooping(true); player.prepare(); audioManager.requestAudioFocus(null, AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); player.start(); } } private static void setDataSourceFromResource(Context context, MediaPlayer player, int res) throws IOException { AssetFileDescriptor afd = context.getResources().openRawResourceFd(res); if (afd != null) { player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); afd.close(); } } }