Android Open Source - android-002 Bubble Activity






From Project

Back to project page android-002.

License

The source code is released under:

Apache License

If you think the Android project android-002 listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package course.labs.graphicslab;
/*  w ww. j a v a 2s .  com*/
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;

  // Gesture Detector
  private GestureDetector mGestureDetector;

  @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();
    Log.d(TAG, "In 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
    mSoundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);

    // TODO - set a SoundPool OnLoadCompletedListener that calls
    // setupGestureDetector()
    mSoundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {

      @Override
      public void onLoadComplete(SoundPool soundPool, int sampleId,
          int status) {
        Log.d(TAG, "SoundPool has finished loading sound " + sampleId);
        setupGestureDetector();
      }
    });
    
    // TODO - load the sound from res/raw/bubble_pop.wav
    mSoundID = mSoundPool.load(this, R.raw.bubble_pop, 1);
  }

  @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();
    }
  }

  // Set up GestureDetector
  private void setupGestureDetector() {
    
    Log.d(TAG, "In setupGestureDetector()...");

    mGestureDetector = new GestureDetector(this,
    new GestureDetector.SimpleOnGestureListener() {

      // If a fling gesture starts on a BubbleView then change the
      // BubbleView's velocity

      @Override
      public boolean onFling(MotionEvent event1, MotionEvent event2,
          float velocityX, float velocityY) {

        // TODO - Implement onFling actions.
        // You can get all Views in mFrame one at a time
        // using the ViewGroup.getChildAt() method
        Log.d(TAG, "onFling() with MotionEvents " + event1 + ", " + event2 
            + ", xVelocity:" + velocityX + ", yVelocity:" + velocityY);
        int numberOfBubbles = mFrame.getChildCount();
        for (int i = 0; i < numberOfBubbles; i++) {
          BubbleView bubble = (BubbleView) mFrame.getChildAt(i);
          if ( bubble.intersects( event1.getX(), event1.getY() ) ) {
            Log.d(TAG, "Flinging bubble (" + bubble.mXPos + "," 
                + bubble.mYPos + ") for MotionEvent at coordinates " 
                + event2.getX() + "," + event2.getY() );
            bubble.deflect(velocityX, velocityY);
            break;
          }
        }
        
        return true;
      }

      // If a single tap intersects a BubbleView, then pop the BubbleView
      // Otherwise, create a new BubbleView at the tap's location and add
      // it to mFrame. You can get all views from mFrame with
      // ViewGroup.getChildAt()

      @Override
      public boolean onSingleTapConfirmed(MotionEvent event) {

        // TODO - Implement onSingleTapConfirmed actions.
        // You can get all Views in mFrame using the
        // ViewGroup.getChildCount() method
        Log.d(TAG, "onSingleTapConfirmed() with MotionEvent " + event);

        boolean popped = false;
        int numberOfBubbles = mFrame.getChildCount();
        for (int i = 0; i < numberOfBubbles; i++) {
          BubbleView bubble = (BubbleView) mFrame.getChildAt(i);
          if ( bubble.intersects( event.getX(), event.getY() ) ) {
            Log.d(TAG, "Removing bubble (" + bubble.mXPos + "," 
                + bubble.mYPos + ") for MotionEvent at coordinates " 
                + event.getX() + "," + event.getY() );
            bubble.stopMovement(true);
            popped = true;
            break;
          }
        }
        
        if (popped == false) {
          Log.d(TAG, "Creating new bubble  at coordinates " 
              + event.getX() + "," + event.getY() );
          BubbleView bubble = 
            new BubbleView(BubbleActivity.this, event.getX(), event.getY() );
          mFrame.addView(bubble);
          bubble.startMovement();
        }
        
        return true;
      }
    });
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {

    Log.d(TAG, "In onTouchEvent() with MotionEvent " + event);
    
    // TODO - Delegate the touch to the gestureDetector
    return mGestureDetector.onTouchEvent(event);
  }

  @Override
  protected void onPause() {

    // TODO - Release all SoundPool resources
    Log.d(TAG, "In onPause()...");
    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.d(TAG, "In BubbleView constructor...");

      // 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]
        mDRotate = r.nextInt(3) + 1;
      } else {
        mDRotate = 0;
      }
    }

    private void setSpeedAndDirection(Random r) {

      // Used by test cases
      switch (speedMode) {

      case SINGLE:
        Log.d(TAG, "Speed Mode = SINGLE");
        mDx = 20;
        mDy = 20;
        break;

      case STILL:
        Log.d(TAG, "Speed Mode = STILL");
        // No speed
        mDx = 0;
        mDy = 0;
        break;

      default:
        Log.d(TAG, "Speed Mode = DEFAULT");
        // TODO - Set mDx and mDy to indicate movement direction and speed 
        // Limit speed in the x and y direction to [-3..3] pixels per movement.
        mDx = r.nextInt(6) + 1;
        mDx -= 3;
        mDy = r.nextInt(6) + 1;
        mDy -= 3;
      }
    }

    private void createScaledBitmap(Random r) {
      
      Log.d(TAG, "In createScaledBitmap(), BITMAP_SIZE = " + BITMAP_SIZE 
          + ", speed mode = " +speedMode);

      if (speedMode != RANDOM) {
        mScaledBitmapWidth = BITMAP_SIZE * 3;
      } else {
        // TODO - set scaled bitmap size in range [1..3] * BITMAP_SIZE
        mScaledBitmapWidth = BITMAP_SIZE * (r.nextInt(3) + 1);
      }
      
      Log.d(TAG, "mScaledBitmapWidth = " + mScaledBitmapWidth);

      // TODO - create the scaled bitmap using size set above
      mScaledBitmap = 
        Bitmap.createScaledBitmap(mBitmap, 
                      mScaledBitmapWidth, 
                      mScaledBitmapWidth, 
                      false);
    }

    // Start moving the BubbleView & updating the display
    private void startMovement() {

      // 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.
          if ( moveWhileOnScreen() ) {
            mFrame.post(new Runnable() {
              @Override
              public void run() {
                BubbleView.this.invalidate();
              }
            });
          } else {
            Log.d(TAG, "Stopping bubble " + BubbleView.this 
                + " - it's off screen");
            stopMovement(false);
          }
        }
      }, 0, REFRESH_RATE, TimeUnit.MILLISECONDS);
    }

    // Returns true if the BubbleView intersects position (x,y)
    private synchronized boolean intersects(float x, float y) {

            boolean insideX = 
              (x >= mXPos) && (x <= (mXPos + mScaledBitmapWidth));
            boolean insideY = 
              (y >= mYPos) && (y <= (mYPos + mScaledBitmapWidth));

            return (insideX && insideY);
    }

    // Cancel the Bubble's movement
    // Remove Bubble from mFrame
    // Play pop sound if the BubbleView was popped

    private void stopMovement(final boolean wasPopped) {
      
      Log.d(TAG, "Stopping bubble " + BubbleView.this);

      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
            mFrame.removeView(BubbleView.this);
            
            // TODO - If the bubble was popped by user,
            // play the popping sound
            if (wasPopped) {
              mSoundPool.play(mSoundID, 
                  mStreamVolume, 
                  mStreamVolume, 
                  0, 
                  0, 
                  1);
            }
          }
        });
      }
    }

    // Change the Bubble's speed and direction
    private synchronized void deflect(float velocityX, float velocityY) {
      mDx = velocityX / REFRESH_RATE;
      mDy = velocityY / REFRESH_RATE;
    }

    // Draw the Bubble at its current location
    @Override
    protected synchronized void onDraw(Canvas canvas) {
      
      // TODO - save the canvas
      canvas.save();
      
      // TODO - increase the rotation of the original image by mDRotate
      mRotate = mRotate + mDRotate;
      
      // TODO Rotate the canvas by current rotation
      // Hint - Rotate around the bubble's center, not its position
      canvas.rotate(mRotate, mXPos + mRadius, mYPos + mRadius);
      
      // TODO - draw the bitmap at it's new location
      canvas.drawBitmap(mScaledBitmap, mXPos, mYPos, mPainter);
      
      // TODO - restore the canvas
      canvas.restore();
    }

    // Returns true if the BubbleView is still on the screen after the move
    // operation
    private synchronized boolean moveWhileOnScreen() {

      // TODO - Move the BubbleView
      mXPos = mXPos + mDx;
      mYPos = mYPos + mDy;
      
      return isOutOfView();
    }

    // Return true if the BubbleView is still on the screen after the move
    // operation
    private boolean isOutOfView() {
      
      // name suggests the opposite functionality?

      // TODO - Return true if the BubbleView is still on the screen after
      // the move operation
      if ( (mXPos + mScaledBitmapWidth) <= mDisplayWidth  
          && (mYPos + mScaledBitmapWidth) <= mDisplayHeight) {
        return true;
      } else {
        return false;
      }
    }
  }

  // Do not modify below here

  @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) {
    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();
  }
}




