diff --git a/Lab9_GesturesLab/.gitignore b/Lab9_GesturesLab/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2a0b276c7c05d081dcf94eaaef153cefb742f9e7 --- /dev/null +++ b/Lab9_GesturesLab/.gitignore @@ -0,0 +1,38 @@ +# built application files +*.apk +*.ap_ + +# files for the dex VM +*.dex + +# Java class files +*.class + +# Mac files +.DS_Store + +# generated files +bin/ +gen/ + +# Ignore gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Eclipse prefs files +.prefs + +# Android Studio +*.iml +.idea +.gradle +build/ + +#NDK +obj/ diff --git a/Lab9_GesturesLab/Lab-Gestures.pdf b/Lab9_GesturesLab/Lab-Gestures.pdf new file mode 100644 index 0000000000000000000000000000000000000000..487bf4d354f83615942f502c01c23d4713706445 Binary files /dev/null and b/Lab9_GesturesLab/Lab-Gestures.pdf differ diff --git a/Lab9_GesturesLab/app/build.gradle b/Lab9_GesturesLab/app/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..325623cd9e3b995290c27f45eece8d0612bc3399 --- /dev/null +++ b/Lab9_GesturesLab/app/build.gradle @@ -0,0 +1,26 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 18 + buildToolsVersion "24.0.0" + + defaultConfig { + applicationId "course.labs.gestureslab" + minSdkVersion 11 + targetSdkVersion 18 + + testApplicationId "course.labs.gestureslab.test" + testInstrumentationRunner "android.test.InstrumentationTestRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} + +dependencies { + androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.2.1' +} diff --git a/Lab9_GesturesLab/app/libs/robotium-solo-5.2.1.jar b/Lab9_GesturesLab/app/libs/robotium-solo-5.2.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..2875fc4de9de5172a9d0f9f6faa7da1a11fcce79 Binary files /dev/null and b/Lab9_GesturesLab/app/libs/robotium-solo-5.2.1.jar differ diff --git a/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFling.java b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFling.java new file mode 100644 index 0000000000000000000000000000000000000000..3bdee92b65ed4c510556310f147378900d712dd9 --- /dev/null +++ b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFling.java @@ -0,0 +1,72 @@ +package course.labs.gestureslab.test; + +import course.labs.gestureslab.BubbleActivity; + +import com.robotium.solo.*; +import android.test.ActivityInstrumentationTestCase2; +import android.view.WindowManager; + +public class BubbleActivityFling extends + ActivityInstrumentationTestCase2<BubbleActivity> { + private Solo solo; + + public BubbleActivityFling() { + super(BubbleActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().getWindow().addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + }); + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + + public void testRun() { + + int delay = 2000; + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo.waitForActivity(course.labs.gestureslab.BubbleActivity.class, + delay); + + solo.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_still_mode); + + solo.sleep(delay); + + // Click to create a bubble + solo.clickOnScreen(100, 100); + + solo.sleep(delay); + + // Assert that a bubble was displayed + assertEquals( + "Bubble hasn't appeared", + 1, + solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size()); + + // Fling the bubble + solo.drag(100, 500, 100, 500, 3); + + // Give bubble time to leave screen + solo.sleep(delay); + + // Assert that the bubble has left the screen + assertEquals( + "Bubble hasn't left the screen", + 0, + solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size()); + } +} diff --git a/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFloatOffScreen.java b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFloatOffScreen.java new file mode 100644 index 0000000000000000000000000000000000000000..0dba5db8067df92e378fe959e6d97a58e29ff2b2 --- /dev/null +++ b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFloatOffScreen.java @@ -0,0 +1,73 @@ +package course.labs.gestureslab.test; + +import course.labs.gestureslab.BubbleActivity; + +import com.robotium.solo.*; +import android.test.ActivityInstrumentationTestCase2; +import android.view.WindowManager; + +public class BubbleActivityFloatOffScreen extends + ActivityInstrumentationTestCase2<BubbleActivity> { + private Solo solo; + + public BubbleActivityFloatOffScreen() { + super(BubbleActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().getWindow().addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + }); + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + + public void testRun() { + + int shortDelay = 250, delay = 2000; + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo.waitForActivity(course.labs.gestureslab.BubbleActivity.class, + delay); + + // Click on action bar item + solo.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_single_speed); + + solo.sleep(delay); + + // Click to create a bubble + solo.clickOnScreen(250.0f, 250.0f); + + // Check whether bubble appears + boolean bubbleAppeared = solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class).size() > 0; + for (int i = 0; i < 8 && !bubbleAppeared; i++) { + solo.sleep(shortDelay); + bubbleAppeared = solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size() > 0; + } + + // Assert that a bubble was displayed + assertTrue("Bubble hasn't appeared", bubbleAppeared); + + solo.sleep(delay); + + // Assert that the bubble has left the screen + assertEquals( + "Bubble hasn't left the screen", + 0, + solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size()); + + } +} diff --git a/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMenu.java b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMenu.java new file mode 100644 index 0000000000000000000000000000000000000000..ce3cea6a2ae305a79b612efe8d6ba01809b0a39c --- /dev/null +++ b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMenu.java @@ -0,0 +1,55 @@ +package course.labs.gestureslab.test; + +import android.test.ActivityInstrumentationTestCase2; +import android.view.WindowManager; + +import com.robotium.solo.Solo; + +import course.labs.gestureslab.BubbleActivity; + + +public class BubbleActivityMenu extends ActivityInstrumentationTestCase2<BubbleActivity> { + private Solo solo; + + public BubbleActivityMenu() { + super(BubbleActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().getWindow().addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + }); + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + + public void testRun() { + + int delay = 2000; + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo.waitForActivity(course.labs.gestureslab.BubbleActivity.class, 2000); + + solo.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_still_mode); + + solo.sleep(delay); + + //Gesture starting top left to open menu + solo.drag(0,300,300,350, 10); + + solo.sleep(delay); + + //checking if menu opened by clicking on a menu item + //without opening the menu. + assertTrue("Menu did not appear", solo.waitForText("Random Speed Mode")); + + } +} diff --git a/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMultiple.java b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMultiple.java new file mode 100644 index 0000000000000000000000000000000000000000..3fda8160598625bb66858258d04642fd68787265 --- /dev/null +++ b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMultiple.java @@ -0,0 +1,84 @@ +package course.labs.gestureslab.test; + +import course.labs.gestureslab.BubbleActivity; + +import com.robotium.solo.*; +import android.test.ActivityInstrumentationTestCase2; +import android.view.WindowManager; + +public class BubbleActivityMultiple extends + ActivityInstrumentationTestCase2<BubbleActivity> { + private Solo solo; + + public BubbleActivityMultiple() { + super(BubbleActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().getWindow().addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + }); + + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + + public void testRun() { + + int delay = 2000; + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo.waitForActivity(course.labs.gestureslab.BubbleActivity.class, + delay); + + // Set Still Mode + solo.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_still_mode); + + solo.sleep(delay); + + // Click to create a bubble + solo.clickOnScreen(100, 100); + + solo.sleep(delay); + + // Assert that a bubble was displayed + assertEquals( + "Bubble hasn't appeared", + 1, + solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size()); + + // Click to create second bubble + solo.clickOnScreen(300, 300); + + solo.sleep(delay); + + // Assert that a bubble was displayed + assertEquals( + "Second bubble hasn't appeared", + 2, + solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size()); + + solo.sleep(delay); + + // Give misbehaving bubbles a chance to move off screen + // Assert that there are two bubbles on the screen + assertEquals( + "There should be two bubbles on the screen", + 2, + solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size()); + } +} diff --git a/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityPop.java b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityPop.java new file mode 100644 index 0000000000000000000000000000000000000000..8db621eeab8ff7cc7285b7010d61a417a0868be1 --- /dev/null +++ b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityPop.java @@ -0,0 +1,73 @@ +package course.labs.gestureslab.test; + +import course.labs.gestureslab.BubbleActivity; + +import com.robotium.solo.*; +import android.test.ActivityInstrumentationTestCase2; +import android.view.WindowManager; + +public class BubbleActivityPop extends + ActivityInstrumentationTestCase2<BubbleActivity> { + private Solo solo; + + public BubbleActivityPop() { + super(BubbleActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().getWindow().addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + }); + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + + public void testRun() { + + int delay = 2000; + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo.waitForActivity(course.labs.gestureslab.BubbleActivity.class, + delay); + + // Set Still Mode + solo.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_still_mode); + + solo.sleep(delay); + + // Click to create a bubble + solo.clickOnScreen(250, 250); + + solo.sleep(delay); + + // Assert that a bubble was displayed + assertEquals( + "Bubble hasn't appeared", + 1, + solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size()); + + // Click to remove the same bubble + solo.clickOnScreen(250, 250); + + solo.sleep(delay); + + // Assert that there are no more bubbles + assertEquals( + "The bubble was not popped", + 0, + solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size()); + + } +} diff --git a/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityTen.java b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityTen.java new file mode 100644 index 0000000000000000000000000000000000000000..3b732fe175de50e88e13c55bf701dc890214195f --- /dev/null +++ b/Lab9_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityTen.java @@ -0,0 +1,61 @@ +package course.labs.gestureslab.test; + +import android.test.ActivityInstrumentationTestCase2; +import android.view.WindowManager; + +import com.robotium.solo.Solo; + +import course.labs.gestureslab.BubbleActivity; + +public class BubbleActivityTen extends + ActivityInstrumentationTestCase2<BubbleActivity> { + private Solo solo; + + public BubbleActivityTen() { + super(BubbleActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().getWindow().addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + }); + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + + public void testRun() { + + int delay = 2000; + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo.waitForActivity(course.labs.gestureslab.BubbleActivity.class, 2000); + + // Set Still Mode + solo.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_still_mode); + + solo.sleep(delay); + + //Gesture starting in top right to add ten bubbles + solo.drag(300,0,200,300,10); + + solo.sleep(delay); + + // Give misbehaving bubbles a chance to move off screen + // Assert that there are two bubbles on the screen + assertEquals( + "There should be ten bubbles on the screen", + 10, + solo.getCurrentViews( + course.labs.gestureslab.BubbleActivity.BubbleView.class) + .size()); + + } +} diff --git a/Lab9_GesturesLab/app/src/androidTest/res/.gitignore b/Lab9_GesturesLab/app/src/androidTest/res/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Lab9_GesturesLab/app/src/main/AndroidManifest.xml b/Lab9_GesturesLab/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..1a535db1c1379de19b28313d8c22a59a5e8da5f2 --- /dev/null +++ b/Lab9_GesturesLab/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="course.labs.gestureslab" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="11" + android:targetSdkVersion="18" /> + + <application + android:allowBackup="false" + android:icon="@drawable/icon" + android:label="@string/app_name" > + + <activity + android:name=".BubbleActivity" + android:label="@string/app_name" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> \ No newline at end of file diff --git a/Lab9_GesturesLab/app/src/main/java/course/labs/gestureslab/BubbleActivity.java b/Lab9_GesturesLab/app/src/main/java/course/labs/gestureslab/BubbleActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..1ed611712c95a7a580d1c29abdfced45f7d965fd --- /dev/null +++ b/Lab9_GesturesLab/app/src/main/java/course/labs/gestureslab/BubbleActivity.java @@ -0,0 +1,551 @@ +package course.labs.gestureslab; + +import java.util.ArrayList; +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.gesture.Gesture; +import android.gesture.GestureLibraries; +import android.gesture.GestureLibrary; +import android.gesture.GestureOverlayView; +import android.gesture.GestureOverlayView.OnGesturePerformedListener; +import android.gesture.Prediction; +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.view.View.OnTouchListener; +import android.widget.FrameLayout; +import android.widget.Toast; + +public class BubbleActivity extends Activity implements + OnGesturePerformedListener { + + private static final double MIN_PRED_SCORE = 3.0; + // 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; + public static int speedMode = RANDOM; + + private static final String TAG = "Lab-Gestures"; + + // The Main view + private FrameLayout 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; + + // Gesture Library + private GestureLibrary mLibrary; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.main); + + // Set up user interface + mFrame = (FrameLayout) findViewById(R.id.frame); + + // Load basic bubble Bitmap + mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b64); + + // TODO - Fetch GestureLibrary from raw + + + GestureOverlayView gestureOverlay = (GestureOverlayView) findViewById(R.id.gestures_overlay); + + // TODO - Make this the target of gesture detection callbacks + + + // TODO - implement OnTouchListener to pass all events received by the + // gestureOverlay to the basic gesture detector + + gestureOverlay.setOnTouchListener(new OnTouchListener() { + + @Override + public boolean onTouch(View v, MotionEvent event) { + return true || false; + } + }); + + + + + // Uncomment next line to turn off gesture highlights + // gestureOverlay.setUncertainGestureColor(Color.TRANSPARENT); + + // Loads the gesture library + if (!mLibrary.load()) { + finish(); + } + + } + + @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); + + // 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) { + + } + }); + + // TODO - Load the sound from res/raw/bubble_pop.wav + mSoundID = 0; + + } + + @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() { + + 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 + + + + + + + + return true || false; + } + + // 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 + + + + + + + + + + + + + + + + + + + + return true || false; + } + + // Good practice to override this method because all + // gestures start with a ACTION_DOWN event + @Override + public boolean onDown(MotionEvent event) { + return true; + } + }); + } + + @Override + protected void onPause() { + + // Release all SoundPool resources + + mSoundPool.unload(mSoundID); + mSoundPool.release(); + mSoundPool = null; + + super.onPause(); + } + + public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) { + + // TODO - Get gesture predictions + ArrayList<Prediction> predictions = null; + + if (predictions.size() > 0) { + + // Get highest-ranked prediction + Prediction prediction = predictions.get(0); + + + // TODO - Ignore predictions with a score of < MIN_PRED_SCORE and display a + // toast message informing the user that no prediction was made. If + // the prediction + // matches the openMenu gesture, open the menu. If the prediction + // matches + // the addTen gesture, add 10 bubbles to the screen. + + } else { + + + + + + + + + + + + + + + + + + + } + } + // 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) { + + // Set rotation in range [1..3] + mDRotate = ((r.nextInt(3 * BITMAP_SIZE) + 1) / mScaledBitmapWidth); + } 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: + + // Set movement direction and speed + // Limit movement speed in the x and y + // direction to [-3..3] pixels per movement. + + mDx = (r.nextInt(mScaledBitmapWidth * 3) + 1) + / (float) mScaledBitmapWidth; + mDx *= r.nextInt() % 2 == 0 ? 1 : -1; + + mDy = (r.nextInt(mScaledBitmapWidth * 3) + 1) + / (float) mScaledBitmapWidth; + mDy *= r.nextInt() % 2 == 0 ? 1 : -1; + + } + } + + private void createScaledBitmap(Random r) { + + if (speedMode != RANDOM) { + mScaledBitmapWidth = BITMAP_SIZE * 3; + } else { + + // Set scaled bitmap size in range [1..3] * BITMAP_SIZE + mScaledBitmapWidth = r.nextInt(2 * BITMAP_SIZE) + BITMAP_SIZE; + + } + + // Create the scaled bitmap using size set above + mScaledBitmap = Bitmap.createScaledBitmap(mBitmap, + mScaledBitmapWidth, mScaledBitmapWidth, false); + } + + // 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() { + + // 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()) { + postInvalidate(); + } else + stop(false); + } + }, 0, REFRESH_RATE, TimeUnit.MILLISECONDS); + } + + // Returns true if the BubbleView intersects position (x,y) + private synchronized boolean intersects(float x, float y) { + + // Return true if the BubbleView intersects position (x,y) + + float xDist = x - (mXPos + mRadius); + float yDist = y - (mYPos + mRadius); + + return xDist * xDist + yDist * yDist <= mRadiusSquared; + + } + + // 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() { + + // Remove the BubbleView from mFrame + mFrame.removeView(BubbleView.this); + + // If the bubble was popped by user, + // play the popping sound + if (wasPopped) { + mSoundPool.play(mSoundID, mStreamVolume, + mStreamVolume, 1, 0, 1.0f); + } + } + }); + } + } + + // 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) { + + // save the canvas + canvas.save(); + + // Increase the rotation of the original image by mDRotate + mRotate += mDRotate; + + // Rotate the canvas by current rotation + // Hint - Rotate around the bubble's center, not its position + + canvas.rotate(mRotate, mXPos + mScaledBitmapWidth / 2, mYPos + + mScaledBitmapWidth / 2); + + // Draw the bitmap at it's new location + canvas.drawBitmap(mScaledBitmap, mXPos, mYPos, mPainter); + + // Restore the canvas + canvas.restore(); + + } + + // Returns true if the BubbleView is still on the screen after the move + // operation + private synchronized boolean moveWhileOnScreen() { + + // Move the BubbleView + + mXPos = mXPos += mDx; + mYPos = mYPos += mDy; + + return !isOutOfView(); + + } + + // Return true if the BubbleView is not on the screen after the move + // operation + private boolean isOutOfView() { + + return (mXPos < 0 - mScaledBitmapWidth || mXPos > mDisplayWidth + || mYPos < 0 - mScaledBitmapWidth || mYPos > mDisplayHeight); + } + } + + @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(); + } +} diff --git a/Lab9_GesturesLab/app/src/main/res/drawable-hdpi/b128.png b/Lab9_GesturesLab/app/src/main/res/drawable-hdpi/b128.png new file mode 100644 index 0000000000000000000000000000000000000000..7845514160501a2c864bf1386154b2280e1332cd Binary files /dev/null and b/Lab9_GesturesLab/app/src/main/res/drawable-hdpi/b128.png differ diff --git a/Lab9_GesturesLab/app/src/main/res/drawable-hdpi/b64.png b/Lab9_GesturesLab/app/src/main/res/drawable-hdpi/b64.png new file mode 100644 index 0000000000000000000000000000000000000000..7101a2580a251dde3fce5fbd6867a3fc5cf48222 Binary files /dev/null and b/Lab9_GesturesLab/app/src/main/res/drawable-hdpi/b64.png differ diff --git a/Lab9_GesturesLab/app/src/main/res/drawable-hdpi/icon.png b/Lab9_GesturesLab/app/src/main/res/drawable-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8074c4c571b8cd19e27f4ee5545df367420686d7 Binary files /dev/null and b/Lab9_GesturesLab/app/src/main/res/drawable-hdpi/icon.png differ diff --git a/Lab9_GesturesLab/app/src/main/res/drawable-ldpi/icon.png b/Lab9_GesturesLab/app/src/main/res/drawable-ldpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1095584ec21f71cd0afc9e0993aa2209671b590c Binary files /dev/null and b/Lab9_GesturesLab/app/src/main/res/drawable-ldpi/icon.png differ diff --git a/Lab9_GesturesLab/app/src/main/res/drawable-mdpi/icon.png b/Lab9_GesturesLab/app/src/main/res/drawable-mdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a07c69fa5a0f4da5d5efe96eea12a543154dbab6 Binary files /dev/null and b/Lab9_GesturesLab/app/src/main/res/drawable-mdpi/icon.png differ diff --git a/Lab9_GesturesLab/app/src/main/res/layout/main.xml b/Lab9_GesturesLab/app/src/main/res/layout/main.xml new file mode 100644 index 0000000000000000000000000000000000000000..2c926be878136e83b0ca7b928e530437713dca76 --- /dev/null +++ b/Lab9_GesturesLab/app/src/main/res/layout/main.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.gesture.GestureOverlayView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/gestures_overlay" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gestureStrokeLengthThreshold="200.0" > + + <FrameLayout + android:id="@+id/frame" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_centerInParent="true" > + </FrameLayout> + +</android.gesture.GestureOverlayView> \ No newline at end of file diff --git a/Lab9_GesturesLab/app/src/main/res/menu/menu.xml b/Lab9_GesturesLab/app/src/main/res/menu/menu.xml new file mode 100644 index 0000000000000000000000000000000000000000..7fb14c6e1976237516b143078b37a4d4b232d285 --- /dev/null +++ b/Lab9_GesturesLab/app/src/main/res/menu/menu.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" > + <item android:id="@+id/menu_still_mode" android:title="@string/still_mode_string"></item> + <item android:id="@+id/menu_single_speed" android:title="@string/single_speed_mode_string"></item> + <item android:id="@+id/menu_random_mode" android:title="@string/random_speed_mode_string"></item> + <item android:id="@+id/quit" android:title="@string/quit_string"></item> +</menu> diff --git a/Lab9_GesturesLab/app/src/main/res/raw/bubble_pop.wav b/Lab9_GesturesLab/app/src/main/res/raw/bubble_pop.wav new file mode 100644 index 0000000000000000000000000000000000000000..50886e247e01f246e57f388ee4b15f66c47979fb Binary files /dev/null and b/Lab9_GesturesLab/app/src/main/res/raw/bubble_pop.wav differ diff --git a/Lab9_GesturesLab/app/src/main/res/raw/gestures b/Lab9_GesturesLab/app/src/main/res/raw/gestures new file mode 100644 index 0000000000000000000000000000000000000000..5e4dc1d601fc788a28c3a3be52b45cec590e4ce0 Binary files /dev/null and b/Lab9_GesturesLab/app/src/main/res/raw/gestures differ diff --git a/Lab9_GesturesLab/app/src/main/res/values/strings.xml b/Lab9_GesturesLab/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..222867aa9430d87fbf27136ea46be5d04dc0aeb5 --- /dev/null +++ b/Lab9_GesturesLab/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">GesturesLab</string> + <string name="still_mode_string">Still Mode</string> + <string name="single_speed_mode_string">Single Speed Mode</string> + <string name="random_speed_mode_string">Random Speed Mode</string> + <string name="quit_string">Quit</string> +</resources> diff --git a/Lab9_GesturesLab/build.gradle b/Lab9_GesturesLab/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..e7e3c60b0a640e00a453031c7b64eb873a0e068a --- /dev/null +++ b/Lab9_GesturesLab/build.gradle @@ -0,0 +1,15 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.2.2' + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/Lab9_GesturesLab/gradle/wrapper/gradle-wrapper.jar b/Lab9_GesturesLab/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..13372aef5e24af05341d49695ee84e5f9b594659 Binary files /dev/null and b/Lab9_GesturesLab/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Lab9_GesturesLab/gradle/wrapper/gradle-wrapper.properties b/Lab9_GesturesLab/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..04e285f34080d98841a9fac832466aec720aecec --- /dev/null +++ b/Lab9_GesturesLab/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Dec 28 10:00:20 PST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/Lab9_GesturesLab/gradlew b/Lab9_GesturesLab/gradlew new file mode 100755 index 0000000000000000000000000000000000000000..9d82f78915133e1c35a6ea51252590fb38efac2f --- /dev/null +++ b/Lab9_GesturesLab/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/Lab9_GesturesLab/gradlew.bat b/Lab9_GesturesLab/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..8a0b282aa6885fb573c106b3551f7275c5f17e8e --- /dev/null +++ b/Lab9_GesturesLab/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Lab9_GesturesLab/settings.gradle b/Lab9_GesturesLab/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..e7b4def49cb53d9aa04228dd3edb14c9e635e003 --- /dev/null +++ b/Lab9_GesturesLab/settings.gradle @@ -0,0 +1 @@ +include ':app'