Android Tutorial: Prank app with SoundPool and MediaPlayer

In the last days I worked on a small prank app to try out how to set random events or actions in Android. I give it some thought to come up with a fun app to try out random events or actions in Android. I’m a fan of the Walking Dead TV show so why not develop a small Walking Dead app :)

In the app you are confronted with a close door, in which you need to pick the lock. After random clicks (or picks) the door opens and a zombie of the Walking Dead appears. Also the zombie which appears is based on a random selection. To make the app complete I added some special effects so to speak. A theme song to the app and a moaning zombie when you open the door plus the phone vibrates when you picked the lock successfully. Hopefully you get a bit scared with these special effects ;)

In this video you can see a small demo of the Walking Dead Prank App.

So lets start with the layout of our Walking Dead app. First we create 2 buttons in the layout. One Button that will function as a back button which will only be visible after you opened the door. The second button is the actual lock you need to pick. Later we will change this to a custom button as it needs to look like an actual lock.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:id="@+id/background"
    android:background="@drawable/backgrounddoor">

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="132dp"
        android:background="@drawable/buttons" />

    <Button
        android:id="@+id/btnback"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_marginBottom="38dp"
        android:layout_marginLeft="16dp"
        android:text="Back" />

</RelativeLayout>

Its pretty straightforward, but maybe worthwhile to mention, is how to set the background (the door) via the XML. You can set this via the property: android:background=”@drawable/backgrounddoor” (make sure you have the background image accessible via your drawable folder).

The user will have to pick the lock by clicking multiple times on the lock. From a user perspective it should be clear to the user that the app is actually working. Therefore we make a lock which have a clear pressed and unpressed state which is visualized while pressing the lock. First, create 2 locks which are different in color setting or gradients. I have created two lock images: lock.png en lockpressed.png

Then make the reference via a separate XML file (e.g. buttons.xml). The buttons.xml should look like this:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item 
        android:state_pressed="true"
        android:drawable="@drawable/lockpressed" />
    <item 
       
        android:drawable="@drawable/lock" />
   
</selector>

Also make sure you make the reference from your activity_main.xml to this buttons.xml file. See the reference android:background=”@drawable/buttons” in the activity_main.xml

Now we are done with setting up the layout elements. If everything went well you have a layout with a lock and background. Note that the back button is visible in this view. We will later set this invisible in the first activity.

In total this app will have two java classes. One MainActivity and one separate class to manage the sound of the zombie. Right click on your project and add an additional java class and name it SoundManager.

In the first part you create the variables. You need the Android SoundPool class, a Hashmap to store the sound file after your application have load the mp3 file, the AudioManager of Android to handle your sound and finally a Context.

	
	private  SoundPool mSoundPool; 
	private  HashMap<Integer, Integer> mSoundPoolMap; 
	private  AudioManager  mAudioManager;
	private  Context mContext;

After you have declared your variables we will create a SoundManager method. The createSounds passes the context and create a new SoundPool and hashmap object. The first parameter in the SoundPool argument refers to the number of sounds which can play in parallel (in our case just 1).

	public void createSounds(Context theContext) { 
		 mContext = theContext;
	     mSoundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0); 
	     mSoundPoolMap = new HashMap<Integer, Integer>(); 
	     mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); 	     
	} 

Third we create a method to add the sound. This way each sound file get indexed which is useful if we want to add additional sounds later in our app. For example when each zombie would have a different sound. In this tutorial we just keep it to one sound-file for all zombies.

	public void addSound(int Index,int SoundID)
	{
		mSoundPoolMap.put(1, mSoundPool.load(mContext, SoundID, 1));
	}
	

The next method is the actual play sound method. In this method we pass the index which we have created in the previous method. In this function we also use the sound settings (volume) as already been set by the device.

public void playSound(int index) { 
		
	     int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 
	     mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f); 
	}
	
	public void playLoopedSound(int index) { 
		
	     int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 
	     mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, -1, 1f); 
	}

See here the complete SoundManager Java class.

package com.christianpeeters.pranck;

import java.util.HashMap;

import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;	

public class SoundManager {
	
