package course.labs.graphicslab;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.media.AudioManager;
import android.media.SoundPool;
import android.media.SoundPool.OnLoadCompleteListener;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
public class BubbleActivity extends Activity {
// These variables are for testing purposes, do not modify
private final static int RANDOM = 0;
private final static int SINGLE = 1;
private final static int STILL = 2;
private static int speedMode = RANDOM;
private static final String TAG = "Lab-Graphics";
// The Main view
private RelativeLayout mFrame;
// Bubble image's bitmap
private Bitmap mBitmap;
// Display dimensions
private int mDisplayWidth, mDisplayHeight;
// Sound variables
// AudioManager
private AudioManager mAudioManager;
// SoundPool
private SoundPool mSoundPool;
// ID for the bubble popping sound
private int mSoundID;
// Audio volume
private float mStreamVolume;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Set up user interface
mFrame = (RelativeLayout) findViewById(R.id.frame);
// Load basic bubble Bitmap
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b64);
}
@Override
protected void onResume() {
super.onResume();
// Manage bubble popping sound
// Use AudioManager.STREAM_MUSIC as stream type
mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
mStreamVolume = (float) mAudioManager
.getStreamVolume(AudioManager.STREAM_MUSIC)
/ mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
// TODO - make a new SoundPool, allowing up to 10 streams
// TODO - load the sound from res/raw/bubble_pop.wav
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// Get the size of the display so this View knows where borders are
mDisplayWidth = mFrame.getWidth();
mDisplayHeight = mFrame.getHeight();
}
}
@Override
protected void onPause() {
mSoundPool.unload(mSoundID);
mSoundPool.release();
mSoundPool = null;
super.onPause();
}
// BubbleView is a View that displays a bubble.
// This class handles animating, drawing, and popping amongst other actions.
// A new BubbleView is created for each bubble on the display
public class BubbleView extends View {
private static final int BITMAP_SIZE = 64;
private static final int REFRESH_RATE = 40;
private final Paint mPainter = new Paint();
private ScheduledFuture<?> mMoverFuture;
private int mScaledBitmapWidth;
private Bitmap mScaledBitmap;
// location, speed and direction of the bubble
private float mXPos, mYPos, mDx, mDy, mRadius, mRadiusSquared;
private long mRotate, mDRotate;
BubbleView(Context context, float x, float y) {
super(context);
Log.i(TAG, "Creating Bubble at: x:" + x + " y:" + y);
// Create a new random number generator to
// randomize size, rotation, speed and direction
Random r = new Random();
// Creates the bubble bitmap for this BubbleView
createScaledBitmap(r);
// Radius of the Bitmap
mRadius = mScaledBitmapWidth / 2;
mRadiusSquared = mRadius * mRadius;
// Adjust position to center the bubble under user's finger
mXPos = x - mRadius;
mYPos = y - mRadius;
// Set the BubbleView's speed and direction
setSpeedAndDirection(r);
// Set the BubbleView's rotation
setRotation(r);
mPainter.setAntiAlias(true);
}
private void setRotation(Random r) {
if (speedMode == RANDOM) {
// TODO - set rotation in range [1..3]
} else {
mDRotate = 0;
}
}
private void setSpeedAndDirection(Random r) {
// Used by test cases
switch (speedMode) {
case SINGLE:
mDx = 20;
mDy = 20;
break;
case STILL:
// No speed
mDx = 0;
mDy = 0;
break;
default:
// TODO - Set movement direction and speed
// Limit movement speed in the x and y
// direction to [-3..3] pixels per movement.
}
}
private void createScaledBitmap(Random r) {
if (speedMode != RANDOM) {
mScaledBitmapWidth = BITMAP_SIZE * 3;
} else {
// TODO - set scaled bitmap size in range [1..3] * BITMAP_SIZE
}
// TODO - create the scaled bitmap using size set above
}
// Start moving the BubbleView & updating the display
private void start() {
// Creates a WorkerThread
ScheduledExecutorService executor = Executors
.newScheduledThreadPool(1);
// Execute the run() in Worker Thread every REFRESH_RATE
// milliseconds
// Save reference to this job in mMoverFuture
mMoverFuture = executor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
// TODO - implement movement logic.
// Each time this method is run the BubbleView should
// move one step. If the BubbleView exits the display,
// stop the BubbleView's Worker Thread.
// Otherwise, request that the BubbleView be redrawn.
}
}, 0, REFRESH_RATE, TimeUnit.MILLISECONDS);
}
// Cancel the Bubble's movement
// Remove Bubble from mFrame
// Play pop sound if the BubbleView was popped
private void stop(final boolean wasPopped) {
if (null != mMoverFuture) {
if (!mMoverFuture.isDone()) {
mMoverFuture.cancel(true);
}
// This work will be performed on the UI Thread
mFrame.post(new Runnable() {
@Override
public void run() {
// TODO - Remove the BubbleView from mFrame
// TODO - If the bubble was popped by user,
// play the popping sound
Log.i(TAG, "Bubble removed from view!");
}
});
}
}
// Draw the Bubble at its current location
@Override
protected synchronized void onDraw(Canvas canvas) {
// TODO - save the canvas
// TODO - increase the rotation of the original image by mDRotate
// TODO Rotate the canvas by current rotation
// Hint - Rotate around the bubble's center, not its position
// TODO - draw the bitmap at it's new location
// TODO - restore the canvas
}
// Returns true if the BubbleView is still on the screen after the move
// operation
private synchronized boolean moveWhileOnScreen() {
// TODO - Move the BubbleView
return isOutOfView();
}
// Return true if the BubbleView is still on the screen after the move
// operation
private boolean isOutOfView() {
// TODO - Return true if the BubbleView is still on the screen after
// the move operation
return true || false;
}
}
@Override
public void onBackPressed() {
openOptionsMenu();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO- Add cases to handle addition and deletion of bubbles
// from options menu added bubbles should be given random locations.
// The bubble to delete is the most recently added bubble that
// is still in the frame.
// Hint: You can get all Views in mFrame using the
// ViewGroup.getChildCount() method
switch (item.getItemId()) {
case R.id.menu_still_mode:
speedMode = STILL;
return true;
case R.id.menu_single_speed:
speedMode = SINGLE;
return true;
case R.id.menu_random_mode:
speedMode = RANDOM;
return true;
case R.id.quit:
exitRequested();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void exitRequested() {
super.onBackPressed();
}
}