1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.deskclock.alarms; 18 19import android.content.Context; 20import android.content.res.AssetFileDescriptor; 21import android.media.AudioAttributes; 22import android.media.AudioManager; 23import android.media.MediaPlayer; 24import android.media.MediaPlayer.OnErrorListener; 25import android.media.RingtoneManager; 26import android.net.Uri; 27import android.os.Vibrator; 28 29import com.android.deskclock.LogUtils; 30import com.android.deskclock.R; 31import com.android.deskclock.provider.AlarmInstance; 32 33import java.io.IOException; 34 35/** 36 * Manages playing ringtone and vibrating the device. 37 */ 38public class AlarmKlaxon { 39 private static final long[] sVibratePattern = new long[] { 500, 500 }; 40 41 // Volume suggested by media team for in-call alarms. 42 private static final float IN_CALL_VOLUME = 0.125f; 43 44 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() 45 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 46 .setUsage(AudioAttributes.USAGE_ALARM) 47 .build(); 48 49 private static boolean sStarted = false; 50 private static MediaPlayer sMediaPlayer = null; 51 52 public static void stop(Context context) { 53 LogUtils.v("AlarmKlaxon.stop()"); 54 55 if (sStarted) { 56 sStarted = false; 57 // Stop audio playing 58 if (sMediaPlayer != null) { 59 sMediaPlayer.stop(); 60 AudioManager audioManager = (AudioManager) 61 context.getSystemService(Context.AUDIO_SERVICE); 62 audioManager.abandonAudioFocus(null); 63 sMediaPlayer.release(); 64 sMediaPlayer = null; 65 } 66 67 ((Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE)).cancel(); 68 } 69 } 70 71 public static void start(final Context context, AlarmInstance instance, 72 boolean inTelephoneCall) { 73 LogUtils.v("AlarmKlaxon.start()"); 74 // Make sure we are stop before starting 75 stop(context); 76 77 if (!AlarmInstance.NO_RINGTONE_URI.equals(instance.mRingtone)) { 78 Uri alarmNoise = instance.mRingtone; 79 // Fall back on the default alarm if the database does not have an 80 // alarm stored. 81 if (alarmNoise == null) { 82 alarmNoise = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); 83 LogUtils.v("Using default alarm: " + alarmNoise.toString()); 84 } 85 86 // TODO: Reuse mMediaPlayer instead of creating a new one and/or use RingtoneManager. 87 sMediaPlayer = new MediaPlayer(); 88 sMediaPlayer.setOnErrorListener(new OnErrorListener() { 89 @Override 90 public boolean onError(MediaPlayer mp, int what, int extra) { 91 LogUtils.e("Error occurred while playing audio. Stopping AlarmKlaxon."); 92 AlarmKlaxon.stop(context); 93 return true; 94 } 95 }); 96 97 try { 98 // Check if we are in a call. If we are, use the in-call alarm 99 // resource at a low volume to not disrupt the call. 100 if (inTelephoneCall) { 101 LogUtils.v("Using the in-call alarm"); 102 sMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME); 103 setDataSourceFromResource(context, sMediaPlayer, R.raw.in_call_alarm); 104 } else { 105 sMediaPlayer.setDataSource(context, alarmNoise); 106 } 107 startAlarm(context, sMediaPlayer); 108 } catch (Exception ex) { 109 LogUtils.v("Using the fallback ringtone"); 110 // The alarmNoise may be on the sd card which could be busy right 111 // now. Use the fallback ringtone. 112 try { 113 // Must reset the media player to clear the error state. 114 sMediaPlayer.reset(); 115 setDataSourceFromResource(context, sMediaPlayer, R.raw.fallbackring); 116 startAlarm(context, sMediaPlayer); 117 } catch (Exception ex2) { 118 // At this point we just don't play anything. 119 LogUtils.e("Failed to play fallback ringtone", ex2); 120 } 121 } 122 } 123 124 if (instance.mVibrate) { 125 Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); 126 vibrator.vibrate(sVibratePattern, 0, VIBRATION_ATTRIBUTES); 127 } 128 129 sStarted = true; 130 } 131 132 // Do the common stuff when starting the alarm. 133 private static void startAlarm(Context context, MediaPlayer player) throws IOException { 134 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 135 // do not play alarms if stream volume is 0 (typically because ringer mode is silent). 136 if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { 137 player.setAudioStreamType(AudioManager.STREAM_ALARM); 138 player.setLooping(true); 139 player.prepare(); 140 audioManager.requestAudioFocus(null, 141 AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 142 player.start(); 143 } 144 } 145 146 private static void setDataSourceFromResource(Context context, MediaPlayer player, int res) 147 throws IOException { 148 AssetFileDescriptor afd = context.getResources().openRawResourceFd(res); 149 if (afd != null) { 150 player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 151 afd.close(); 152 } 153 } 154} 155