OSDN Git Service

c862292dc3d149841ef63219359f62a9f426ffc1
[android-x86/packages-apps-AlarmClock.git] / src / com / android / alarmclock / AlarmKlaxon.java
1 /*
2  * Copyright (C) 2008 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.alarmclock;
18
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.content.res.AssetFileDescriptor;
22 import android.media.AudioManager;
23 import android.media.MediaPlayer;
24 import android.media.MediaPlayer.OnErrorListener;
25 import android.net.Uri;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Vibrator;
29
30 /**
31  * Manages alarms and vibe.  Singleton, so it can be initiated in
32  * AlarmReceiver and shut down in the AlarmAlert activity
33  */
34 class AlarmKlaxon implements Alarms.AlarmSettings {
35
36     interface KillerCallback {
37         public void onKilled();
38     }
39
40     /** Play alarm up to 10 minutes before silencing */
41     final static int ALARM_TIMEOUT_SECONDS = 10 * 60;
42
43     private static long[] sVibratePattern = new long[] { 500, 500 };
44
45     private static AlarmKlaxon sInstance;
46
47     private int mAlarmId;
48     private String mAlert;
49     private Alarms.DaysOfWeek mDaysOfWeek;
50     private boolean mVibrate;
51
52     private boolean mPlaying = false;
53
54     private Vibrator mVibrator;
55     private MediaPlayer mMediaPlayer;
56
57     private Handler mTimeout;
58     private KillerCallback mKillerCallback;
59
60
61     static synchronized AlarmKlaxon getInstance() {
62         if (sInstance == null) sInstance = new AlarmKlaxon();
63         return sInstance;
64     }
65
66     private AlarmKlaxon() {
67         mVibrator = new Vibrator();
68     }
69
70     public void reportAlarm(
71             int idx, boolean enabled, int hour, int minutes,
72             Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String message,
73             String alert) {
74         if (Log.LOGV) Log.v("AlarmKlaxon.reportAlarm: " + idx + " " + hour +
75                             " " + minutes + " dow " + daysOfWeek);
76         mAlert = alert;
77         mDaysOfWeek = daysOfWeek;
78         mVibrate = vibrate;
79     }
80
81     synchronized void play(Context context, int alarmId) {
82         ContentResolver contentResolver = context.getContentResolver();
83
84         if (mPlaying) stop(context, false);
85
86         mAlarmId = alarmId;
87
88         /* this will call reportAlarm() callback */
89         Alarms.getAlarm(contentResolver, this, mAlarmId);
90
91         if (Log.LOGV) Log.v("AlarmKlaxon.play() " + mAlarmId + " alert " + mAlert);
92
93         /* play audio alert */
94         if (mAlert == null) {
95             Log.e("Unable to play alarm: no audio file available");
96         } else {
97             /* we need a new MediaPlayer when we change media URLs */
98             mMediaPlayer = new MediaPlayer();
99             mMediaPlayer.setOnErrorListener(new OnErrorListener() {
100                 public boolean onError(MediaPlayer mp, int what, int extra) {
101                     Log.e("Error occurred while playing audio.");
102                     mp.stop();
103                     mp.release();
104                     mMediaPlayer = null;
105                     return true;
106                 }
107             });
108
109             try {
110                 mMediaPlayer.setDataSource(context, Uri.parse(mAlert));
111             } catch (Exception ex) {
112                 Log.v("Using the fallback ringtone");
113                 /* The alert may be on the sd card which could be busy right
114                  * now. Use the fallback ringtone. */
115                 AssetFileDescriptor afd =
116                         context.getResources().openRawResourceFd(
117                                 com.android.internal.R.raw.fallbackring);
118                 if (afd != null) {
119                     try {
120                         mMediaPlayer.setDataSource(afd.getFileDescriptor(),
121                                 afd.getStartOffset(), afd.getLength());
122                         afd.close();
123                     } catch (Exception ex2) {
124                         Log.e("Failed to play fallback ringtone", ex2);
125                         /* At this point we just don't play anything */
126                     }
127                 }
128             }
129             /* Now try to play the alert. */
130             try {
131                 mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
132                 mMediaPlayer.setLooping(true);
133                 mMediaPlayer.prepare();
134                 mMediaPlayer.start();
135             } catch (Exception ex) {
136                 Log.e("Error playing alarm: " + mAlert, ex);
137             }
138         }
139
140         /* Start the vibrator after everything is ok with the media player */
141         if (mVibrate) {
142             mVibrator.vibrate(sVibratePattern, 0);
143         } else {
144             mVibrator.cancel();
145         }
146
147         enableKiller();
148         mPlaying = true;
149     }
150
151
152     /**
153      * Stops alarm audio and disables alarm if it not snoozed and not
154      * repeating
155      */
156     synchronized void stop(Context context, boolean snoozed) {
157         if (Log.LOGV) Log.v("AlarmKlaxon.stop() " + mAlarmId);
158         if (mPlaying) {
159             mPlaying = false;
160
161             // Stop audio playing
162             if (mMediaPlayer != null) {
163                 mMediaPlayer.stop();
164                 mMediaPlayer.release();
165                 mMediaPlayer = null;
166             }
167
168             // Stop vibrator
169             mVibrator.cancel();
170
171             /* disable alarm only if it is not set to repeat */
172             if (!snoozed && ((mDaysOfWeek == null || !mDaysOfWeek.isRepeatSet()))) {
173                 Alarms.enableAlarm(context, mAlarmId, false);
174             }
175         }
176         disableKiller();
177     }
178
179     /**
180      * This callback called when alarm killer times out unattended
181      * alarm
182      */
183     void setKillerCallback(KillerCallback killerCallback) {
184         mKillerCallback = killerCallback;
185     }
186
187     /**
188      * Kills alarm audio after ALARM_TIMEOUT_SECONDS, so the alarm
189      * won't run all day.
190      *
191      * This just cancels the audio, but leaves the notification
192      * popped, so the user will know that the alarm tripped.
193      */
194     private void enableKiller() {
195         mTimeout = new Handler();
196         mTimeout.postDelayed(new Runnable() {
197                 public void run() {
198                     if (Log.LOGV) Log.v("*********** Alarm killer triggered *************");
199                     if (mKillerCallback != null) mKillerCallback.onKilled();
200                 }
201             }, 1000 * ALARM_TIMEOUT_SECONDS);
202     }
203
204     private void disableKiller() {
205         if (mTimeout != null) {
206             mTimeout.removeCallbacksAndMessages(null);
207             mTimeout = null;
208         }
209     }
210
211
212 }