Java Source Code List

com.michaelfitzmaurice.android.modernart.MainActivity.java
com.michaelfitzmaurice.dailyselfie.AlarmReceiver.java
com.michaelfitzmaurice.dailyselfie.AlarmTimeInterval.java
com.michaelfitzmaurice.dailyselfie.Alarms.java
com.michaelfitzmaurice.dailyselfie.SelfieDetailActivity.java
com.michaelfitzmaurice.dailyselfie.SelfieListActivity.java
com.michaelfitzmaurice.dailyselfie.SelfieListViewAdapter.java
com.michaelfitzmaurice.dailyselfie.SelfieRecord.java
com.michaelfitzmaurice.dailyselfie.settings.ReminderIntervalDialogPreference.java
com.michaelfitzmaurice.dailyselfie.settings.SettingsActivity.java
com.michaelfitzmaurice.dailyselfie.settings.SettingsFragment.java
course.labs.contentproviderlab.MockLocationProvider.java
course.labs.contentproviderlab.PlaceDownloaderTask.java
course.labs.contentproviderlab.PlaceRecord.java
course.labs.contentproviderlab.PlaceViewActivity.java
course.labs.contentproviderlab.PlaceViewAdapter.java
course.labs.contentproviderlab.provider.PlaceBadgeContentProvider.java
course.labs.contentproviderlab.provider.PlaceBadgesContract.java
course.labs.contentproviderlab.provider.PlaceBadgesContract.java
course.labs.graphicslab.BubbleActivity.java
course.labs.locationlab.MockLocationProvider.java
course.labs.locationlab.PlaceDownloaderTask.java
course.labs.locationlab.PlaceRecord.java
course.labs.locationlab.PlaceViewActivity.java
course.labs.locationlab.PlaceViewAdapter.java
course.labs.notificationslab.DownloaderTask.java
course.labs.notificationslab.FeedFragment.java
course.labs.notificationslab.FriendsFragment.java
course.labs.notificationslab.MainActivity.java
course.labs.notificationslab.SelectionListener.java
course.labs.notificationslab.TestFrontEndActivity.java