Sound plays earlier than view is changed - java

I am creating a piano app in android studio:
I have an on click listener for my play button which when pressed is supposed to make the record and play buttons invisible and the stop button visible while the recorded sounds are playing.
Play button
mBtn_Play.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mRecordingState = "Playing";
switchButtonVisibility();
for (final int sound: mListRecordedSounds )
{
if (mRecordingState == "Ready")
{//break out of loop when stop button is pressed
break;
}
else {
mSoundPool.play(sound, 1,1,1,0,1);
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
});
}
Method for switching button visibility
private void switchButtonVisibility()
{
if (mRecordingState != "Ready")
{
mBtn_Stop.setVisibility(View.VISIBLE);
mBtn_Record.setVisibility(View.GONE);
mBtn_Play.setVisibility(View.GONE);
}
else
{
mBtn_Stop.setVisibility(View.GONE);
mBtn_Record.setVisibility(View.VISIBLE);
mBtn_Play.setVisibility(View.VISIBLE);
}
}
With the record and stop buttons, this works correctly
mBtn_Record.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mListRecordedSounds.clear();
mRecordingState = "Recording";
switchButtonVisibility();
}
});
mBtn_Stop.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mRecordingState = "Ready";
switchButtonVisibility();
}
});
For some strange reason it is executing the for loop first and playing the sounds before the switchButtonVisibility(); method changes which buttons are visible. This does not seem to make any sense as the method is above the loop. Is there any way to set it so the button visibility is changed first before the
loop is executed and the sounds are played?

Delay the execution, posting an event on the view. This will ensure, that the action would be performed as soon as the view is updated:
#Override
public void onClick(View v) {
mRecordingState = "Playing";
switchButtonVisibility();
mBtn_Play.post(new Runnable() {
#Override
public void run() {
for (final int sound : mListRecordedSounds ) {
// play the sound here
...
}
}
});
}

Related

Stop MediaPlayer when minimizing

button4 = findViewById(R.id.button4);
musicSound = MediaPlayer.create(this, R.raw.music);
buttonClick(); }
private Button button4;
private MediaPlayer musicSound;
public void buttonClick() {
button4.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
soundPlay(musicSound);
}
} ); }
public void soundPlay(MediaPlayer sound) {
{ sound.start();
sound.setLooping(true); }
buttonMusicStop = findViewById(R.id.buttonMusStop);
buttonClick2();}
private ImageButton buttonMusicStop;
public void buttonClick2() {
buttonMusicStop.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
soundPlay2(musicSound);
}});}
public void soundPlay2(MediaPlayer sound) {
if (sound.isPlaying()) {
sound.pause();
} else {
sound.start();
sound.setLooping(true);
} }
Hello! I start music in my application, but when I minimize the application, the music does not stop. What do I need to write so that when the application is minimized and also when the screen is turned off, the music stops. Any help would be greatly appreciated!
Seem your app is playing in background. So create onResume and onStop to stop play and release resource, somethings look like this
onStop{
sound.stop();
sound.reset();
sound.release();
mediaplayer = null;
supper.onStop();
}

Is there a way to set adapter position of view holder of recycler view programmatically?

