From c7fd4ac93f3ed7b6bac0145966fb00cfca88309c Mon Sep 17 00:00:00 2001 From: Phillip Hsu Date: Mon, 5 Sep 2016 02:59:51 -0700 Subject: [PATCH] Move looping ringtone code to its own class. Use looped playback for RingtonePickerDialog. --- .../clock2/RingtonePickerDialog.java | 25 +++++--- .../clock2/ringtone/RingtoneLoop.java | 63 +++++++++++++++++++ .../clock2/ringtone/RingtoneService.java | 35 ++--------- 3 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneLoop.java diff --git a/app/src/main/java/com/philliphsu/clock2/RingtonePickerDialog.java b/app/src/main/java/com/philliphsu/clock2/RingtonePickerDialog.java index 0a739a0..5cd4a58 100644 --- a/app/src/main/java/com/philliphsu/clock2/RingtonePickerDialog.java +++ b/app/src/main/java/com/philliphsu/clock2/RingtonePickerDialog.java @@ -2,14 +2,14 @@ package com.philliphsu.clock2; import android.content.DialogInterface; import android.database.Cursor; -import android.media.AudioManager; -import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; +import com.philliphsu.clock2.ringtone.RingtoneLoop; + /** * Created by Phillip Hsu on 9/3/2016. *

@@ -31,7 +31,7 @@ public class RingtonePickerDialog extends BaseAlertDialogFragment { private RingtoneManager mRingtoneManager; private OnRingtoneSelectedListener mOnRingtoneSelectedListener; private Uri mRingtoneUri; - private Ringtone mRingtone; + private RingtoneLoop mRingtone; public interface OnRingtoneSelectedListener { void onRingtoneSelected(Uri ringtoneUri); @@ -66,12 +66,12 @@ public class RingtonePickerDialog extends BaseAlertDialogFragment { .setSingleChoiceItems(cursor, checkedItem, labelColumn, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { + if (mRingtone != null) { + destroyLocalPlayer(); + } // Here, 'which' param refers to the position of the item clicked. mRingtoneUri = mRingtoneManager.getRingtoneUri(which); - mRingtone = mRingtoneManager.getRingtone(which); - // TODO: Deprecated, but setAudioAttributes() is for 21+, so what is the - // pre-21 alternative? - mRingtone.setStreamType(AudioManager.STREAM_ALARM); + mRingtone = new RingtoneLoop(getActivity(), mRingtoneUri); mRingtone.play(); } }); @@ -81,9 +81,7 @@ public class RingtonePickerDialog extends BaseAlertDialogFragment { @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); - if (mRingtone != null && mRingtone.isPlaying()) { - mRingtone.stop(); - } + destroyLocalPlayer(); } @Override @@ -98,4 +96,11 @@ public class RingtonePickerDialog extends BaseAlertDialogFragment { public void setOnRingtoneSelectedListener(OnRingtoneSelectedListener onRingtoneSelectedListener) { mOnRingtoneSelectedListener = onRingtoneSelectedListener; } + + private void destroyLocalPlayer() { + if (mRingtone != null) { + mRingtone.stop(); + mRingtone = null; + } + } } diff --git a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneLoop.java b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneLoop.java new file mode 100644 index 0000000..7e87b5a --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneLoop.java @@ -0,0 +1,63 @@ +package com.philliphsu.clock2.ringtone; + +import android.content.Context; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.net.Uri; + +import java.io.IOException; + +/** + * Created by Phillip Hsu on 9/5/2016. + * + * A MediaPlayer configured to play a ringtone in a loop. + */ +public final class RingtoneLoop { + + private final Context mContext; + private final AudioManager mAudioManager; + private final Uri mUri; + + private MediaPlayer mMediaPlayer; + + public RingtoneLoop(Context context, Uri uri) { + mContext = context; + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + mUri = uri; + } + + public void play() { + try { + mMediaPlayer = new MediaPlayer(); + mMediaPlayer.setDataSource(mContext, mUri); + if (mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { + // "Must call this method before prepare() or prepareAsync() in order + // for the target stream type to become effective thereafter." + mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); + mMediaPlayer.setLooping(true); + // There is prepare() and prepareAsync(). + // "For files, it is OK to call prepare(), which blocks until + // MediaPlayer is ready for playback." + mMediaPlayer.prepare(); + mMediaPlayer.start(); + } + } catch (SecurityException | IOException e) { + destroyLocalPlayer(); + } + } + + public void stop() { + if (mMediaPlayer != null) { + destroyLocalPlayer(); + } + } + + private void destroyLocalPlayer() { + if (mMediaPlayer != null) { + mMediaPlayer.reset(); + mMediaPlayer.release(); + mMediaPlayer = null; + } + } + +} diff --git a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java index e8ea5a1..a13860b 100644 --- a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java +++ b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java @@ -7,7 +7,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.media.AudioManager; -import android.media.MediaPlayer; import android.net.Uri; import android.os.Handler; import android.os.IBinder; @@ -19,7 +18,6 @@ import android.util.Log; import com.philliphsu.clock2.R; import com.philliphsu.clock2.util.LocalBroadcastHelper; -import java.io.IOException; import java.util.concurrent.TimeUnit; /** @@ -41,7 +39,7 @@ public abstract class RingtoneService extends Service { public static final String EXTRA_RINGING_OBJECT = RingtoneActivity.EXTRA_RINGING_OBJECT; private AudioManager mAudioManager; - private MediaPlayer mMediaPlayer; + private RingtoneLoop mRingtone; private Vibrator mVibrator; private T mRingingObject; @@ -97,7 +95,7 @@ public abstract class RingtoneService extends Service { } } // Play ringtone, if not already playing - if (mAudioManager == null && mMediaPlayer == null) { + if (mAudioManager == null && mRingtone == null) { // TOneverDO: Pass 0 as the first argument startForeground(R.id.ringtone_service_notification, getForegroundNotification()); @@ -110,23 +108,8 @@ public abstract class RingtoneService extends Service { // Request permanent focus, as ringing could last several minutes AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - try { - mMediaPlayer = new MediaPlayer(); - mMediaPlayer.setDataSource(this, getRingtoneUri()); - if (mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { - // "Must call this method before prepare() or prepareAsync() in order - // for the target stream type to become effective thereafter." - mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); - mMediaPlayer.setLooping(true); - // There is prepare() and prepareAsync(). - // "For files, it is OK to call prepare(), which blocks until - // MediaPlayer is ready for playback." - mMediaPlayer.prepare(); - mMediaPlayer.start(); - } - } catch (SecurityException | IOException e) { - destroyLocalPlayer(); - } + mRingtone = new RingtoneLoop(this, getRingtoneUri()); + mRingtone.play(); if (doesVibrate()) { mVibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); mVibrator.vibrate(new long[] { // apply pattern @@ -156,7 +139,7 @@ public abstract class RingtoneService extends Service { @Override public void onDestroy() { Log.d(TAG, "onDestroy()"); - destroyLocalPlayer(); + mRingtone.stop(); mAudioManager.abandonAudioFocus(null); // no listener was set if (mVibrator != null) { mVibrator.cancel(); @@ -205,12 +188,4 @@ public abstract class RingtoneService extends Service { protected final T getRingingObject() { return mRingingObject; } - - private void destroyLocalPlayer() { - if (mMediaPlayer != null) { - mMediaPlayer.reset(); - mMediaPlayer.release(); - mMediaPlayer = null; - } - } }