I'm new to Android programming and am building a simple app that plays a sound from a local file when you flip the phone over, using a SensorListener for the accelerometer. Additionally, there's an image with a click listener that also plays the same sound when touched.
My code has the following sequence:
onCreate and onRestart - call MediaPlayer.create() for the local file.
onDestroy - call MediaPlayer.release and set the reference to null.
I'm debugging the app on my phone since the emulator doesn't support accelerometers.
When I click the image, I get a start called in state 64 error in Logcat, and when I flip the phone, the app FCs with a NullPointerException when mp.start() is called.
My understanding of how it works, from the MediaPlayer documentation is that you have to call create(), which in turn calls prepare() before you can start or stop the player. Is there a problem with the sequence of steps?
I've also tried releasing the media player in onStop, to no avail.
I have the following sensor listener for the accelerometer (mp is the MediaPlayer object) -
private final SensorEventListener accelerometerListener = new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent arg0) {
float z_value = arg0.values[2];
if (z_value < 0 && playerReady) {
mp.start(); // <-- NullPointerException thrown here.
} else {
mp.stop();
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
..and the following click listener for the button -
private final View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (playerReady) {
mp.start();
}
}
};
I also wrote an onPreparedListener for the media player (which sets the playerReady seen above)
private final OnPreparedListener opl = new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
playerReady = true;
}
};
Edit Here's onCreate() and onRestart() -
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp = MediaPlayer.create(Moo.this, R.raw.moo);
mp.setOnPreparedListener(opl);
ImageView im = (ImageView) findViewById(R.id.imageView1);
im.setOnClickListener(onClickListener);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
List<Sensor> sensorList = sensorManager
.getSensorList(Sensor.TYPE_ACCELEROMETER);
if (sensorList.size() > 0) {
accelerometerPresent = true;
accelerometerSensor = sensorList.get(0);
sensorManager.registerListener(accelerometerListener,
accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
#Override
protected void onRestart() {
super.onRestart();
mp = MediaPlayer.create(Moo.this, R.raw.moo);
}
Related
I'm working on an app that needs to play the system notification at different volume levels and have some issues with getting setVolume to work properly.
To debug this, I prepared a sample app with two buttons that allows me to play the system notification at a low or a high volume. So far, the app works, and the sound comes out of the speaker.
However, no matter in which sequence I press the low and high volume buttons, once I have played the notification at a low volume, it stays low, I can't get it to play at the higher volume again.
Below is my code. I used an oncompletion callback to increment a counter and sound the notification 3 times.
public class MainActivity extends AppCompatActivity {
int soundsCounter = 0; // Counts how many times the notification has sounded
double playbackVolume = 1.0; // Playback volume of the notification
AudioManager audioManager;
MediaPlayer thePlayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Setup the media player
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
thePlayer = MediaPlayer.create(getApplicationContext(), RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
// Initialize listener using setOnCompletionListener for mediaPlayer object
// and declare new method OnComletionListener as an argument.
thePlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
// Override onCompletion method to apply desired operations.
#Override
public void onCompletion(MediaPlayer mediaPlayer){
soundsCounter++;
if (soundsCounter < 3){ // Sound the notification 3 times
playNotification();
}
}
} );
}
// Plays system notification at the selected volume
private void playNotification() {
try {
thePlayer.setVolume(((float) (audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) * playbackVolume)),
(float) (audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) * playbackVolume));
}
thePlayer.start();
}
// Called when the user taps the HighV button
public void playHighV(View view) {
soundsCounter = 0;
playbackVolume = 1.0;
playNotification();
}
// Called when the user taps the LowV button
public void playLowV(View view) {
soundsCounter = 0;
playbackVolume = 0.02;
playNotification();
}
Any ideas where to look? Thanks!
Thanks to Oemel09 I realized I was making some mistakes in the parameters passed to setVolume.
Here's the new code that works fine:
public class MainActivity extends AppCompatActivity {
int soundsCounter = 0; // Counts how many times the notification has sounded
float playbackVolume = 1; // Playback volume of the notification
AudioManager audioManager;
MediaPlayer thePlayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Setup the media player
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
thePlayer = MediaPlayer.create(getApplicationContext(), RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
// Initialize listener using setOnCompletionListener for mediaPlayer object
// and declare new method OnComletionListener as an argument.
thePlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
// Override onCompletion method to apply desired operations.
#Override
public void onCompletion(MediaPlayer mediaPlayer){
soundsCounter++;
if (soundsCounter < 3){ // PLay sound 3 times
playNotification();
}
}
} );
}
// Plays system notification at the selected volume
private void playNotification() {
thePlayer.setVolume(playbackVolume, playbackVolume);
thePlayer.start();
}
// Called when the user taps the HighV button
public void playHighV(View view) {
soundsCounter = 0;
playbackVolume = 1f;
playNotification();
}
// Called when the user taps the LowV button
public void playLowV(View view) {
soundsCounter = 0;
playbackVolume = 0.1f;
playNotification();
}
}
I want to play a repeating audio when my hand is near the proximity sensor and stop the audio once the hand is moved away from the sensor. This is the code i have written.. The audio plays when i go near the proximity sensor but it does not stop when my hand moves away. Any suggestions? Thanks in advance.
public class MainActivity extends Activity implements SensorEventListener {
private SensorManager sensorManager;
private Sensor proxy;
TextView tv;
MediaPlayer mp;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
proxy = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
tv = (TextView) findViewById(R.id.tv);
//tv.setText("Maximum range = " + proxy.getMaximumRange() + proxy.getName());
}
#Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}
#Override
protected void onResume() {
super.onResume();
sensorManager.registerListener(this,proxy,SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onSensorChanged(SensorEvent event) {
if(event.sensor.getType() == Sensor.TYPE_PROXIMITY){
mp = MediaPlayer.create(this,R.raw.alarm);
if(event.values[0] < proxy.getMaximumRange()){
mp.start();
mp.setLooping(true);
}
else{
mp.stop();
}
//tv.setText("SEnsor NOT activated");
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
Everytime in the when the onSensorChanged(SensorEvent event) is called your code is initializing the media player.
When you are trying to call stop() method, it is being called in the new object you just created and the previous object is somewhere running the alarm in memory already, hence it is not stopping.
According to documentation stop() has no effect if you start() is never called yet, ideally, it should throw an exception but apparently, there is no effect.
I have modified your code, which I suggest, should work (possibly).
public class MainActivity extends Activity implements SensorEventListener {
private SensorManager sensorManager;
private Sensor proxy;
TextView tv;
static final MediaPlayer mp= MediaPlayer.create(this,R.raw.alarm);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
proxy = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
tv = (TextView) findViewById(R.id.tv);
/**
* set on prepared listener
*/
mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
mp.setLooping(true);
}
});
}
#Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}
#Override
protected void onResume() {
super.onResume();
sensorManager.registerListener(this,proxy,SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onSensorChanged(SensorEvent event) {
if(event.sensor.getType() == Sensor.TYPE_PROXIMITY){
if(event.values[0] < proxy.getMaximumRange()){
mp.prepareAsync();
}
else{
mp.stop();
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
I use exoplayer to play audio file.
You must add this library:
compile 'com.google.android.exoplayer:exoplayer:r2.5.4'
and add read permission:
"android.permission.READ_EXTERNAL_STORAGE"
Code:
public class MainActivity extends Activity implements SensorEventListener {
private SensorManager sensorManager;
private Sensor sensor;
public static final int MY_PERMISSIONS_REQUEST_READ = 1 ;
private SimpleExoPlayer exoPlayer ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestPermissions() ;
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
initExoPlayer();
}
public void initExoPlayer(){
DefaultRenderersFactory defaultRenderersFactory = new DefaultRenderersFactory(this , null , DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector();
exoPlayer = ExoPlayerFactory.newSimpleInstance(defaultRenderersFactory , defaultTrackSelector) ;
String agent = Util.getUserAgent(this, "exoplayer") ;
ExtractorMediaSource extractorMediaSource = new ExtractorMediaSource(your_audio_uri , new DefaultDataSourceFactory(this , agent)
, new DefaultExtractorsFactory() , null , null) ;
exoPlayer.prepare(extractorMediaSource);
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initExoPlayer();
}
return;
}
// other 'switch' lines to check for other
// permissions this app might request
}
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
if (event.values[0] < sensor.getMaximumRange()) {
exoPlayer.setAudioStreamType(C.STREAM_TYPE_VOICE_CALL);
exoPlayer.setPlayWhenReady(true);
} else {
exoPlayer.setPlayWhenReady(false);
}
}
}
#Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}
#Override
protected void onResume() {
super.onResume();
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public void requestPermissions() {
if (Build.VERSION.SDK_INT >= 23) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ);
}
}
}
I am creating a game, I want to play a background music for one activity only(For main menu of game), my code shown below, The problem is that the music plays more than one time, I want to play the same music also when activity Resumes.
public class Menu extends Activity {
MediaPlayer mp
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.menu);
mp = MediaPlayer.create(Menu.this, R.raw.adalante);
if(!mp.isPlaying()) {
mp.start();
}
public void play(View ButtonClicked) {
mp.stop();
mp.release();
//mp = MediaPlayer.create(Menu.this, R.raw.l);
//mp.start();
goToActivity(Game.class);
}
#Override
public void onResume() {
super.onResume(); // Always call the superclass method first
//coins
coin.setText(data.getString("coin"));
mp = MediaPlayer.create(Menu.this, R.raw.adalante);
if(!mp.isPlaying()) {
mp.start();
}
//mps.release();
}
In your onResume don't initialise MediaPlayer again and again. It creates new instance every time when you come to onResume. So add a check in onResume like this :
#Override
protected void onResume() {
super.onResume();
if (mp==null)
mp=MediaPlayer.create(MainActivity.this,R.raw.adalante);
if (!mp.isPlaying())
mp.start();
}
and additionally add this for prevention to play when activity goes to onPause
#Override
protected void onPause() {
super.onPause();
mp.pause();
}
I am making a new android sound application. I made a clickable button to play sound when I click on it. But I also want it to stop playing sound when I click for the second time. That part works fine now here is the problem, when I click again on button to play sound again, it doesn't play it, Media player is completely stopped. I was looking on forums but I can't seem to find an answer that could help me.
Here is my Activity:
MediaPlayer mpButtonClick1;
MediaPlayer mpButtonClick2;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.prvi);
final MediaPlayer mpButtonClick1 = MediaPlayer.create(this, R.raw.spalshm);
final MediaPlayer mpButtonClick2 = MediaPlayer.create(this, R.raw.splashs);
Button dugme = (Button) findViewById(R.id.dugme);
dugme.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mpButtonClick1.isPlaying()) {
mpButtonClick1.stop();
mpButtonClick1.reset();
}
else {
mpButtonClick1.start();
}
}
});
When I try to write mpButtonClick1.prepare(); I get error Unhandled Exception Type IOE exception
Try to use pause instead of stop.
Reason: if you pause the MediaPlayer, then you can resume it later. However, if you use stop, almost any other method won't work and you will have to prepare the MediaPlayer again (or create a new one).
More info: here and here
PS: don't forget to release the memory when you finish using the resources.
Try this:
You should use only one mediaplayer object
public class PlayaudioActivity extends Activity {
private MediaPlayer mp;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button) findViewById(R.id.button1);
Button b2 = (Button) findViewById(R.id.button2);
final TextView t = (TextView) findViewById(R.id.textView1);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stopPlaying();
mp = MediaPlayer.create(PlayaudioActivity.this, R.raw.far);
mp.start();
}
});
b2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stopPlaying();
mp = MediaPlayer.create(PlayaudioActivity.this, R.raw.beet);
mp.start();
}
});
}
private void stopPlaying() {
if (mp != null) {
mp.stop();
mp.release();
mp = null;
}
}
}
Change your class with below code:
remove reset();.
init well all components:
MediaPlayer mpButtonClick1;
MediaPlayer mpButtonClick2;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.prvi);
mpButtonClick1 = MediaPlayer.create(this, R.raw.spalshm);
mpButtonClick2 = MediaPlayer.create(this, R.raw.splashs);
Button dugme = (Button) findViewById(R.id.dugme);
dugme.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mpButtonClick1.isPlaying()) {
mpButtonClick1.stop();
}
else {
mpButtonClick1.start();
}
}
});
You're calling mpButtonClick1.reset() after mpButtonClick1.stop() - don't do that:
if (mpButtonClick1.isPlaying()) {
mpButtonClick1.stop();
mpButtonClick1.reset(); //<--------- calling reset(), remove this line
}
The docs for reset() say:
Resets the MediaPlayer to its uninitialized state. After calling this method, you will have to initialize it again by setting the data source and calling prepare().
Remove mpButtonClick1.reset() and it should work.
Keep in mind that MediaPlayer works as a state machine, which means that if you call methods in the wrong order, you'll get problems. Please read about MediaPlayer here and here.
Hey please use following
for stop -> media player
mp.seekTo(0);
mp.pause();
again for start just call
mp.start();
In my experience when I need to play multiple times and I may need to stop one play to start another play, (like in the case of multiple buttons), I just create another player, making sure that I release the resources for the previous one. To stop just use
mediaPlayer.stop();
But for play use something like this (adapt the logging to your specific needs) to create/recreate your player:
private boolean createMediaPlayer()
{
if (mediaPlayer!=null)
{
if(mediaPlayer.isPlaying())
{
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer=null;
}
}
mediaPlayer = new MediaPlayer();
mediaPlayer.setVolume(1f, 1f);
try
{
mediaPlayer.setAudioStreamType(Interop.PRIMARY_STREAM);
mediaPlayer.setDataSource(m_soundFile);
mediaPlayer.prepare();
return true;
// Interop.logDebug(TAG + "-loadAudio: SUCCESS" + m_soundFile);
} catch (Exception e)
{
Interop.logError(TAG + "-LoadAudio for Clic Sound: audioPlayer prepare failed for current file: " + m_soundFile);
Interop.logError(TAG + "-Exception: " , e);
return false;
}
}
and than use
if (createMediaPlayer())
mediaPlayer.start();
this will ensure proper release of the resources used by the media player.
A simple solution is to Use pause instead of stop and the seek to the beginning of the song.
I know that this question is quite old but recently while learning Android, I also got stuck at this point and found a very simple solution which I'd like to share with everyone.
Instead of trying to stop or reset the media, you can just seek back to the starting position.
mediaPlayer.seekTo(0);
For reference, I am also posting my code below:
public class MainActivity extends AppCompatActivity {
MediaPlayer mp;
public void play(View view) {
mp.start();
}
public void pause(View view) {
mp.pause();
}
public void stop(View view) {
// this seeks to the beginning of the file
mp.seekTo(0);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp = MediaPlayer.create(this, R.raw.sample_audio);
}
}
This has been driving me insane for days, i am new to android and i can't seem to figure out how to access resources from other threads. I am trying to schedule a task to execute after a while, here is the relevant code:
public class TikTakBoom extends Activity {
private SensorManager mSensorManager;
public ToggleButton startButton;
private Bitmap mBombOn;
private Bitmap mBombOff;
/** Tik tak boom **/
protected MediaPlayer mediaPlayer; // play tik tak or boom
protected Timer boomTimer; // timer for explosion
// bomb button backgrounds
protected android.graphics.drawable.BitmapDrawable buttonBombOn;
protected android.graphics.drawable.BitmapDrawable buttonBombOff;
// random boom delay in ms
protected long boomDelay = 1500;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
// tell system to use the layout defined in our XML file
setContentView(R.layout.main);
Log.w(this.getClass().getName(), "SIS is null");
// configure toggle button (bomb on/off)
startButton = (ToggleButton)findViewById(R.id.toggleButton1);
startButton.setText("");
startButton.setTextOn("");
startButton.setTextOff("");
// scale bomb images to button size
mBombOff = BitmapFactory.decodeResource(this.getResources(), R.drawable.bomb_off);
mBombOff = Bitmap.createScaledBitmap(mBombOff, 120, 120, true);
mBombOn = BitmapFactory.decodeResource(this.getResources(), R.drawable.bomb_on);
mBombOn = Bitmap.createScaledBitmap(mBombOn, 120, 120, true);
buttonBombOn = new android.graphics.drawable.BitmapDrawable(mBombOn);
buttonBombOff = new android.graphics.drawable.BitmapDrawable(mBombOff);
startButton.setChecked(false);
boomTimer = new Timer();
// onclick listener for toggle bomb on/off
startButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
if (startButton.isChecked()) {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.tik_tak);
mediaPlayer.setLooping(true);
mediaPlayer.start();
// align boom delay with tik tak duration
boomDelay -= boomDelay%mediaPlayer.getDuration();
boomTimer.schedule(new BoomTimeTask(getApplicationContext()), boomDelay);
startButton.setBackgroundDrawable(buttonBombOn);
} else {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
startButton.setBackgroundDrawable(buttonBombOff);
}
}
});
}
class BoomTimeTask extends TimerTask {
Context context;
public BoomTimeTask(Context context) {
super();
this.context = context;
}
public void run() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.sheldon);
mediaPlayer.setLooping(false);
mediaPlayer.start();
startButton = (ToggleButton)((Activity)context).findViewById(R.id.toggleButton1);
startButton.setChecked(false);
//startButton.setBackgroundDrawable(buttonBombOff);
}
}
App crashes on startButton.setChecked(false) in BoomTimeTask.run(), i am guessing NullPointerException.
Pls help, i am gonna go insane, i have tried everything! Nothing works.. Tried it without the context passed as a parameter, since the reference should be visible to BoomTimeTask thread as well, tried a dozen of other things, just can't get it to work.
What am i missing? Thx in advance, would really appreciate any kind of help. Cheers, Val.
SOLVED: how to change button text dynamically for every 3 sec in android?
You should use
runOnUiThread(new Runnable() {
public void run() {
// YOUR ACTIONS ON Graphic interface
}
});
It's because in android you can't modify GUI elements without being in the thread that deal with GUI.
http://developer.android.com/reference/android/app/Activity.html