Just like a view holder has the method getAdapterPosition(), I want to write my own setAdapterPosition() for the view holder(which doesn't exist).
What am I working on?
Currently, I am working on a project wherein I have to list 10 songs in the form of a recycler view and then play them, if any particular song is clicked. Each list item contains the song name, a seek bar and a play button
I have implemented this part, by adding all the media player contros inside the adapter class.
What do I want?
Currently, this model used mediaPlayer.setLooping(true) so that as soon as a song finished, it kept looping till some other song is clicked for playing.
Now I want to extend this model to an "autoplay" version so that as soon as a song is finished, the next song is played
What is the problem?
The seekBar progress and playButton image transition to a pauseButton image were happening with the call
viewHolder.seekBar.setProgress(...)
viewHolder.playButton.setImageDrawable(...)
Since this was happening on the
viewHolder.itemView.setOnClickListener(...)
as soon as a new song was clicked, the seekBar and playButton image were being updated.
Now, when the song is completed, I don't want the user to click on the next song to play it. I want it to be autoplayed, but I am unable to understand, how do I change myViewHolder position accordingly.
Suppose Song 0 has finished playing and Song 1 has started playing, viewHolder.getAdapterPosition() still returns 0 instead of 1. Hence the seekBar & playButton of song 0 is being updated and not of song 1.
I want to be able to do something like this
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
int nextSongPosition, currentSongPosition;
currentSongPosition = viewHolder.getAdapterPosition();
if(currentSongPosition==(songs.size()-1)){
nextSongPosition = 0;
//do some miracle so that viewHolder.getAdapterPosition() now shows 0 instead of currentSongPosition
}else{
nextSongPosition = currentSongPosition + 1;
//do some miracle so that viewHolder.getAdapterPosition() now shows nextSongPosition instead of the currentSongPosition;
}
}
});
As clear from the question statement, I want to implement something similar to a setAdapterPosition() which doesn't exist.
EDIT 1
onBindViewHolder Code:
#Override
public void onBindViewHolder(#NonNull final ViewHolder viewHolder, int i) {
Song song = songs.get(i);
viewHolder.itemView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
audioManager = (AudioManager) Objects.requireNonNull(context).getSystemService(Context.AUDIO_SERVICE);
if(mediaPlayer.isPlaying()){
pauseMediaPlayer(viewHolder);
}else{
StorageReference storageRef = FirebaseStorage.getInstance().getReference("Songs");
StorageReference dateRef = storageRef.child(song.getStoredAs());
dateRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
try {
mediaPlayer.setDataSource(uri.toString());
mediaPlayer.prepare();
viewHolder.seekBar.setMax(mediaPlayer.getDuration() / 1000);
mSeekbarUpdateHandler = new Handler();
mUpdateSeekbar = new Runnable() {
#Override
public void run() {
if (viewHolder.seekBar != null && mediaPlayer != null) {
try{
viewHolder.seekBar.setProgress(mediaPlayer.getCurrentPosition() / 1000);
mSeekbarUpdateHandler.postDelayed(this, 50);
} catch(IllegalStateException e){
Log.i("Exception",e.toString());
}
}
}
};
viewHolder.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser && mediaPlayer != null)
mediaPlayer.seekTo( progress * 1000);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
startMediaPlayer(viewHolder);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle any errors
Log.i("Song Loading Error", exception.toString());
}
});
}
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
int nextSongPosition, currentSongPosition;
currentSongPosition = viewHolder.getAdapterPosition();
if(currentSongPosition==(songs.size()-1)){
nextSongPosition = 0;
//do some miracle so that viewHolder.getAdapterPosition() now shows 0 instead of currentSongPosition
}else{
nextSongPosition = currentSongPosition + 1;
//do some miracle so that viewHolder.getAdapterPosition() now shows nextSongPosition instead of the currentSongPosition;
}
}
});
}
pauseMediaPlayer(..) & startMediaPlayer(..) Code:
private void pauseMediaPlayer(ViewHolder viewHolder) {
int result = audioManager.requestAudioFocus(this, AudioManager.USE_DEFAULT_STREAM_TYPE, AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
isSongPlaying = false;
viewHolder.playButton.setImageResource(R.drawable.play_button);
if (mediaPlayer != null) {
mediaPlayer.pause();
}
}
}
private void startMediaPlayer(ViewHolder viewHolder) {
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
isSongPlaying = true;
viewHolder.playButton.setImageResource(R.drawable.pause_button);
if (mediaPlayer != null) {
//mediaPlayer.setLooping(true);
mediaPlayer.start();
}
mSeekbarUpdateHandler.postDelayed(mUpdateSeekbar, 0);
}
}

Questions about my memory problem for my soundboard android app (E/MediaPlayerNative: error (1, -19))