	private  SoundPool mSoundPool; 
	private  HashMap<Integer, Integer> mSoundPoolMap; 
	private  AudioManager  mAudioManager;
	private  Context mContext;
	
		
	public void createSounds(Context theContext) { 
		 mContext = theContext;
	     mSoundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0); 
	     mSoundPoolMap = new HashMap<Integer, Integer>(); 
	     mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); 	     
	} 
	
	public void addSound(int Index,int SoundID)
	{
		mSoundPoolMap.put(1, mSoundPool.load(mContext, SoundID, 1));
	}
	
	public void playSound(int index) { 
		
	     int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 
	     mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f); 
	}
	
	public void playLoopedSound(int index) { 
		
	     int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 
	     mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, -1, 1f); 
	}
	
}

Now that we have set the Java class we can start building the Main java class which operates the Walking Dead app.

First we need to set the variables we need:
- Create 2 buttons. One btnback for the back button and one button for the lock (btnlock).
- Create a SoundManager variable to initiate an object for the class we just created (SoundManager.java)
- Create a variable to generate a random number
- Create an Array which hold our zombie images (they should be in your drawable folder).
- Create a MediaPlayer variable.

	Button btnback, btnlock;
	private SoundManager mSoundManager;
	MediaPlayer player;
	private static final Random rgenerator = new Random();
	private static final Integer[] mImageIds = 
	    {R.drawable.suprisezombie0, R.drawable.suprisezombie1, R.drawable.suprisezombie2, 
		R.drawable.suprisezombie3, R.drawable.suprisezombie4};

As you probably have noticed I have used both the SoundPool as the MediaPlayer for Android. I wanted to configure a background sound in the app and noticed that this wasn’t possible with the SoundPool Manager of Android. Somehow, SoundPool only allows you to configure a sound after a certain event (e.g. button click) and doesn’t allow you to play a background sound. In case, you know how it is possible to use the SoundPool Manager to configure a background sound, please leave a comment on the end of this post.

So first we create the background sound of the app, I used the theme song for this of the Walking Dead. As you can see I have a raw folder with the Theme MP3 file. I have also set the looping on false. So the theme song is only played once. When the app is launched it will directly start the theme song.

Next, I created a new object of the SoundManager class, with index 1 referring to the raw folder with the zombiesound.

        player = MediaPlayer.create(this, R.raw.theme);
        player.setLooping(false); // Set looping
        player.setVolume(100,100);
        player.start();
        
        
        mSoundManager = new SoundManager();
        mSoundManager.createSounds(getBaseContext());
        mSoundManager.addSound(1, R.raw.zombiesound);
        

The next lines of codes are the references to the buttons, this should be straightforward. The btnback is set as invisible (Button.gone) as the back button should only appear after the user has picked the lock successfully.

        btnback = (Button)findViewById(R.id.btnback);
        btnback.setVisibility(Button.GONE);
        btnlock = (Button)findViewById(R.id.btn1);
        btnlock.setOnClickListener(this);
        }
   

The next step is to set the behavior of the lock when the user clicks on it. We want the user to have the feeling he has to pick a lock. Therefore, the user might have to click on the lock button several times before it opens the door. We do this by using the random number generator in the onClick method of the btnlock.
We create an if statement telling the button that the door should only open and perform the suprise() method after: the random number which is generated is equal to the number of clicks of the user on the btnlock. As a random number could be very large, and probably test the patience of the user to much we set a maximum and minimun on the number of clicks required or at least not more than 15 clicks.

	@Override
	public void onClick(View v) {
		
		// TODO Auto-generated method stub
		if (v.getId()==R.id.btn1) {
			int i = 15;
			int min = 3;
			int max = 8;

			
			if ((rgenerator.nextInt(max - min + 1) + min == 8) || (i<15)) {
			suprise();
			((Button) v).setOnClickListener(null);
			
			}
			
			}
	}	

The next step is to create the suprise effect of the app. When the user has successfully picked the lock the door opens and the follow things will happen:
- We stop the theme-song of the app (background sound)
- We create the soundeffect of a zombie screaming to the user :)
- We create the visual effect, that is one of the set of zombie images are displayed to the user
- And as cherry on the cake, we let the phone vibrate.

	private void suprise() {
	player.stop();
	soundeffect();
	visualeffect();
	vibrationeffect();
	}

