This is an application that measures the sound intensity and in accordance with that measured value it sets the seek bar and in turn it adjusts the ringer volume in accordance with the seek bar automatically.
I want to run this application in foreground as well as in background
when user destroys it. Because the user will definately be needing
the app even after he quits it. I have read the documentation of
creating a service but the problem is i want full code same as
activity to be run in the background after the app destroyal..So any
help would be greatly appreciated.
package com.example.soundmeter;
import android.app.Activity;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;
import android.content.Context;
import android.media.AudioManager;
import android.view.KeyEvent;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import com.example.soundmeter.R;
public class MainActivity extends Activity {
//public static boolean isService = false;
TextView mStatusView;
MediaRecorder mRecorder;
Thread runner;
private static double mEMA = 0.0;
static final private double EMA_FILTER = 0.6;
//a variable to store the seek bar from the XML file
public SeekBar volumeBar;
//an AudioManager object, to change the volume settings
private AudioManager amanager;
final Runnable updater = new Runnable(){
public void run(){
updateTv();
};
};
final Handler mHandler = new Handler();
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.main);
super.onCreate(savedInstanceState);
mStatusView = (TextView) findViewById(R.id.status);
//startService(new Intent(MainActivity.this,BackGroundService.class));
if (runner == null)
{
runner = new Thread(){
public void run()
{
while (runner != null)
{
volumeChanger();
try
{
Thread.sleep(5000);
Log.i("Noise", "Tock");
} catch (InterruptedException e) { };
mHandler.post(updater);
}
}
};
runner.start();
Log.d("Noise", "start runner()");
}
}
public void onResume()
{
super.onResume();
startRecorder();
}
public void onPause()
{
super.onResume();
startRecorder();
//super.onPause();
//super.stopRecorder();
}
/*#Override
public void onBackPressed()
{
super.onResume();
startRecorder();
}*/
public void startRecorder(){
if (mRecorder == null)
{
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mRecorder.setOutputFile("/dev/null");
try
{
mRecorder.prepare();
}catch (java.io.IOException ioe) {
android.util.Log.e("[Monkey]", "IOException: " +
android.util.Log.getStackTraceString(ioe));
}catch (java.lang.SecurityException e) {
android.util.Log.e("[Monkey]", "SecurityException: " +
android.util.Log.getStackTraceString(e));
}
try
{
mRecorder.start();
}catch (java.lang.SecurityException e) {
android.util.Log.e("[Monkey]", "SecurityException: " +
android.util.Log.getStackTraceString(e));
}
//mEMA = 0.0;
}
}
/*public void stopRecorder() {
if (mRecorder != null) {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
}*/
public void updateTv(){
double amp=getAmplitudeEMA();
mStatusView.setText(Double.toString((amp)) + " dB");
}
public double soundDb(double ampl){
double intensity=20 * Math.log10(getAmplitudeEMA() / ampl);
return intensity;
}
public double getAmplitude() {
if (mRecorder != null)
return (mRecorder.getMaxAmplitude());
else
return 0;
}
public double getAmplitudeEMA() {
double amp = getAmplitude();
mEMA = EMA_FILTER * amp + (1.0 - EMA_FILTER) * mEMA;
return mEMA;
}
public void volumeChanger()
{
volumeBar = (SeekBar) findViewById(R.id.sb_volumebar);
//get the audio manager
amanager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
//seek bar settings//
//sets the range between 0 and the max volume
volumeBar.setMax(amanager.getStreamMaxVolume(AudioManager.STREAM_RING));
//set the seek bar progress to 1
//volumeBar.setKeyProgressIncrement(1);
//sets the progress of the seek bar based on the system's volume
// volumeBar.setProgress(500);
if(mEMA<(double)800.00)
{
volumeBar.setProgress((int)amanager.getStreamMaxVolume(AudioManager.STREAM_RING)/5);
}
else if(mEMA<(double)15000.00)
{
volumeBar.setProgress((int)amanager.getStreamMaxVolume(AudioManager.STREAM_RING)*2/5);
}
else if(mEMA<25000.00)
{
volumeBar.setProgress((int)amanager.getStreamMaxVolume(AudioManager.STREAM_RING)*3/5);
}
else if(mEMA<50000.00)
{
volumeBar.setProgress((int)amanager.getStreamMaxVolume(AudioManager.STREAM_RING)*4/5);
}
else
{
volumeBar.setProgress((int)amanager.getStreamMaxVolume(AudioManager.STREAM_RING));
}
//register OnSeekBarChangeListener, so that the seek bar can change the volume
volumeBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener()
{
#Override
public void onStopTrackingTouch(SeekBar seekBar)
{
}
#Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
int index=volumeBar.getProgress();
//change the volume, displaying a toast message containing the current volume and playing a feedback sound
amanager.setStreamVolume(AudioManager.STREAM_RING, index, AudioManager.FLAG_SHOW_UI);
}
});
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
//if one of the volume keys were pressed
if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)
{
//change the seek bar progress indicator position
volumeBar.setProgress(amanager.getStreamVolume(AudioManager.STREAM_RING));
}
if (keyCode == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(true);
return true;
}
//propagate the key event
return super.onKeyDown(keyCode, event);
}
}
Have the measurement code always listen on the service and let it update shared preferences.
Then all the activity needs to do is listen to changes on shared preferences and update the UI accordingly.
You can obviously use any number of mechanisms to allow the activity respond to the services changes, but I think listening for shared preference changes is probably easiest to start with.
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
//update ui if your key was updated
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
I have figured out another way of doing it.
A broadcast Receiver can be used in the main activity in syncronization with a broadcast service.
Related
Requirement is to play a chime sound an phrase after that using Android Text to Speech.
for (final Integer orderId : voiceoverIds) {
alertChimePlayer = MediaPlayer.create(getApplicationContext(), R.raw.orderalert);
alertChimePlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
String orderSpeechText = "Number " + orderId;
textToSpeech.speak(orderSpeechText, TextToSpeech.QUEUE_ADD, null, "ORDER_NO_" + orderId);
textToSpeech.playSilentUtterance(2000, TextToSpeech.QUEUE_ADD, "PAUSE_NO_" + orderId);
System.out.println(">>>>>>>>>>>>>>>>>>> orderSpeechText : " + orderSpeechText);
}
});
alertChimePlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
alertChimePlayer.start();
}
});
}
But this only works one time. How to handle this properly?
Good question. Stayed up all night on this. The problem is that in the loop, those chimes just get rapidly sent to the media player all at the same time. Media Player cant really handle that properly.
Here is my solution. I am using SoundPool to play the chime because it is better at playing short sounds in repetition.
I am also using a timer thread to trigger the "Chime + spoken text-to-speech (tts)" sequences. The tts onUtteranceProgressListener is used to play the tts after the chime sound.
Here is the tested code. What you will hear is:
chime "number 1" (3 second delay)
chime "number 2" (3 second delay)
... continues until terminated
import android.app.Activity;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.util.Log;
import java.util.Locale;
public class MainActivity extends Activity implements TextToSpeech.OnInitListener {
AudioAttributes aa;
SoundPool sp;
private TextToSpeech tts;
int MAX_STREAMS = 5;
int REPEAT = 0;
int DELAY = 3000;
int orderId = 0;
// Clock thread
Thread m_clockThread;
boolean m_bClockThreadStop;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("TTS", "Starting...");
// Set up the sound pool sound
AudioAttributes aa = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build();
sp = new SoundPool.Builder()
.setMaxStreams(8)
.setAudioAttributes(aa)
.build();
// Start the tts
tts = new TextToSpeech(MainActivity.this,MainActivity.this);
tts.setLanguage(Locale.US);
}
#Override
public void onInit(int status) {
Log.e("TTS", "Enter onInit...");
if (status == TextToSpeech.SUCCESS) {
int result = tts.setLanguage(Locale.US);
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.e("TTS", "This Language is not supported");
} else {
Log.e("TTS", "onInit Success");
// create and run clock thread
createAndRunClockThread(this);
}
} else {
Log.e("TTS", "onInit Fail");
}
}
public void createAndRunClockThread(final Activity act) {
m_bClockThreadStop=false;
m_clockThread = new Thread(new Runnable() {
public void run() {
while(!m_bClockThreadStop) {
try {
act.runOnUiThread(new Runnable() {
public void run() {
playChime();
}
});
Thread.sleep(DELAY);
}
catch(InterruptedException e) {
Log.e("TTS", "ClockThread fail");
}
}
}
});
m_clockThread.start();
}
private void playChime() {
Log.e("TTS", "Entering startChimes...");
sp.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
#Override
public void onLoadComplete(final SoundPool soundPool, final int soundId, int status) {
final int priority = 0;
final int repeat = 0;
final float rate = 1.f; // Frequency Rate can be from .5 to 2.0
// Set volume
AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
final float volume = streamVolumeCurrent / streamVolumeMax;
// Play a chime followed by the tts
tts.speak("Number " + orderId, TextToSpeech.QUEUE_ADD, null, "ID" + orderId);
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
// Speaking started.
sp.play(soundId, volume, volume, priority, repeat, rate);
}
#Override
public void onDone(String utteranceId) {
// Speaking stopped.
orderId = orderId + 1;
}
#Override
public void onError(String utteranceId) {
// There was an error.
}
});
}
});
sp.load(this, R.raw.beep, 1);
}
}
Thanks for the answer #Mark W. But I was thinking of solution that doesn't involve explicit delays/sleeps.
So I was implementing this Service Class.
public class OrderNoticeService extends Service implements TextToSpeech.OnInitListener {
private List<OrderSpeechAsyncTask> orderSpeechAsyncTasks = new ArrayList<>();
private TextToSpeech textToSpeech;
private Context context;
public void addToOrderNoticeQueue(int orderId) {
String orderSpeechText = String.format(getResources().getString(R.string.order_voice_over_default_text), Integer.toString(orderId));
orderSpeechAsyncTasks.add(new OrderSpeechAsyncTask(getApplicationContext(), R.raw.orderalert, orderSpeechText, textToSpeech, new AsyncTaskCallback() {
#Override
public void onTaskCompleted(Object response) {
}
}));
if (orderSpeechAsyncTasks.size() > 1) {
final OrderSpeechAsyncTask orderSpeechAsyncTask = orderSpeechAsyncTasks.get(orderSpeechAsyncTasks.size() - 1);
OrderSpeechAsyncTask orderSpeechAsyncTaskPrior = orderSpeechAsyncTasks.get(orderSpeechAsyncTasks.size() - 2);
orderSpeechAsyncTaskPrior.setAsyncTaskCallback(new AsyncTaskCallback() {
#Override
public void onTaskCompleted(Object response) {
try {
orderSpeechAsyncTask.execute();
System.out.println("Execution!");
} catch (Exception e) {
}
}
});
}
}
#Override
public void onCreate() {
textToSpeech = new TextToSpeech(this, this);
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
context = this;
return Service.START_STICKY;
}
private static final String TAG = "OrderNoticeService";
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "OrderNoticeService onBind");
return mBinder;
}
#Override
public void onDestroy() {
if (textToSpeech != null) {
textToSpeech.stop();
textToSpeech.shutdown();
}
Log.i(TAG, "OrderNoticeService onDestroy");
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
OrderNoticeVoiceOverThread orderNoticeVoiceOverThread = new OrderNoticeVoiceOverThread(context, orderSpeechAsyncTasks);
orderNoticeVoiceOverThread.start();
} else {
System.out.println("Text To Speech not supported!");
}
}
private class OrderNoticeVoiceOverThread extends Thread {
private Context context;
private List<OrderSpeechAsyncTask> orderSpeechAsyncTasks;
private boolean anyTaskRunning = false;
public OrderNoticeVoiceOverThread(Context context, List<OrderSpeechAsyncTask> orderSpeechAsyncTasks) {
this.context = context;
this.orderSpeechAsyncTasks = orderSpeechAsyncTasks;
}
public void run() {
while (true) {
for (OrderSpeechAsyncTask orderSpeechAsyncTask : new ArrayList<OrderSpeechAsyncTask>(orderSpeechAsyncTasks)) {
if (orderSpeechAsyncTask != null && orderSpeechAsyncTask.getStatus().equals(AsyncTask.Status.RUNNING)) {
anyTaskRunning = true;
break;
}
}
if (!anyTaskRunning) {
for (OrderSpeechAsyncTask orderSpeechAsyncTask : new ArrayList<OrderSpeechAsyncTask>(orderSpeechAsyncTasks)) {
if (orderSpeechAsyncTask != null && orderSpeechAsyncTask.getStatus().equals(AsyncTask.Status.PENDING)) {
orderSpeechAsyncTask.execute();
anyTaskRunning = false;
break;
}
}
}
}
}
}
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
public OrderNoticeService getService() {
return OrderNoticeService.this;
}
}
}
And the OrderSpeechAsyncTask as follows.
public class OrderSpeechAsyncTask extends AsyncTask<Void, Void, Void> {
private static final String LOG_TAG = OrderSpeechAsyncTask.class.getSimpleName();
private MediaPlayer mediaPlayer;
private int soundId;
private Context context;
private String orderSpeechText;
private AsyncTaskCallback asyncTaskCallback;
private TextToSpeech textToSpeech;
public OrderSpeechAsyncTask(final Context context, int soundId, String orderSpeechText, TextToSpeech textToSpeech, AsyncTaskCallback asyncTaskCallback) {
this.context = context;
this.soundId = soundId;
this.orderSpeechText = orderSpeechText;
this.textToSpeech = textToSpeech;
this.asyncTaskCallback = asyncTaskCallback;
}
public AsyncTaskCallback getAsyncTaskCallback() {
return asyncTaskCallback;
}
public void setAsyncTaskCallback(AsyncTaskCallback asyncTaskCallback) {
this.asyncTaskCallback = asyncTaskCallback;
}
#Override
protected Void doInBackground(Void... params) {
mediaPlayer = MediaPlayer.create(context, soundId);
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mediaPlayer.release();
textToSpeech.speak(orderSpeechText, TextToSpeech.QUEUE_ADD, null, "ORDER_NO_" + orderSpeechText);
textToSpeech.playSilentUtterance(2000, TextToSpeech.QUEUE_ADD, "PAUSE_NO_" + orderSpeechText);
textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
}
#Override
public void onDone(String utteranceId) {
asyncTaskCallback.onTaskCompleted(null);
}
#Override
public void onError(String utteranceId) {
}
});
}
});
mediaPlayer.start();
return null;
}
}
This so far handles the following;
Playing the sound and the text in the list
Adding an item to the queue whilst the existing list is still being read
To do;
This doesn't handle any new item that gets added to the list once the existing items are done being read.
I put one song into an app (tabhost) and I have progress bar, play button, stop button, loop button.
The function I want is:
when user plays the music, the progress bar starts to run from "0"
when user presses stop, the progress bar stops at where it was, waits for the user to press play again, and then continues where it left off
I might change the button name to "pause" later, but this is the function I need right now.
Right now, 2 issues:
When stop is pressed, the progress bar returns to "0", but the music resumes playing from where it left off (music not playing from very beginning)
The progress bar runs faster than desired; it runs to the end before the music ending.
Here is my code, hope someone could help thank you so much:
import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import java.util.logging.LogRecord;
public class TabMusicActivity extends Activity {
MediaPlayer mediaPlayer;
private boolean playing = false;
private ProgressBar progressBar;
private Handler handler = new Handler();
int progress = 0;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music);
final ImageButton buttonStart = (ImageButton)findViewById(R.id.buttonStart);
progressBar = (ProgressBar)findViewById(R.id.progressBar);
buttonStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!playing) {
playing = true;
buttonStart.setImageResource(R.drawable.music_stop_button);
Uri path = Uri.parse("android.resource://"+getPackageName()+"/"+ R.raw.bashibafo);
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(TabMusicActivity.this, path);
}
if (mediaPlayer != null) {
mediaPlayer.start();
process();
}
}
else {
playing = false;
buttonStart.setImageResource(R.drawable.button_play);
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
}
}
}
});
ImageButton buttonLoop = (ImageButton)findViewById(R.id.buttonLoop);
buttonLoop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mediaPlayer.setLooping(true);
}
});
}
private void process (){
progressBar.setProgress(0);
progress = 0;
new Thread(new Runnable() {
#Override
public void run() {
while (progress < 100){
progress = doWork();
try{
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post( new Runnable() {
#Override
public void run() {
progressBar.setProgress(progress);
}
});
}
}
}).start();
}
private int doWork(){
progress++;
if (progress < 100){
return progress;
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 100;
}
#Override
protected void onDestroy() {
if(mediaPlayer!=null && mediaPlayer.isPlaying()){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
super.onDestroy();
}
}
I used this code for playing music
if (player != null)
{
player.seekTo(length);
player.start();
}
else
{
player = MediaPlayer.create(context, Uri.fromFile(new File(url)));
seek_bar.setMax(player.getDuration());
seek_bar.setProgress(player.getCurrentPosition());
player.setOnCompletionListener(new OnCompletionListener()
{
#Override
public void onCompletion(MediaPlayer mp)
{
player.reset();
player.release();
player = null;
seekHandler.removeCallbacks(run);
seek_bar.setProgress(0);
if (recieveOrSend.equals("send"))
{
holder.iv_audio_pause_send.setVisibility(View.INVISIBLE);
holder.iv_audio_play_send.setVisibility(View.VISIBLE);
}
else
{
holder.iv_audio_pause_recieve.setVisibility(View.INVISIBLE);
holder.iv_audio_play_recieve.setVisibility(View.VISIBLE);
}
}
});
// player.prepare();
player.start();
}
seekUpdation();
Methods--
Runnable run = new Runnable()
{
#Override
public void run()
{
seekUpdation();
}
};
public void seekUpdation()
{
seek_bar.setProgress(player.getCurrentPosition());
seekHandler.postDelayed(run, 1000);
}
private void pausePlayer()
{
try
{
if (player != null)
{
player.pause();
length = player.getCurrentPosition();
seekHandler.removeCallbacks(run);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
I need make some action after video have played 50% of its length.
So I did not see some listener with something like "onCertainMills()", I see onVideoEnded() but I need get event when current time of played video will more then length/2.
You could try to use the YoutubePlayer's property getCurrentTimeMillis() to constantly obtain the time and getDurationMillis() to get the video's total duration. Thus, you can set the desired task when the youtubePlayer.getCurrentTimeMillis() >= youtubePlayer.getDurationMillis() with an asynctask.
The code will look like this:
player.setPlaylistEventListener(playlistEventListener);
player.setPlayerStateChangeListener(playerStateChangeListener);
player.setPlaybackEventListener(playbackEventListener);
AsyncTask a = new AsyncTask(); //listener for time
a.execute(player, 2000,this); //player, time, and activity
if (!wasRestored) {
playVideoAtSelection();
}
setControlsEnabled(true);
At the end of the ends I have made class to make action on certain second of playing video.
Here is the code to make some action after half time of the video playing: (+ 1 sec) Hope it will help to someone.
import android.os.Handler;
import android.util.Log;
import com.clipclash.android.entities.Clip;
import com.google.android.youtube.player.YouTubePlayer;
public class YoutubeCounter {
Clip clip;
long counter;
long startCut;
long endCut;
long durationGoal;
private YouTubePlayer youTubePlayer;
boolean goal = false;
Handler h;
Runnable checkGoal = new Runnable() {
public void run() {
getProgress();
if (counter >= durationGoal) {
// GOAL !!!
//TODO MAKE SOME ACTION
goal = true;
stopSchedule();
} else {
startSchedule();
}
}
};
public YoutubeCounter(Clip clip) {
this.clip = clip;
h = new Handler();
}
public void setYouTubePlayer(YouTubePlayer youTubePlayer) {
this.youTubePlayer = youTubePlayer;
}
public void play() {
if (!goal) {
if (durationGoal == 0) {
durationGoal = this.youTubePlayer.getDurationMillis() / 2;
}
startCut = youTubePlayer.getCurrentTimeMillis();
startSchedule();
}
}
public void stop() {
if (!goal) {
getProgress();
stopSchedule();
}
}
private void startSchedule() {
long newSchedule = durationGoal - counter;
newSchedule = newSchedule + 1000; // just a little bit more - not requires
h.postDelayed(checkGoal, newSchedule);
}
public void stopSchedule() {
h.removeCallbacks(checkGoal);
}
private void getProgress() {
try {
endCut = youTubePlayer.getCurrentTimeMillis();
long cut = endCut - startCut;
if (cut < 0) {
cut = 0;
}
counter += cut;
} catch (Exception e) {
}
}
public long getCounter() {
return counter;
}
}
I have a simple player and recorder. Everything works great but have one problem. I want to add seek bar to see progress in playing record and use this seek bar to set the place from the player should play. I have onProgress but with no effect. This is the code:
package com.example.recorder;
import java.io.IOException;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
public class MainActivity extends Activity implements OnSeekBarChangeListener
{
private static final String LOG_TAG = "AudioRecordTest";
private static String mFileName = null;
private SeekBar seekBar;
private MediaRecorder mRecorder = null;
private Button startRecord, startPlaying, stopPlaying;
private MediaPlayer mPlayer = null;
private void onRecord(boolean start) {
if (start) {
startRecording();
} else {
stopRecording();
}
}
private void startPlaying() {
if(mPlayer != null && mPlayer.isPlaying()){
mPlayer.pause();
} else if(mPlayer != null){
mPlayer.start();
}else{
mPlayer = new MediaPlayer();
try {
mPlayer.setDataSource(mFileName);
mPlayer.prepare();
mPlayer.start();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
}
}
}
private void stopPlaying() {
mPlayer.release();
mPlayer = null;
startPlaying.setText("Start playing");
}
private void pausePlaying(){
if(mPlayer.isPlaying()){
mPlayer.pause();
} else {
mPlayer.start();
}
}
private void startRecording() {
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setOutputFile(mFileName);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
mRecorder.prepare();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
}
mRecorder.start();
}
private void stopRecording() {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
public MainActivity() {
mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
mFileName += "/audiorecordtest.3gp";
}
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_main);
startPlaying = (Button) findViewById(R.id.buttonStartPlay);
stopPlaying = (Button) findViewById(R.id.buttonStopPlaying);
startRecord = (Button) findViewById(R.id.buttonStartRecord);
seekBar = (SeekBar) findViewById(R.id.seekBar);
startRecord.setOnClickListener(new OnClickListener() {
boolean mStartRecording = true;
#Override
public void onClick(View v) {
onRecord(mStartRecording);
if (mStartRecording) {
startRecord.setText("Stop recording");
} else {
startRecord.setText("Start recording");
}
mStartRecording = !mStartRecording;
}
});
startPlaying.setOnClickListener(new OnClickListener() {
boolean mStartPlaying = true;
#Override
public void onClick(View v) {
//onPlay(mStartPlaying);
startPlaying();
if (mStartPlaying) {
startPlaying.setText("Stop playing");
} else {
startPlaying.setText("Start playing");
}
mStartPlaying = !mStartPlaying;
}
});
stopPlaying.setOnClickListener(new OnClickListener() {
boolean mStartPlaying = true;
#Override
public void onClick(View v) {
stopPlaying();
}
});
}
#Override
public void onPause() {
super.onPause();
if (mRecorder != null) {
mRecorder.release();
mRecorder = null;
}
if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(fromUser){
mPlayer.seekTo(progress);
seekBar.setProgress(progress);
}
else{
// the event was fired from code and you shouldn't call player.seekTo()
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
}
Any ideas on how to use seek bar to see progress and set place from the record should play?
To create a 'connection' between SeekBar and MediaPlayer you need first to get your current recording max duration and set it to your seek bar.
mSeekBar.setMax(mFileDuration/1000); // where mFileDuration is mMediaPlayer.getDuration();
After you initialise your MediaPlayer and for example press play button, you should create handler and post runnable so you can update your SeekBar (in the UI thread itself) with the current position of your MediaPlayer like this :
private Handler mHandler = new Handler();
//Make sure you update Seekbar on UI thread
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
if(mMediaPlayer != null){
int mCurrentPosition = mMediaPlayer.getCurrentPosition() / 1000;
mSeekBar.setProgress(mCurrentPosition);
}
mHandler.postDelayed(this, 1000);
}
});
and update that value every second.
If you need to update the MediaPlayer's position while user drag your SeekBar you should add OnSeekBarChangeListener to your SeekBar and do it there :
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(mMediaPlayer != null && fromUser){
mMediaPlayer.seekTo(progress * 1000);
}
}
});
And that should do the trick! : )
EDIT:
One thing which I've noticed in your code, don't do :
public MainActivity() {
mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
mFileName += "/audiorecordtest.3gp";
}
make all initialisations in your onCreate(); , do not create constructors of your Activity.
I've used this tutorial with success, it's really simple to understand:
www.androidhive.info/2012/03/android-building-audio-player-tutorial/
This is the interesting part:
/**
* Update timer on seekbar
* */
public void updateProgressBar() {
mHandler.postDelayed(mUpdateTimeTask, 100);
}
/**
* Background Runnable thread
* */
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
long totalDuration = mp.getDuration();
long currentDuration = mp.getCurrentPosition();
// Displaying Total Duration time
songTotalDurationLabel.setText(""+utils.milliSecondsToTimer(totalDuration));
// Displaying time completed playing
songCurrentDurationLabel.setText(""+utils.milliSecondsToTimer(currentDuration));
// Updating progress bar
int progress = (int)(utils.getProgressPercentage(currentDuration, totalDuration));
//Log.d("Progress", ""+progress);
songProgressBar.setProgress(progress);
// Running this thread after 100 milliseconds
mHandler.postDelayed(this, 100);
}
};
/**
*
* */
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
}
/**
* When user starts moving the progress handler
* */
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// remove message Handler from updating progress bar
mHandler.removeCallbacks(mUpdateTimeTask);
}
/**
* When user stops moving the progress hanlder
* */
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
mHandler.removeCallbacks(mUpdateTimeTask);
int totalDuration = mp.getDuration();
int currentPosition = utils.progressToTimer(seekBar.getProgress(), totalDuration);
// forward or backward to certain seconds
mp.seekTo(currentPosition);
// update timer progress again
updateProgressBar();
}
After you initialize your MediaPlayer and SeekBar, you can do this :
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
mSeekBar.setProgress(mMediaPlayer.getCurrentPosition());
}
},0,1000);
This updates SeekBar every second(1000ms)
And for updating MediaPlayer, if user drag SeekBar, you must add OnSeekBarChangeListener to your SeekBar :
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
mMediaPlayer.seekTo(i);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
HAPPY CODING!!!
Code in Kotlin:
var updateSongTime = object : Runnable {
override fun run() {
val getCurrent = mediaPlayer?.currentPosition
startTimeText?.setText(String.format("%d:%d",
TimeUnit.MILLISECONDS.toMinutes(getCurrent?.toLong() as Long),
TimeUnit.MILLISECONDS.toSeconds(getCurrent?.toLong()) -
TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(getCurrent?.toLong()))))
seekBar?.setProgress(getCurrent?.toInt() as Int)
Handler().postDelayed(this, 1000)
}
}
For changing media player audio file every second
If user drags the seek bar then following code snippet can be use
Statified.seekBar?.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
if(b && Statified.mediaPlayer != null){
Statified.mediaPlayer?.seekTo(i)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
check this, you should give arguments in msecs, Dont just send progress to seekTo(int)
and also check this getCurrentPostion() and getDuration().
You can do some calcuations, ie., convert progress in msec like msce = (progress/100)*getDuration() then do seekTo(msec)
Or else i have an easy idea, you don't need to change any code anywer else just add seekBar.setMax(mPlayer.getDuration()) once your media player is prepared.
and here is link exactly what you want seek bar update
The below code worked for me.
I've created a method for seekbar
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mp.start();
getDurationTimer();
getSeekBarStatus();
}
//Creating duration time method
public void getDurationTimer(){
final long minutes=(mSongDuration/1000)/60;
final int seconds= (int) ((mSongDuration/1000)%60);
SongMaxLength.setText(minutes+ ":"+seconds);
}
//creating a method for seekBar progress
public void getSeekBarStatus(){
new Thread(new Runnable() {
#Override
public void run() {
// mp is your MediaPlayer
// progress is your ProgressBar
int currentPosition = 0;
int total = mp.getDuration();
seekBar.setMax(total);
while (mp != null && currentPosition < total) {
try {
Thread.sleep(1000);
currentPosition = mp.getCurrentPosition();
} catch (InterruptedException e) {
return;
}
seekBar.setProgress(currentPosition);
}
}
}).start();
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
int progress=0;
#Override
public void onProgressChanged(final SeekBar seekBar, int ProgressValue, boolean fromUser) {
if (fromUser) {
mp.seekTo(ProgressValue);//if user drags the seekbar, it gets the position and updates in textView.
}
final long mMinutes=(ProgressValue/1000)/60;//converting into minutes
final int mSeconds=((ProgressValue/1000)%60);//converting into seconds
SongProgress.setText(mMinutes+":"+mSeconds);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
SongProgress and SongMaxLength are the TextView to show song duration and song length.
My code snippet:
public class PlayerActivity extends AppCompatActivity {
private static final String TAG = "PlayerActivity";
private ActivityPlayerBinding binding;
private MediaPlayer mediaPlayer;
private boolean playingAudio = false;
Runnable runnable;
Handler handler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityPlayerBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
);
try {
mediaPlayer.setDataSource(songUrl);
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.setOnPreparedListener(mp -> {
binding.playBtn.setBackground(getDrawable(R.drawable.ic_pause));
binding.seekbarPlayer.setMax(mp.getDuration());
mediaPlayer.start();
playingAudio = true;
updateSeekbar();
});
mediaPlayer.setOnBufferingUpdateListener((mp, percent) -> {
double ratio = percent / 100.0;
int bufferingLevel = (int) (mp.getDuration() * ratio);
binding.seekbarPlayer.setSecondaryProgress(bufferingLevel);
});
binding.seekbarPlayer.setOnSeekBarChangeListener(seekBarChangeListener);
}
private SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
mediaPlayer.seekTo(progress);
seekBar.setProgress(progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
};
private void updateSeekbar() {
try {
if (mediaPlayer != null) {
int currentPos = mediaPlayer.getCurrentPosition();
binding.seekbarPlayer.setProgress(currentPos);
runnable = this::updateSeekbar;
handler.postDelayed(runnable, 1000);
}
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "updateSeekbar: " + e.getMessage());
}
}
}
int pos = 0;
yourSeekBar.setMax(mPlayer.getDuration());
After You start Your MediaPlayer i.e mplayer.start()
Try this code
while(mPlayer!=null){
try {
Thread.sleep(1000);
pos = mPlayer.getCurrentPosition();
} catch (Exception e) {
//show exception in LogCat
}
yourSeekBar.setProgress(pos);
}
Before you added this code you have to create xml resource for SeekBar and use it in Your Activity class of ur onCreate() method.
This works for me:
seekbarPlayer.setMax(mp.getDuration());
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if(mp != null){
seekbarPlayer.setProgress(mp.getCurrentPosition());
}
mHandler.postDelayed(this, 1000);
}
});
Given the answer hardartcore that worked for me with a small change and did not work before the change:
private Handler mHandler = new Handler();
MusicPlayer.this.runOnUiThread(new Runnable() {
#Override
public void run() {
if(player != null){
int mCurrentPosition = player.getCurrentPosition();//clear ' /1000 '
seekBar.setProgress(mCurrentPosition);
}
mHandler.postDelayed(this, 1000);
}
});
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(player != null && fromUser){
player.seekTo(progress); // clear ' * 1000 '
}
}
});
To add on to #hardartcore's answer.
Instead of calling postDelayed on a Handler, the best approach would be to get callbacks from the MediaPlayer during play-back and then accordingly update the seekBar with the progress.
Also, pause your MediaPlayer at onStartTrackingTouch(SeekBar seekBar) of the OnSeekBarChangeListener and then re-start it on onStopTrackingTouch(SeekBar seekBar).
Based on previous statements, for better performance, you can also add an if condition
if (player.isPlaying() {
handler.postDelayed(..., 1000);
}
Try this Code:
public class MainActivity extends AppCompatActivity {
MediaPlayer mplayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//You create MediaPlayer variable ==> set the path and start the audio.
mplayer = MediaPlayer.create(this, R.raw.example);
mplayer.start();
//Find the seek bar by Id (which you have to create in layout)
// Set seekBar max with length of audio
// You need a Timer variable to set progress with position of audio
final SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar);
seekBar.setMax(mplayer.getDuration());
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
seekBar.setProgress(mplayer.getCurrentPosition());
}
}, 0, 1000);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// Update the progress depending on seek bar
mplayer.seekTo(progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
I have two fragments (left and right) and getting in the left fragment a list of Radiostreams. By clicking on one of these streams, the right fragment should change the Name of the Stream and start playing the stream with the given uri.
2 Problems:
Some of the radio streams aren't up to date, so a lot of them aren't working anymore. The problem is, this causes my app to do a forceclose! I did error handling, but after calling such a stream I get:
03-20 14:23:28.192: A/libc(1021): Fatal signal 11 (SIGSEGV) at
0x00000000 (code=1)
03-20 14:23:28.192: W/AudioSystem(1021): AudioFlinger server died!
03-20 14:23:28.192: W/IMediaDeathNotifier(1021): media server died
03-20 14:23:28.192: E/MediaPlayer(1021): error (100, 0)
03-20 14:23:28.192: I/ServiceManager(1021): Waiting for service
media.audio_flinger...
03-20 14:23:28.752: I/dalvikvm(1021): threadid=3: reacting to signal 3
03-20 14:23:28.782: I/dalvikvm(1021): Wrote stack traces to
'/data/anr/traces.txt'
03-20 14:23:29.192: I/ServiceManager(1021): Waiting for service
media.audio_flinger...
I don't know why. Is there any other way to do error handling? Or is there a way to check all the streams before calling mediaPlayer.setDataSource(uri) to avoid preparing defekt uris? (see my code at the end)
I'm controlling the left ListFragment with a remote control. When I try to switch very fast from one channel to the other everything is very laggy. It seems that the reinstanciation of the Mediaplayer take very long. When I don't reinstanciate I get an runtimeerror when I call mediaPlayer.setDataSource(..) again. Is there a way to call .setDataSource two times on one MediaPlayer Object?
Here is my code:
My MediaPlayer Wrapper class:
package net.smart4life.tvplay.model;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.util.Log;
/**
* A wrapper class for {#link android.media.MediaPlayer}.
* <p>
* Encapsulates an instance of MediaPlayer, and makes a record of its internal
* state accessible via a {#link MediaPlayerWrapper#getState()} accessor.
*/
public class MediaPlayerStateWrapper {
private static String tag = "MediaPlayerWrapper";
private MediaPlayer mPlayer;
private State currentState;
private MediaPlayerStateWrapper mWrapper;
public MediaPlayerStateWrapper() {
mWrapper = this;
mPlayer = new MediaPlayer();
currentState = State.IDLE;
mPlayer.setOnPreparedListener(mOnPreparedListener);
mPlayer.setOnCompletionListener(mOnCompletionListener);
mPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
mPlayer.setOnErrorListener(mOnErrorListener);
mPlayer.setOnInfoListener(mOnInfoListener);
}
/* METHOD WRAPPING FOR STATE CHANGES */
public static enum State {
IDLE, ERROR, INITIALIZED, PREPARING, PREPARED, STARTED, STOPPED, PLAYBACK_COMPLETE, PAUSED;
}
public void setDataSource(String path) {
if (currentState == State.IDLE) {
try {
mPlayer.setDataSource(path);
currentState = State.INITIALIZED;
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else
throw new RuntimeException();
}
public void prepareAsync() {
Log.d(tag, "prepareAsync()");
if (EnumSet.of(State.INITIALIZED, State.STOPPED).contains(currentState)) {
mPlayer.prepareAsync();
currentState = State.PREPARING;
} else
throw new RuntimeException();
}
public boolean isPlaying() {
Log.d(tag, "isPlaying()");
if (currentState != State.ERROR) {
return mPlayer.isPlaying();
} else
throw new RuntimeException();
}
public void seekTo(int msec) {
Log.d(tag, "seekTo()");
if (EnumSet.of(State.PREPARED, State.STARTED, State.PAUSED,
State.PLAYBACK_COMPLETE).contains(currentState)) {
mPlayer.seekTo(msec);
} else
throw new RuntimeException();
}
public void pause() {
Log.d(tag, "pause()");
if (EnumSet.of(State.STARTED, State.PAUSED).contains(currentState)) {
mPlayer.pause();
currentState = State.PAUSED;
} else
throw new RuntimeException();
}
public void start() {
Log.d(tag, "start()");
if (EnumSet.of(State.PREPARED, State.STARTED, State.PAUSED,
State.PLAYBACK_COMPLETE).contains(currentState)) {
mPlayer.start();
currentState = State.STARTED;
} else
throw new RuntimeException();
}
public void stop() {
Log.d(tag, "stop()");
if (EnumSet.of(State.PREPARED, State.STARTED, State.STOPPED,
State.PAUSED, State.PLAYBACK_COMPLETE).contains(currentState)) {
mPlayer.stop();
currentState = State.STOPPED;
} else
throw new RuntimeException();
}
public void reset() {
Log.d(tag, "reset()");
mPlayer.reset();
currentState = State.IDLE;
}
/**
* #return The current state of the mediaplayer state machine.
*/
public State getState() {
Log.d(tag, "getState()");
return currentState;
}
public void release() {
Log.d(tag, "release()");
mPlayer.release();
}
/* INTERNAL LISTENERS */
private OnPreparedListener mOnPreparedListener = new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
Log.d(tag, "on prepared");
currentState = State.PREPARED;
mWrapper.onPrepared(mp);
mPlayer.start();
currentState = State.STARTED;
}
};
private OnCompletionListener mOnCompletionListener = new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.d(tag, "on completion");
currentState = State.PLAYBACK_COMPLETE;
mWrapper.onCompletion(mp);
}
};
private OnBufferingUpdateListener mOnBufferingUpdateListener = new OnBufferingUpdateListener() {
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
Log.d(tag, "on buffering update");
mWrapper.onBufferingUpdate(mp, percent);
}
};
private OnErrorListener mOnErrorListener = new OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.d(tag, "on error");
currentState = State.ERROR;
mWrapper.onError(mp, what, extra);
return false;
}
};
private OnInfoListener mOnInfoListener = new OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Log.d(tag, "on info");
mWrapper.onInfo(mp, what, extra);
return false;
}
};
/* EXTERNAL STUBS TO OVERRIDE */
public void onPrepared(MediaPlayer mp) {
}
public void onCompletion(MediaPlayer mp) {
}
public void onBufferingUpdate(MediaPlayer mp, int percent) {
}
boolean onError(MediaPlayer mp, int what, int extra) {
// Error Handling of type: "MEdiaPlayer error(100,0)
mp.stop();
mp.release();
return false;
}
public boolean onInfo(MediaPlayer mp, int what, int extra) {
return false;
}
/* OTHER STUFF */
public int getCurrentPosition() {
if (currentState != State.ERROR) {
return mPlayer.getCurrentPosition();
} else {
return 0;
}
}
public int getDuration() {
// Prepared, Started, Paused, Stopped, PlaybackCompleted
if (EnumSet.of(State.PREPARED, State.STARTED, State.PAUSED,
State.STOPPED, State.PLAYBACK_COMPLETE).contains(currentState)) {
return mPlayer.getDuration();
} else {
return 100;
}
}
}
Here is my TestFragment (right Fragment). Note: the left Fragment is calling the method "newChannel(radioChannel)" from TestFragment, everytime a listitem was clicked.
package net.smart4life.tvplay.fragment;
import java.io.IOException;
import net.smart4life.tvplay.R;
import net.smart4life.tvplay.model.MediaPlayerStateWrapper;
import net.smart4life.tvplay.model.RadioChannel;
import android.app.Fragment;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
public class TestFragment extends Fragment {
private RadioChannel radioCh;
private TextView tv_RadioCh;
private MediaPlayerStateWrapper mediaWrapper;
private View view;
// firstcall
public TestFragment(RadioChannel radioChannel) {
this.radioCh = radioChannel;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
tv_RadioCh = (TextView) view.findViewById(R.id.radioText);
mediaWrapper = new MediaPlayerStateWrapper();
newChannel(radioCh);
}
public void newChannel (RadioChannel radioChannel) {
this.radioCh = radioChannel;
Log.e("RadioChannel", radioCh.getName());
tv_RadioCh.setText(radioCh.getName());
if(mediaWrapper.isPlaying()) {
mediaWrapper.stop();
mediaWrapper.reset();
} else if(mediaWrapper.getState() == MediaPlayerStateWrapper.State.PREPARING) {
mediaWrapper.release();
mediaWrapper = new MediaPlayerStateWrapper();
}
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_radio_player, container,
false);
return view;
}
#Override
public void onDetach() {
super.onDetach();
mediaWrapper.release();
}
}
Pros, could you please help me with one or both questions?
If a stream can't load you're often stucked in the prepare state, you can try this here, when mediaWrapper.getState() == MediaPlayerStateWrapper.State.ERROR:
mediaWrapper.reset();
mediaWrapper.release();
System.gc();
mediaWrapper = new MediaPlayerStateWrapper();
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
Best to put it in an AsyncTask, to avoid Not responding error. Or when you get an Error you have to create a new MediaPlayer, because Media Server died:
if(mediaWrapper.getState() == MediaPlayerStateWrapper.State.ERROR){
mediaWrapper = new MediaPlayerStateWrapper();
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
}
If the MediaPlayer is playing a stream you have to stop and reset it first :
mediaWrapper.stop();
mediaWrapper.reset();
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
It's working for me but i think it isn't the best way. Hope someone can find a better solution for what to do, when you're stucked in the prepare state.
Regarding the audioflinger service error, as you have noticed, it is marked by "what == 100" or error(100,0).
What you can do to avoid audioflinger error from my humble experience:
Avoid fast calls to the service (I do add like 500 millis delay after creating the player)
Limit the number of concurrent Mediaplayers active at the same time.
What you can do to handle audioflinger error:
Detect the audioflinger error 100, set a flag it occurred and disable GUI (releasing the player only is recommended, as stopping it when it already is in error state is not safe and will throw IllegalStateException & error(38,0)
Start another thread that keeps testing the service is back (could be by creating a new mediaplayer with no exceptions) with a timeout of let's say 5-10 seconds
When the Service is back reset the flag and re-enable GUI
So referring to your code:
boolean onError(MediaPlayer mp, int what, int extra) {
// Error Handling of type: "MEdiaPlayer error(100,0)
mp.release();
// here you add logic communicating the wrapper or main UI thread
// to disable GUI and set a flag
return false;
}
Then you add a method to handle this at the wrapper.
I would be really grateful when you work this out and post a solution. I too am facing a very similar problem.