i have buttons to play sounds, they all works individually, after 14+ uses they dont work anymore if i doesn't restart the app
already tried to mediaplayer.realease after each use, doesnt work, think it is a memory problem dont know how do deal with it
MediaPlayer SonDeadliest
protected void onCreate(Bundle savedInstanceState)
{
boutonDeadliest.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
SonDeadliest = MediaPlayer.create(getApplicationContext(),R.raw.themostdeadliestganginuganda);
SonDeadliest.start();
fin(SonDeadliest);
}
});
public void fin(MediaPlayer m)
{
if(m.isPlaying())
{
}
else
{
m.stop();
m.release();
}
}
i have like 24 buttons, i'm trying to get an app with everything that works without having to restart the application thank you :)
The problem is that using MediaPlayer repeatedly in this way can be misleading:
SonDeadliest = MediaPlayer.create(getApplicationContext(),R.raw.themostdeadliestganginuganda);
SonDeadliest.start();
It will consume unnecessary resources.
For a better understanding I suggest that you read this post. It is written using Kotlin but with a little of kotlin study you can understand.
Here is a implementation in Java, trying to use your scenario:
MediaPlayer myMediaPlayer;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//Media Player setup
myMediaPlayer = new MediaPlayer();
myMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
myMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.reset();
}
});
//Buttons
Button button1 = view.findViewById(R.id.button_record1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playSound(R.raw.record1);
}
});
Button button2 = view.findViewById(R.id.button_record2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playSound(R.raw.record2);
}
});
}
void playSound(int rawResId) {
try {
AssetFileDescriptor afd = getContext().getResources().openRawResourceFd(rawResId);
if (afd != null) {
myMediaPlayer.reset();
myMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
myMediaPlayer.prepareAsync();
}
} catch (Exception ex) {
Log.d("MediaPLayer", "playSound exception: " + ex.getMessage());
}
}
The code can be improved, but my intention here was just to facilitate the comprehension. I'm using only 2 buttons for simplification. But I suggest you to consider using better solutions instead of creating a listener to each one of the 24 buttons. (Take a look on DataBinding, passing the resource as reference).

Android mediaplayer next song onButton Click

