11c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski/*
21c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * Copyright (C) 2013 The Android Open Source Project
31c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski *
41c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * Licensed under the Apache License, Version 2.0 (the "License");
51c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * you may not use this file except in compliance with the License.
61c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * You may obtain a copy of the License at
71c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski *
81c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski *      http://www.apache.org/licenses/LICENSE-2.0
91c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski *
101c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * Unless required by applicable law or agreed to in writing, software
111c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * distributed under the License is distributed on an "AS IS" BASIS,
121c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * See the License for the specific language governing permissions and
141c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * limitations under the License.
151c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski */
161c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
171c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskipackage com.android.deskclock.alarms;
181c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
191c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.content.Context;
201c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.content.res.AssetFileDescriptor;
211c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.media.AudioManager;
221c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.media.MediaPlayer;
231c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.media.MediaPlayer.OnErrorListener;
241c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.media.RingtoneManager;
251c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.net.Uri;
261c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.os.Vibrator;
271c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
281c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport com.android.deskclock.Log;
291c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport com.android.deskclock.R;
301c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport com.android.deskclock.provider.AlarmInstance;
311c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
321c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport java.io.IOException;
331c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
341c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski/**
351c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * Manages playing ringtone and vibrating the device.
361c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski */
371c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskipublic class AlarmKlaxon {
381c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private static final long[] sVibratePattern = new long[] { 500, 500 };
391c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
401c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    // Volume suggested by media team for in-call alarms.
411c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private static final float IN_CALL_VOLUME = 0.125f;
421c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
431c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private static boolean sStarted = false;
441c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private static MediaPlayer sMediaPlayer = null;
451c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
461c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public static void stop(Context context) {
471c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        Log.v("AlarmKlaxon.stop()");
481c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
491c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        if (sStarted) {
501c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            sStarted = false;
511c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            // Stop audio playing
521c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            if (sMediaPlayer != null) {
531c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                sMediaPlayer.stop();
541c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                AudioManager audioManager = (AudioManager)
551c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                        context.getSystemService(Context.AUDIO_SERVICE);
561c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                audioManager.abandonAudioFocus(null);
571c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                sMediaPlayer.release();
581c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                sMediaPlayer = null;
591c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            }
601c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
611c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            ((Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE)).cancel();
621c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        }
631c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
641c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
651c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public static void start(final Context context, AlarmInstance instance,
661c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            boolean inTelephoneCall) {
671c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        Log.v("AlarmKlaxon.start()");
681c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        // Make sure we are stop before starting
691c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        stop(context);
701c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
711c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        if (!AlarmInstance.NO_RINGTONE_URI.equals(instance.mRingtone)) {
721c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            Uri alarmNoise = instance.mRingtone;
731c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            // Fall back on the default alarm if the database does not have an
741c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            // alarm stored.
751c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            if (alarmNoise == null) {
761c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                alarmNoise = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
771c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                if (Log.LOGV) {
781c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    Log.v("Using default alarm: " + alarmNoise.toString());
791c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                }
801c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            }
811c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
821c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            // TODO: Reuse mMediaPlayer instead of creating a new one and/or use RingtoneManager.
831c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            sMediaPlayer = new MediaPlayer();
841c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            sMediaPlayer.setOnErrorListener(new OnErrorListener() {
851c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                @Override
861c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                public boolean onError(MediaPlayer mp, int what, int extra) {
871c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    Log.e("Error occurred while playing audio. Stopping AlarmKlaxon.");
881c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    AlarmKlaxon.stop(context);
891c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    return true;
901c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                }
911c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            });
921c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
931c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            try {
941c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                // Check if we are in a call. If we are, use the in-call alarm
951c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                // resource at a low volume to not disrupt the call.
961c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                if (inTelephoneCall) {
971c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    Log.v("Using the in-call alarm");
981c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    sMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
991c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    setDataSourceFromResource(context, sMediaPlayer, R.raw.in_call_alarm);
1001c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                } else {
1011c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    sMediaPlayer.setDataSource(context, alarmNoise);
1021c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                }
1031c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                startAlarm(context, sMediaPlayer);
1041c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            } catch (Exception ex) {
1051c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                Log.v("Using the fallback ringtone");
1061c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                // The alarmNoise may be on the sd card which could be busy right
1071c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                // now. Use the fallback ringtone.
1081c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                try {
1091c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    // Must reset the media player to clear the error state.
1101c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    sMediaPlayer.reset();
1111c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    setDataSourceFromResource(context, sMediaPlayer, R.raw.fallbackring);
1121c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    startAlarm(context, sMediaPlayer);
1131c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                } catch (Exception ex2) {
1141c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    // At this point we just don't play anything.
1151c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    Log.e("Failed to play fallback ringtone", ex2);
1161c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                }
1171c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            }
1181c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        }
1191c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1201c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        if (instance.mVibrate) {
1211c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
1221c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            vibrator.vibrate(sVibratePattern, 0);
1231c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        }
1241c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1251c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        sStarted = true;
1261c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
1271c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1281c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    // Do the common stuff when starting the alarm.
1291c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private static void startAlarm(Context context, MediaPlayer player) throws IOException {
1301c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
1311c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        // do not play alarms if stream volume is 0 (typically because ringer mode is silent).
1321c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
1331c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            player.setAudioStreamType(AudioManager.STREAM_ALARM);
1341c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            player.setLooping(true);
1351c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            player.prepare();
1361c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            audioManager.requestAudioFocus(null,
1371c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
1381c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            player.start();
1391c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        }
1401c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
1411c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1421c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private static void setDataSourceFromResource(Context context, MediaPlayer player, int res)
1431c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            throws IOException {
1441c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        AssetFileDescriptor afd = context.getResources().openRawResourceFd(res);
1451c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        if (afd != null) {
1461c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
1471c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            afd.close();
1481c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        }
1491c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
1501c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski}
151