OSDN Git Service

auto import from //branches/cupcake/...@131421
[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     final static String ICICLE_PLAYING = "IciclePlaying";
43     final static String ICICLE_ALARMID = "IcicleAlarmId";
44
45     private static long[] sVibratePattern = new long[] { 500, 500 };
46
47     private static AlarmKlaxon sInstance;
48
49     private int mAlarmId;
50     private String mAlert;
51     private Alarms.DaysOfWeek mDaysOfWeek;
52     private boolean mVibrate;
53
54     private boolean mPlaying = false;
55
56     private Vibrator mVibrator;
57     private MediaPlayer mMediaPlayer;
58
59     private Handler mTimeout;
60     private KillerCallback mKillerCallback;
61
62
63     static synchronized AlarmKlaxon getInstance() {
64         if (sInstance == null) sInstance = new AlarmKlaxon();
65         return sInstance;
66     }
67
68     private AlarmKlaxon() {
69         mVibrator = new Vibrator();
70     }
71
72     public void reportAlarm(
73             int idx, boolean enabled, int hour, int minutes,
74             Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String message,
75             String alert) {
76         if (Log.LOGV) Log.v("AlarmKlaxon.reportAlarm: " + idx + " " + hour +
77                             " " + minutes + " dow " + daysOfWeek);
78         mAlert = alert;
79         mDaysOfWeek = daysOfWeek;
80         mVibrate = vibrate;
81     }
82
83     synchronized void play(Context context, int alarmId) {
84         ContentResolver contentResolver = context.getContentResolver();
85
86         if (mPlaying) stop(context, false);
87
88         mAlarmId = alarmId;
89
90         /* this will call reportAlarm() callback */
91         Alarms.getAlarm(contentResolver, this, mAlarmId);
92
93         if (Log.LOGV) Log.v("AlarmKlaxon.play() " + mAlarmId + " alert " + mAlert);
94
95         /* play audio alert */
96         if (mAlert == null) {
97             Log.e("Unable to play alarm: no audio file available");
98         } else {
99             /* we need a new MediaPlayer when we change media URLs */
100             mMediaPlayer = new MediaPlayer();
101             mMediaPlayer.setOnErrorListener(new OnErrorListener() {
102                 public boolean onError(MediaPlayer mp, int what, int extra) {
103                     Log.e("Error occurred while playing audio.");
104                     mp.stop();
105                     mp.release();
106                     mMediaPlayer = null;
107                     return true;
108                 }
109             });
110
111             try {
112                 mMediaPlayer.setDataSource(context, Uri.parse(mAlert));
113             } catch (Exception ex) {
114                 Log.v("Using the fallback ringtone");
115                 /* The alert may be on the sd card which could be busy right
116                  * now. Use the fallback ringtone. */
117                 AssetFileDescriptor afd =
118                         context.getResources().openRawResourceFd(
119                                 com.android.internal.R.raw.fallbackring);
120                 if (afd != null) {
121                     try {
122                         mMediaPlayer.setDataSource(afd.getFileDescriptor(),
123                                 afd.getStartOffset(), afd.getLength());
124                         afd.close();
125                     } catch (Exception ex2) {
126                         Log.e("Failed to play fallback ringtone", ex2);
127                         /* At this point we just don't play anything */
128                     }
129                 }
130             }
131             /* Now try to play the alert. */
132             try {
133                 mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
134                 mMediaPlayer.setLooping(true);
135                 mMediaPlayer.prepare();
136                 mMediaPlayer.start();
137             } catch (Exception ex) {
138                 Log.e("Error playing alarm: " + mAlert, ex);
139             }
140         }
141
142         /* Start the vibrator after everything is ok with the media player */
143         if (mVibrate) {
144             mVibrator.vibrate(sVibratePattern, 0);
145         } else {
146             mVibrator.cancel();
147         }
148
149         enableKiller();
150         mPlaying = true;
151     }
152
153
154     /**
155      * Stops alarm audio and disables alarm if it not snoozed and not
156      * repeating
157      */
158     synchronized void stop(Context context, boolean snoozed) {
159         if (Log.LOGV) Log.v("AlarmKlaxon.stop() " + mAlarmId);
160         if (mPlaying) {
161             mPlaying = false;
162
163             // Stop audio playing
164             if (mMediaPlayer != null) {
165                 mMediaPlayer.stop();
166                 mMediaPlayer.release();
167                 mMediaPlayer = null;
168             }
169
170             // Stop vibrator
171             mVibrator.cancel();
172
173             /* disable alarm only if it is not set to repeat */
174             if (!snoozed && ((mDaysOfWeek == null || !mDaysOfWeek.isRepeatSet()))) {
175                 Alarms.enableAlarm(context, mAlarmId, false);
176             }
177         }
178         disableKiller();
179     }
180
181     /**
182      * This callback called when alarm killer times out unattended
183      * alarm
184      */
185     void setKillerCallback(KillerCallback killerCallback) {
186         mKillerCallback = killerCallback;
187     }
188
189
190     /**
191      * Called by the AlarmAlert activity on configuration change
192      */
193     protected void onSaveInstanceState(Bundle icicle) {
194         icicle.putBoolean(ICICLE_PLAYING, mPlaying);
195         icicle.putInt(ICICLE_ALARMID, mAlarmId);
196     }
197
198     /**
199      * Restores alarm playback state on configuration change
200      */
201     void restoreInstanceState(Context context, Bundle icicle) {
202         if (!mPlaying &&
203             icicle != null &&
204             icicle.containsKey(ICICLE_PLAYING) &&
205             icicle.getBoolean(ICICLE_PLAYING)) {
206             play(context, icicle.getInt(ICICLE_ALARMID));
207         }
208     }
209
210     /**
211      * Kills alarm audio after ALARM_TIMEOUT_SECONDS, so the alarm
212      * won't run all day.
213      *
214      * This just cancels the audio, but leaves the notification
215      * popped, so the user will know that the alarm tripped.
216      */
217     private void enableKiller() {
218         mTimeout = new Handler();
219         mTimeout.postDelayed(new Runnable() {
220                 public void run() {
221                     if (Log.LOGV) Log.v("*********** Alarm killer triggered *************");
222                     if (mKillerCallback != null) mKillerCallback.onKilled();
223                 }
224             }, 1000 * ALARM_TIMEOUT_SECONDS);
225     }
226
227     private void disableKiller() {
228         if (mTimeout != null) {
229             mTimeout.removeCallbacksAndMessages(null);
230             mTimeout = null;
231         }
232     }
233
234
235 }