Button btnPlayPause, btnNext, btnPrevious;
Bundle b;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_song);
btnPlayPause = (Button) findViewById(R.id.btnPlayPause);
btnNext = (Button) findViewById(R.id.btnNext);
btnPrevious = (Button) findViewById(R.id.btnPrevious);
Intent currSong = getIntent();
b = currSong.getExtras();
btnPlayPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
btnPlayPause.setText("Play");
} else {
mediaPlayer.start();
btnPlayPause.setText("Stop");
}
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
btnPlayPause.setText("Play");
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
btnNext.setOnClickListener(new View.OnClickListener() {
int songIndex = (int) b.get("songIndex");
#Override
public void onClick(View v) {
if (songIndex < songList.size() - 1) {
songIndex = songIndex + 1;
Toast.makeText(getApplicationContext(), "You Clicked " + songIndex , Toast.LENGTH_SHORT).show();
try {
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.setDataSource(songList.get(songIndex).getData());
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}else{
songIndex=0;
}
}
});
btnPrevious.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (songIndex > 0){
songIndex = songIndex-1;
Toast.makeText(getApplicationContext(), "You Clicked " + songIndex , Toast.LENGTH_SHORT).show();
try {
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.setDataSource(songList.get(songIndex).getData());
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}else {
songIndex = songList.size() - 1;
}
}
});
}
}
So when i click on the next button, it plays the second song, but at the end of my songlist it skips the first song from the list stored in [0] and on second click it goes to [1] so there is always one song not playing.
How can i solve this or is there an easier way too go to the next song?
Thanks,
-Vince
EDIT;
Problem 2; when i choose a song, lets say with position 37, and i click on previous it goes to 36, but if i then click on next it goes to 38, so it skips the song i played first.
EDIT 2 ;
Ok problems still persists, but i found out that when i click a few times on previous btn let's say i start from position 25 and go to 15 with the previous btn, and when i then click on next again it just starts to count from 25. How can i solve this? Thanks in advance, - vince
Ok, you have several problems here:
i found out that when i click a few times on previous btn let's say i start from position 25 and go to 15 with the previous btn, and when i then click on next again it just starts to count from 25
In PlayNext's first line, you reassign songIndex to the original songIndex passed to the activity. PlayPrevious uses another songIndex that I can't see where is defined. If you had followed Android guidelines to always prefix with an "m" the class properties vs local variables, you'd easily have realised it.
So when i click on the next button, it plays the second song, but at the end of my songlist it skips the first song from the list stored in [0] and on second click it goes to [1]
The edge cases where user plays the next after the last song, or the previous after the first are incorrectly coded. In those cases you correctly update songIndex, but do not call play.
Besides the problems, what you should do is to rework your code a little using functions, to avoid repeating functionality. That is always prone to these kind of errors. I'd suggest something like this:
int mCurrentIndex; // always prefix class properties with an "m"
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_song);
Button btnPlayPause = (Button) findViewById(R.id.btnPlayPause),
btnNext = (Button) findViewById(R.id.btnNext),
btnPrevious = (Button) findViewById(R.id.btnPrevious);
Intent currSong = getIntent();
Bundle b = currSong.getExtras();
// load initial index only once
mCurrentIndex = (int) b.get("songIndex");
btnPrevious.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCurrentIndex = mCurrentIndex > 0 ? mCurrentIndex - 1 : mSongList.size() - 1;
playSongNumber(mCurrentIndex);
}
}
btnNext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCurrentIndex++;
mCurrentIndex %= mSongList.size();
playSongNumber(mCurrentIndex);
}
}
}
private void playSongNumber(int index) {
try {
mMediaPlayer.stop();
mMediaPlayer.reset();
mMediaPlayer.setDataSource(mSongList.get(index).getData());
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
slightly cleaner, isn't it?

How to properly interrupt a thread in android

In my application I have a button and when it gets clicked I start a new thread and change the text of button. If I press the button again it will start changing its text faster.
I would like to interrupt the thread when the button is pressed in the second time. What's the correct way to do it?
public class TestActivity extends Activity {
Button btn;
int i = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
runThread();
}
});
}
private void runThread() {
new Thread() {
public void run() {
while (i++ < 1000) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setText("#" + i);
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
In this case, just keep a reference to your thread and use Thread.interrupt():
private Thread runThread() {
return new Thread() {
public void run() {
while (i++ < 1000) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setText("#" + i);
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Then:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (myThread != null) myThread.interrupt();
myThread = runThread();
myThread.start();
}
});
Read this post for more info and options:
How to properly stop the Thread in Java?
In my opinion, the best way would be using a variable to control this.
Something like:
while(i++ < 1000 && keepRunning)
I see that as a good solution because it cant cause unexpected behavior, as you are sure the exactly moment your thread would exit.
extra--
As a suggestion, I also would recommend you to set your thread non-Damon (setDaemon(false)) because it makes layout changes
Also it is a good practice to give thread a name (setName()) to make it easy on debugging.
Right now you start a new Thread each time you press the button.
Something like this should work.
public class TestActivity extends Activity {
Button btn;
int i = 0;
Thread countThread = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
countThread = new Thread() {
public void run() {
while (i++ < 1000) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setText("#" + i);
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
runThread();
}
});
}
private void runThread() {
if(countThread != null) {
if(countThread.isAlive()) {
countThread.stop();
} else {
countThread.start();
}
}
}
I only had a text editor so I can't guarantee if this solves your problem.
You can use thread.interrupt() to interrupt the thread.
Try this, Just take another variable j and it will handle your code:-
public class TestActivity extends Activity {
Button btn;
int i = 0,j=0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
j=1;
runThread();
}
});
}
private void runThread() {
new Thread() {
public void run() {
while (i++ < 1000) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(j==1){
btn.setText("#" + i);
j=0;
}
else
Thread.interrupted();
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
You can use normal Thread in Android (and call interrupt() for your use case) but frameworks provides other better options by providing helper classes around Threads. You can refer to official documentation page for other options.
HandlerThread is preferred option. You can call quitSafely() or quit() for your use case if you go for HandlerThread
Related post:
Why use HandlerThread in Android

Categories