• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.deskclock.alarms;
18 
19 import android.content.Context;
20 import android.content.res.AssetFileDescriptor;
21 import android.media.AudioAttributes;
22 import android.media.AudioManager;
23 import android.media.MediaPlayer;
24 import android.media.MediaPlayer.OnErrorListener;
25 import android.media.RingtoneManager;
26 import android.net.Uri;
27 import android.os.Vibrator;
28 
29 import com.android.deskclock.LogUtils;
30 import com.android.deskclock.R;
31 import com.android.deskclock.provider.AlarmInstance;
32 
33 import java.io.IOException;
34 
35 /**
36  * Manages playing ringtone and vibrating the device.
37  */
38 public 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 
stop(Context context)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 
start(final Context context, AlarmInstance instance, boolean inTelephoneCall)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.
startAlarm(Context context, MediaPlayer player)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 
setDataSourceFromResource(Context context, MediaPlayer player, int res)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