In the soundeffect() method we play the sound of the SoundManager with Index 1
In the visualeffect() method we change the background image to one of the random images (method: changeImageResource()) and we make the back-button visible to the user. Plus we create an onClicklistener to tell the backbutton what to do (going back to the main screen).
In the vibratoreffect() we use the vibrator service of Android to create a new vibrator effect.

		private void soundeffect(){
			mSoundManager.playSound(1);
		}
	
		private void visualeffect() 	{
			
		changeImageResource();

		View b = findViewById(R.id.btn1);
		b.setVisibility(View.GONE);
		btnback.setVisibility(Button.VISIBLE);
		btnback.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View back) {
				
				Intent backevent = new Intent(getApplicationContext(), MainActivity.class);
				startActivity(backevent);

			}
		});
		}
		
		private void vibrationeffect() {
		Vibrator vib = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
		vib.vibrate(new long[]{ 0, 500, 0 }, -1);
		}
	

The changeImageResource method use the same random number generator which we used for the number of picks on the lock button. As we have in total five different zombie images we pass (5) in the integer which we create in this method. The next line just set the backgroundresource (or background image) based on the order of the Array we have created earlier.

		public void changeImageResource()
		{
		    int i = rgenerator.nextInt(5);
		    findViewById(R.id.background).setBackgroundResource(mImageIds[i]);
		    
		}
		

Finally, when the user clicks on the home button, we dont want the app to continue to play the theme-song. Therefore we call player.stop in the onPause().

Your complete MainActivity should now look like this:

package com.christianpeeters.pranck;

import java.util.Random;

import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Vibrator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener{
	
	
	Button btnback, btnlock;
	private SoundManager mSoundManager;
	MediaPlayer player;
	private static final Random rgenerator = new Random();
	private static final Integer[] mImageIds = 
	    {R.drawable.suprisezombie0, R.drawable.suprisezombie1, R.drawable.suprisezombie2, 
		R.drawable.suprisezombie3, R.drawable.suprisezombie4};
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        
        player = MediaPlayer.create(this, R.raw.theme);
        player.setLooping(false); // Set looping
        player.setVolume(100,100);
        player.start();
        
        
        mSoundManager = new SoundManager();
        mSoundManager.createSounds(getBaseContext());
        mSoundManager.addSound(1, R.raw.zombiesound);
        
       
        btnback = (Button)findViewById(R.id.btnback);
        btnback.setVisibility(Button.GONE);
        btnlock = (Button)findViewById(R.id.btn1);
        btnlock.setOnClickListener(this);
        }
    
   
    	
	@Override
	public void onClick(View v) {
		
		// TODO Auto-generated method stub
		if (v.getId()==R.id.btn1) {
			int i = 15;
			int min = 3;
			int max = 8;

			
			if ((rgenerator.nextInt(max - min + 1) + min == 8) || (i<15)) {
			suprise();
			((Button) v).setOnClickListener(null);
			
			}
			
			}
	}	

	private void suprise() {
	player.stop();
	soundeffect();
	visualeffect();
	vibrationeffect();
	}

		private void soundeffect(){
			mSoundManager.playSound(1);
		}
	
		private void visualeffect() 	{
			
		changeImageResource();

		View b = findViewById(R.id.btn1);
		b.setVisibility(View.GONE);
		btnback.setVisibility(Button.VISIBLE);
		btnback.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View back) {
				
				Intent backevent = new Intent(getApplicationContext(), MainActivity.class);
				startActivity(backevent);

			}
		});
		}
		
		private void vibrationeffect() {
		Vibrator vib = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
		vib.vibrate(new long[]{ 0, 500, 0 }, -1);
		}
	
		
		public void changeImageResource()
		{
		    int i = rgenerator.nextInt(5);
		    findViewById(R.id.background).setBackgroundResource(mImageIds[i]);
		    
		}
		
		
		@Override
		protected void onPause		() {
		    super.onPause();
		    player.stop();
		}
		
		
}


4 Responses

  1. xxx December 10, 2012 / 8:09 pm

    Great dude thank you so much .
    could you plz provide download link for this tutorial

  2. arun February 15, 2013 / 2:04 pm

    hai,
    can you please provide download project link for this tutorial:)

    • Christian February 15, 2013 / 6:19 pm

      Hi Arun,

      I have some problems with my WordPress to make zip-files available for download. I made it available via this link (up till 1st of March). Let me know if you see this message after the 1st of March and I ‘m happy to make it available again for you. http://we.tl/iF9VMxfscF

  3. Bhavik May 8, 2013 / 10:07 am

    I like your tutorial.
    Can you Please send me this project on my Email id-bkothari99@gmail.com

    Please send me ….I am waiting for your reply…

Leave a Reply to Bhavik Cancel reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*