OSDN Git Service

Automated import from //branches/cupcake/...@142551,142551
[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.content.res.Resources;
23 import android.media.AudioManager;
24 import android.media.MediaPlayer;
25 import android.media.MediaPlayer.OnErrorListener;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.os.Vibrator;
31 import android.telephony.TelephonyManager;
32
33 /**
34  * Manages alarms and vibe.  Singleton, so it can be initiated in
35  * AlarmReceiver and shut down in the AlarmAlert activity
36  */
37 class AlarmKlaxon implements Alarms.AlarmSettings {
38
39     interface KillerCallback {
40         public void onKilled();
41     }
42
43     /** Play alarm up to 10 minutes before silencing */
44     final static int ALARM_TIMEOUT_SECONDS = 10 * 60;
45
46     private static final long[] sVibratePattern = new long[] { 500, 500 };
47
48     private int mAlarmId;
49     private String mAlert;
50     private Alarms.DaysOfWeek mDaysOfWeek;
51     private boolean mVibrate;
52     private boolean mPlaying = false;
53     private Vibrator mVibrator;
54     private MediaPlayer mMediaPlayer;
55     private KillerCallback mKillerCallback;
56
57     // Internal messages
58     private static final int KILLER = 1000;
59     private static final int PLAY   = 1001;
60     private Handler mHandler = new Handler() {
61         public void handleMessage(Message msg) {
62             switch (msg.what) {
63                 case KILLER:
64                     if (Log.LOGV) {
65                         Log.v("*********** Alarm killer triggered ***********");
66                     }
67                     if (mKillerCallback != null) {
68                         mKillerCallback.onKilled();
69                     }
70                     break;
71                 case PLAY:
72                     play((Context) msg.obj, msg.arg1);
73                     break;
74             }
75         }
76     };
77
78     AlarmKlaxon() {
79         mVibrator = new Vibrator();
80     }
81
82     public void reportAlarm(
83             int idx, boolean enabled, int hour, int minutes,
84             Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String message,
85             String alert) {
86         if (Log.LOGV) Log.v("AlarmKlaxon.reportAlarm: " + idx + " " + hour +
87                             " " + minutes + " dow " + daysOfWeek);
88         mAlert = alert;
89         mDaysOfWeek = daysOfWeek;
90         mVibrate = vibrate;
91     }
92
93     public void postPlay(final Context context, final int alarmId) {
94         mHandler.sendMessage(mHandler.obtainMessage(PLAY, alarmId, 0, context));
95     }
96
97     // Volume suggested by media team for in-call alarms.
98     private static final float IN_CALL_VOLUME = 0.125f;
99
100     private void play(Context context, int alarmId) {
101         ContentResolver contentResolver = context.getContentResolver();
102
103         if (mPlaying) stop(context, false);
104
105         mAlarmId = alarmId;
106
107         /* this will call reportAlarm() callback */
108         Alarms.getAlarm(contentResolver, this, mAlarmId);
109
110         if (Log.LOGV) Log.v("AlarmKlaxon.play() " + mAlarmId + " alert " + mAlert);
111
112         // TODO: Reuse mMediaPlayer instead of creating a new one and/or use
113         // RingtoneManager.
114         mMediaPlayer = new MediaPlayer();
115         mMediaPlayer.setOnErrorListener(new OnErrorListener() {
116             public boolean onError(MediaPlayer mp, int what, int extra) {
117                 Log.e("Error occurred while playing audio.");
118                 mp.stop();
119                 mp.release();
120                 mMediaPlayer = null;
121                 return true;
122             }
123         });
124
125         try {
126             TelephonyManager tm = (TelephonyManager) context.getSystemService(
127                     Context.TELEPHONY_SERVICE);
128             // Check if we are in a call. If we are, use the in-call alarm
129             // resource at a low volume to not disrupt the call.
130             if (tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
131                 Log.v("Using the in-call alarm");
132                 mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
133                 setDataSourceFromResource(context.getResources(),
134                         mMediaPlayer, R.raw.in_call_alarm);
135             } else {
136                 mMediaPlayer.setDataSource(context, Uri.parse(mAlert));
137             }
138             startAlarm(mMediaPlayer);
139         } catch (Exception ex) {
140             Log.v("Using the fallback ringtone");
141             // The alert may be on the sd card which could be busy right now.
142             // Use the fallback ringtone.
143             try {
144                 // Must reset the media player to clear the error state.
145                 mMediaPlayer.reset();
146                 setDataSourceFromResource(context.getResources(), mMediaPlayer,
147                         com.android.internal.R.raw.fallbackring);
148                 startAlarm(mMediaPlayer);
149             } catch (Exception ex2) {
150                 // At this point we just don't play anything.
151                 Log.e("Failed to play fallback ringtone", ex2);
152             }
153         }
154
155         /* Start the vibrator after everything is ok with the media player */
156         if (mVibrate) {
157             mVibrator.vibrate(sVibratePattern, 0);
158         } else {
159             mVibrator.cancel();
160         }
161
162         enableKiller();
163         mPlaying = true;
164     }
165
166     // Do the common stuff when starting the alarm.
167     private void startAlarm(MediaPlayer player)
168             throws java.io.IOException, IllegalArgumentException,
169                    IllegalStateException {
170         player.setAudioStreamType(AudioManager.STREAM_ALARM);
171         player.setLooping(true);
172         player.prepare();
173         player.start();
174     }
175
176     private void setDataSourceFromResource(Resources resources,
177             MediaPlayer player, int res) throws java.io.IOException {
178         AssetFileDescriptor afd = resources.openRawResourceFd(res);
179         if (afd != null) {
180             player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
181                     afd.getLength());
182             afd.close();
183         }
184     }
185
186     /**
187      * Stops alarm audio and disables alarm if it not snoozed and not
188      * repeating
189      */
190     public void stop(Context context, boolean snoozed) {
191         if (Log.LOGV) Log.v("AlarmKlaxon.stop() " + mAlarmId);
192         if (mPlaying) {
193             mPlaying = false;
194
195             // Stop audio playing
196             if (mMediaPlayer != null) {
197                 mMediaPlayer.stop();
198                 mMediaPlayer.release();
199                 mMediaPlayer = null;
200             }
201
202             // Stop vibrator
203             mVibrator.cancel();
204
205             /* disable alarm only if it is not set to repeat */
206             if (!snoozed && ((mDaysOfWeek == null || !mDaysOfWeek.isRepeatSet()))) {
207                 Alarms.enableAlarm(context, mAlarmId, false);
208             }
209         }
210         disableKiller();
211     }
212
213     /**
214      * This callback called when alarm killer times out unattended
215      * alarm
216      */
217     public void setKillerCallback(KillerCallback killerCallback) {
218         mKillerCallback = killerCallback;
219     }
220
221     /**
222      * Kills alarm audio after ALARM_TIMEOUT_SECONDS, so the alarm
223      * won't run all day.
224      *
225      * This just cancels the audio, but leaves the notification
226      * popped, so the user will know that the alarm tripped.
227      */
228     private void enableKiller() {
229         mHandler.sendMessageDelayed(mHandler.obtainMessage(KILLER),
230                 1000 * ALARM_TIMEOUT_SECONDS);
231     }
232
233     private void disableKiller() {
234         mHandler.removeMessages(KILLER);
235     }
236
237
238 }