diff --git a/.DS_Store b/.DS_Store index 63a7fcaaf4c69a0d94f1c3101153d2023c70e805..1aa5424192db2038c006ccecd59f193b87f4c0e2 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/Labs/.DS_Store b/Labs/.DS_Store index 3a8ff575c8ccdd91ca49472e2dd69641d5414d61..3e27c1c60597b7ecd63b5930033b9d96f71eee08 100644 Binary files a/Labs/.DS_Store and b/Labs/.DS_Store differ diff --git a/Labs/Lab8_GesturesLab/.gitignore b/Labs/Lab8_GesturesLab/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..570cb522403c467c48a5a733970e12dcea986a3c --- /dev/null +++ b/Labs/Lab8_GesturesLab/.gitignore @@ -0,0 +1,37 @@ +# 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 + +#NDK +obj/ diff --git a/Labs/Lab8_GesturesLab/Lab-Gestures.pdf b/Labs/Lab8_GesturesLab/Lab-Gestures.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5c4a5920b2502d0434d3e764e16beabc9998843f Binary files /dev/null and b/Labs/Lab8_GesturesLab/Lab-Gestures.pdf differ diff --git a/Labs/Lab8_GesturesLab/app/build.gradle b/Labs/Lab8_GesturesLab/app/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..bd5ed3cf880d0165c6dad3c5abe8783a0fd8c081 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +android { + //noinspection GradleCompatible,GradleCompatible + compileSdkVersion 26 + buildToolsVersion '29.0.3' + + defaultConfig { + applicationId "course.labs.gestureslab" + minSdkVersion 21 + targetSdkVersion 30 + + testApplicationId "course.labs.gestureslab.tests" + testInstrumentationRunner "android.test.InstrumentationTestRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} + +dependencies { + androidTestImplementation 'com.jayway.android.robotium:robotium-solo:5.6.3' + //noinspection GradleDependency + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} +repositories { + mavenCentral() +} diff --git a/Labs/Lab8_GesturesLab/app/libs/robotium-solo-5.2.1.jar b/Labs/Lab8_GesturesLab/app/libs/robotium-solo-5.2.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..2875fc4de9de5172a9d0f9f6faa7da1a11fcce79 Binary files /dev/null and b/Labs/Lab8_GesturesLab/app/libs/robotium-solo-5.2.1.jar differ diff --git a/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFling.kt b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFling.kt new file mode 100644 index 0000000000000000000000000000000000000000..6a7d348147d3495df3ee18f6b0df047641775570 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFling.kt @@ -0,0 +1,66 @@ +package course.labs.gestureslab.test + +import course.labs.gestureslab.BubbleActivity + +import com.robotium.solo.* +import android.test.ActivityInstrumentationTestCase2 +import android.view.WindowManager +import junit.framework.Assert + +class BubbleActivityFling : ActivityInstrumentationTestCase2<BubbleActivity>(BubbleActivity::class.java) { + private var solo: Solo? = null + + @Throws(Exception::class) + public override fun setUp() { + solo = Solo(instrumentation, activity) + instrumentation.runOnMainSync { + activity.window.addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) + } + } + + @Throws(Exception::class) + public override fun tearDown() { + solo!!.finishOpenedActivities() + } + + fun testRun() { + + val delay = 2000 + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo!!.waitForActivity(BubbleActivity::class.java, + delay) + + solo!!.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_still_mode) + + solo!!.sleep(delay) + + // Click to create a bubble + solo!!.clickOnScreen(120f, 120f) + + solo!!.sleep(delay) + + // Assert that a bubble was displayed + Assert.assertEquals( + "Bubble hasn't appeared", + 1, + solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size) + + // Fling the bubble + solo!!.drag(100f, 1000f, 100f, 1000f, 3) + + // Give bubble time to leave screen + solo!!.sleep(delay) + + // Assert that the bubble has left the screen + Assert.assertEquals( + "Bubble hasn't left the screen", + 0, + solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size) + } +} diff --git a/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFloatOffScreen.kt b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFloatOffScreen.kt new file mode 100644 index 0000000000000000000000000000000000000000..0cbea28119e22a4241fdde04b5a41511b0342547 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityFloatOffScreen.kt @@ -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 +import junit.framework.Assert + +class BubbleActivityFloatOffScreen : ActivityInstrumentationTestCase2<BubbleActivity>(BubbleActivity::class.java) { + private var solo: Solo? = null + + @Throws(Exception::class) + public override fun setUp() { + solo = Solo(instrumentation, activity) + instrumentation.runOnMainSync { + activity.window.addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) + } + } + + @Throws(Exception::class) + public override fun tearDown() { + solo!!.finishOpenedActivities() + } + + fun testRun() { + + val shortDelay = 300 + val delay = 2000 + val delay2 = 3000 + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo!!.waitForActivity(BubbleActivity::class.java, + 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(350.0f, 350.0f) + + // Check whether bubble appears + var bubbleAppeared = solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java).size > 0 + var i = 0 + while (i < 10 && !bubbleAppeared) { + solo!!.sleep(shortDelay) + bubbleAppeared = solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size > 0 + i++ + } + + // Assert that a bubble was displayed + Assert.assertTrue("Bubble hasn't appeared", bubbleAppeared) + + // The bubble moves a bit too slowly, so the test fails + solo!!.sleep(delay2) + + // Assert that the bubble has left the screen + Assert.assertEquals( + "Bubble hasn't left the screen", + 0, + solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size) + + } +} diff --git a/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMenu.kt b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMenu.kt new file mode 100644 index 0000000000000000000000000000000000000000..f392544e41630b3529be4b435ef172aa226ca1a6 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMenu.kt @@ -0,0 +1,50 @@ +package course.labs.gestureslab.test + +import android.test.ActivityInstrumentationTestCase2 +import android.view.WindowManager + +import com.robotium.solo.Solo + +import course.labs.gestureslab.BubbleActivity +import junit.framework.Assert + + +class BubbleActivityMenu : ActivityInstrumentationTestCase2<BubbleActivity>(BubbleActivity::class.java) { + private var solo: Solo? = null + + @Throws(Exception::class) + public override fun setUp() { + solo = Solo(instrumentation, activity) + instrumentation.runOnMainSync { + activity.window.addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) + } + } + + @Throws(Exception::class) + public override fun tearDown() { + solo!!.finishOpenedActivities() + } + + fun testRun() { + + val delay = 2000 + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo!!.waitForActivity(BubbleActivity::class.java, 2000) + + solo!!.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_still_mode) + + solo!!.sleep(delay) + + //Gesture starting top left to open menu + solo!!.drag(0f, 300f, 300f, 350f, 10) + + solo!!.sleep(delay) + + //checking if menu opened by clicking on a menu item + //without opening the menu. + Assert.assertTrue("Menu did not appear", solo!!.waitForText("Random Speed Mode")) + + } +} diff --git a/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMultiple.kt b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMultiple.kt new file mode 100644 index 0000000000000000000000000000000000000000..0733b2978014709439aa2863d7c6c27757b60714 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityMultiple.kt @@ -0,0 +1,78 @@ +package course.labs.gestureslab.test + +import course.labs.gestureslab.BubbleActivity + +import com.robotium.solo.* +import android.test.ActivityInstrumentationTestCase2 +import android.view.WindowManager +import junit.framework.Assert + +class BubbleActivityMultiple : ActivityInstrumentationTestCase2<BubbleActivity>(BubbleActivity::class.java) { + private var solo: Solo? = null + + @Throws(Exception::class) + public override fun setUp() { + solo = Solo(instrumentation, activity) + instrumentation.runOnMainSync { + activity.window.addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) + } + + } + + @Throws(Exception::class) + public override fun tearDown() { + solo!!.finishOpenedActivities() + } + + fun testRun() { + + val delay = 2000 + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo!!.waitForActivity(BubbleActivity::class.java, + delay) + + // Set Still Mode + solo!!.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_still_mode) + + solo!!.sleep(delay) + + // Click to create a bubble + solo!!.clickOnScreen(100f, 100f) + + solo!!.sleep(delay) + + // Assert that a bubble was displayed + Assert.assertEquals( + "Bubble hasn't appeared", + 1, + solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size) + + // Click to create second bubble + solo!!.clickOnScreen(300f, 300f) + + solo!!.sleep(delay) + + // Assert that a bubble was displayed + Assert.assertEquals( + "Second bubble hasn't appeared", + 2, + solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size) + + solo!!.sleep(delay) + + // Give misbehaving bubbles a chance to move off screen + // Assert that there are two bubbles on the screen + Assert.assertEquals( + "There should be two bubbles on the screen", + 2, + solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size) + } +} diff --git a/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityPop.kt b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityPop.kt new file mode 100644 index 0000000000000000000000000000000000000000..6ab3d972c5d2d3a3ecc4fb07bbcc4133308b9511 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityPop.kt @@ -0,0 +1,67 @@ +package course.labs.gestureslab.test + +import course.labs.gestureslab.BubbleActivity + +import com.robotium.solo.* +import android.test.ActivityInstrumentationTestCase2 +import android.view.WindowManager +import junit.framework.Assert + +class BubbleActivityPop : ActivityInstrumentationTestCase2<BubbleActivity>(BubbleActivity::class.java) { + private var solo: Solo? = null + + @Throws(Exception::class) + public override fun setUp() { + solo = Solo(instrumentation, activity) + instrumentation.runOnMainSync { + activity.window.addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) + } + } + + @Throws(Exception::class) + public override fun tearDown() { + solo!!.finishOpenedActivities() + } + + fun testRun() { + + val delay = 2000 + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo!!.waitForActivity(BubbleActivity::class.java, + delay) + + // Set Still Mode + solo!!.clickOnActionBarItem(course.labs.gestureslab.R.id.menu_still_mode) + + solo!!.sleep(delay) + + // Click to create a bubble + solo!!.clickOnScreen(250f, 250f) + + solo!!.sleep(delay) + + // Assert that a bubble was displayed + Assert.assertEquals( + "Bubble hasn't appeared", + 1, + solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size) + + // Click to remove the same bubble + solo!!.clickOnScreen(250f, 250f) + + solo!!.sleep(delay) + + // Assert that there are no more bubbles + Assert.assertEquals( + "The bubble was not popped", + 0, + solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size) + + } +} diff --git a/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityTen.kt b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityTen.kt new file mode 100644 index 0000000000000000000000000000000000000000..97030021ca98c027338798829d5c8166fc3e95e4 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/androidTest/java/course/labs/gestureslab/test/BubbleActivityTen.kt @@ -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 +import junit.framework.Assert + +class BubbleActivityTen : ActivityInstrumentationTestCase2<BubbleActivity>(BubbleActivity::class.java) { + private var solo: Solo? = null + + @Throws(Exception::class) + public override fun setUp() { + solo = Solo(instrumentation, activity) + instrumentation.runOnMainSync { + activity.window.addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) + } + } + + @Throws(Exception::class) + public override fun tearDown() { + solo!!.finishOpenedActivities() + } + + fun testRun() { + + val delay = 2000 + + // Wait for activity: 'course.labs.TouchLab.BubbleActivity' + solo!!.waitForActivity(BubbleActivity::class.java, 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(300f, 0f, 200f, 300f, 10) + + solo!!.sleep(delay) + + // Give misbehaving bubbles a chance to move off screen + // Assert that there are two bubbles on the screen + Assert.assertEquals( + "There should be ten bubbles on the screen", + 10, + solo!!.getCurrentViews( + BubbleActivity.BubbleView::class.java) + .size) + + } +} diff --git a/Labs/Lab8_GesturesLab/app/src/androidTest/res/.gitignore b/Labs/Lab8_GesturesLab/app/src/androidTest/res/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Labs/Lab8_GesturesLab/app/src/main/AndroidManifest.xml b/Labs/Lab8_GesturesLab/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..eeccb769ecca05741b13f2f80ecefd5bad934e21 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?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" > + + <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/Labs/Lab8_GesturesLab/app/src/main/java/course/labs/gestureslab/BubbleActivity.kt b/Labs/Lab8_GesturesLab/app/src/main/java/course/labs/gestureslab/BubbleActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..f4a4def49d18d1c056f09e8d06ae216f29727d30 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/main/java/course/labs/gestureslab/BubbleActivity.kt @@ -0,0 +1,511 @@ +package course.labs.gestureslab + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Context +import android.gesture.Gesture +import android.gesture.GestureLibraries +import android.gesture.GestureLibrary +import android.gesture.Prediction +import android.gesture.GestureOverlayView +import android.gesture.GestureOverlayView.OnGesturePerformedListener +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Paint +import android.media.AudioAttributes +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.util.Log +import android.view.* +import android.widget.FrameLayout +import android.widget.Toast +import java.util.* +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit + + +class BubbleActivity : Activity(), OnGesturePerformedListener { + + // The Main view + private var mFrame: FrameLayout? = null + + // Bubble image's bitmap + private var mBitmap: Bitmap? = null + + // Display dimensions + private var mDisplayWidth: Int = 0 + private var mDisplayHeight: Int = 0 + + // Sound variables + + // AudioManager + private var mAudioManager: AudioManager? = null + // SoundPool + private var mSoundPool: SoundPool? = null + // ID for the bubble popping sound + private var mSoundID: Int = 0 + // Audio volume + private var mStreamVolume: Float = 0.toFloat() + + // Gesture Detector + private var mGestureDetector: GestureDetector? = null + + // Gesture Library + private var mLibrary: GestureLibrary? = null + + @SuppressLint("ClickableViewAccessibility") + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.main) + + // Set up user interface + mFrame = findViewById<View>(R.id.frame) as FrameLayout + + // Load basic bubble Bitmap + mBitmap = BitmapFactory.decodeResource(resources, R.drawable.b64) + + // TODO - Fetch GestureLibrary from raw + + + val gestureOverlay = findViewById<View>(R.id.gestures_overlay) as GestureOverlayView + + // 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 { v, event -> + true || false + } + + // Uncomment to remove gesture highlights + // gestureOverlay.setUncertainGestureColor(Color.TRANSPARENT); + + mLibrary?.apply { + if (!load()) { + Log.i(TAG, "Could not load Gesture Library") + } + } + } + + override fun onResume() { + super.onResume() + + // Manage bubble popping sound + // Use AudioManager.STREAM_MUSIC as stream type + + mAudioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager + + mStreamVolume = mAudioManager!! + .getStreamVolume(AudioManager.STREAM_MUSIC).toFloat() / mAudioManager!!.getStreamMaxVolume( + AudioManager.STREAM_MUSIC + ) + + val musicAttribute = AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .build() + mSoundPool = SoundPool.Builder() + .setMaxStreams(10) + .setAudioAttributes(musicAttribute) + .build() + + mSoundID = mSoundPool!!.load(this, R.raw.bubble_pop, 1) + // setupGestureDetector() + mSoundPool!!.setOnLoadCompleteListener { _, _, _ -> setupGestureDetector() } + + mSoundID = mSoundPool!!.load(this, R.raw.bubble_pop, 1) + + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + if (hasFocus) { + + // Get the size of the display so this View knows where borders are + mDisplayWidth = mFrame!!.width + mDisplayHeight = mFrame!!.height + + } + } + + // Set up GestureDetector + private fun setupGestureDetector() { + + mGestureDetector = GestureDetector(this, + object : GestureDetector.SimpleOnGestureListener() { + + // If a fling gesture starts on a BubbleView then change the + // BubbleView's velocity + + override fun onFling( + event1: MotionEvent, + event2: MotionEvent, velocityX: Float, velocityY: Float + ): Boolean { + + // TODO - Implement onFling actions. + // You can get all Views in mFrame one at a time + // using the ViewGroup.getChildAt() method + + + 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 fun onSingleTapConfirmed(event: MotionEvent): Boolean { + + // TODO - Implement onSingleTapConfirmed actions. + // You can get all Views in mFrame using the + // ViewGroup.getChildCount() method + + + return true + } + + // Good practice to override this method because all + // gestures start with a ACTION_DOWN event + override fun onDown(event: MotionEvent): Boolean { + return true + } + }) + } + + override fun onPause() { + + // Release all SoundPool resources + + mSoundPool!!.unload(mSoundID) + mSoundPool!!.release() + mSoundPool = null + + super.onPause() + } + + override fun onGesturePerformed(overlay: GestureOverlayView, gesture: Gesture) { + + // TODO - Get gesture predictions + val predictions: ArrayList<Prediction>? = null + + if (predictions!!.size > 0) { + + // Get highest-ranked prediction + val prediction = predictions[0] + // Log.i(TAG, "pred:" + prediction.name + " score:" + prediction.score) + + // 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 + + inner class BubbleView internal constructor(context: Context, x: Float, y: Float) : + View(context) { + private val mPainter = Paint() + private var mMoverFuture: ScheduledFuture<*>? = null + private var mScaledBitmapWidth: Int = 0 + private var mScaledBitmap: Bitmap? = null + private val BITMAP_SIZE = 64 + private val REFRESH_RATE = 40 + // location, speed and direction of the bubble + private var mXPos: Float = 0.toFloat() + private var mYPos: Float = 0.toFloat() + private var mDx: Float = 0.toFloat() + private var mDy: Float = 0.toFloat() + private val mRadius: Float + private val mRadiusSquared: Float + private var mRotate: Long = 0 + private var mDRotate: Long = 0 + + // Return true if the BubbleView is not on the screen after the move + // operation + private val isOutOfView: Boolean + get() = (mXPos < 0 - mScaledBitmapWidth || mXPos > mDisplayWidth + || mYPos < 0 - mScaledBitmapWidth || mYPos > mDisplayHeight) + + init { + Log.i(TAG, "Creating Bubble at: x:$x y:$y") + + // Create a new random number generator to + // randomize size, rotation, speed and direction + val r = Random() + + // Creates the bubble bitmap for this BubbleView + createScaledBitmap(r) + + // Radius of the Bitmap + mRadius = (mScaledBitmapWidth / 2).toFloat() + 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.isAntiAlias = true + + } + + private fun setRotation(r: Random) { + + mDRotate = if (speedMode == RANDOM) { + + // Set rotation in range [1..3] + ((r.nextInt(3 * BITMAP_SIZE) + 1) / mScaledBitmapWidth).toLong() + } else { + 0 + } + } + + private fun setSpeedAndDirection(r: Random) { + + // Used by test cases + when (speedMode) { + + SINGLE -> { + + mDx = 20f + mDy = 20f + } + + STILL -> { + + // No speed + mDx = 0f + mDy = 0f + } + + else -> { + + // 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) / mScaledBitmapWidth.toFloat() + mDx *= (if (r.nextInt() % 2 == 0) 1 else -1).toFloat() + + mDy = (r.nextInt(mScaledBitmapWidth * 3) + 1) / mScaledBitmapWidth.toFloat() + mDy *= (if (r.nextInt() % 2 == 0) 1 else -1).toFloat() + } + } + } + + private fun createScaledBitmap(r: Random) { + + mScaledBitmapWidth = if (speedMode != RANDOM) { + BITMAP_SIZE * 3 + } else { + + // Set scaled bitmap size in range [1..3] * BITMAP_SIZE + 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 + fun start() { + + // Creates a WorkerThread + val executor = Executors + .newScheduledThreadPool(1) + + // Execute the run() in Worker Thread every REFRESH_RATE + // milliseconds + // Save reference to this job in mMoverFuture + mMoverFuture = executor.scheduleWithFixedDelay({ + // 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.toLong(), TimeUnit.MILLISECONDS) + } + + // Returns true if the BubbleView intersects position (x,y) + @Synchronized + fun intersects(x: Float, y: Float): Boolean { + + //Return true if the BubbleView intersects position (x,y) + + val xDist = x - (mXPos + mRadius) + val 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 + // + fun stop(wasPopped: Boolean) { + + if (null != mMoverFuture) { + + if (!mMoverFuture!!.isDone) { + mMoverFuture!!.cancel(true) + } + + // This work will be performed on the UI Thread + mFrame!!.post { + // Remove the BubbleView from mFrame + mFrame!!.removeView(this@BubbleView) + + // 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 + @Synchronized + fun deflect(velocityX: Float, velocityY: Float) { + mDx = velocityX / REFRESH_RATE + mDy = velocityY / REFRESH_RATE + } + + // Draw the Bubble at its current location + @Synchronized + override fun 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.toFloat(), + 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 + @Synchronized + private fun moveWhileOnScreen(): Boolean { + + // Move the BubbleView + + mXPos += mDx + mYPos += mDy + + return !isOutOfView + + } + +// companion object { +// +// private val BITMAP_SIZE = 64 +// private val REFRESH_RATE = 40 +// } + } + + // Do not modify below here + + override fun onBackPressed() { + openOptionsMenu() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + + menuInflater.inflate(R.menu.menu, menu) + + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.menu_still_mode -> { + speedMode = STILL + return true + } + R.id.menu_single_speed -> { + speedMode = SINGLE + return true + } + R.id.menu_random_mode -> { + speedMode = RANDOM + return true + } + R.id.quit -> { + exitRequested() + return true + } + else -> return super.onOptionsItemSelected(item) + } + } + + private fun exitRequested() { + super.onBackPressed() + } + + companion object { + + private const val MIN_PRED_SCORE = 3.0 + // These variables are for testing purposes, do not modify + private const val RANDOM = 0 + private const val SINGLE = 1 + private const val STILL = 2 + var speedMode = RANDOM + + private const val TAG = "Lab-Gestures" + } +} diff --git a/Labs/Lab8_GesturesLab/app/src/main/res/drawable-hdpi/b64.png b/Labs/Lab8_GesturesLab/app/src/main/res/drawable-hdpi/b64.png new file mode 100644 index 0000000000000000000000000000000000000000..7101a2580a251dde3fce5fbd6867a3fc5cf48222 Binary files /dev/null and b/Labs/Lab8_GesturesLab/app/src/main/res/drawable-hdpi/b64.png differ diff --git a/Labs/Lab8_GesturesLab/app/src/main/res/drawable-hdpi/icon.png b/Labs/Lab8_GesturesLab/app/src/main/res/drawable-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8074c4c571b8cd19e27f4ee5545df367420686d7 Binary files /dev/null and b/Labs/Lab8_GesturesLab/app/src/main/res/drawable-hdpi/icon.png differ diff --git a/Labs/Lab8_GesturesLab/app/src/main/res/drawable-ldpi/icon.png b/Labs/Lab8_GesturesLab/app/src/main/res/drawable-ldpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1095584ec21f71cd0afc9e0993aa2209671b590c Binary files /dev/null and b/Labs/Lab8_GesturesLab/app/src/main/res/drawable-ldpi/icon.png differ diff --git a/Labs/Lab8_GesturesLab/app/src/main/res/drawable-mdpi/icon.png b/Labs/Lab8_GesturesLab/app/src/main/res/drawable-mdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a07c69fa5a0f4da5d5efe96eea12a543154dbab6 Binary files /dev/null and b/Labs/Lab8_GesturesLab/app/src/main/res/drawable-mdpi/icon.png differ diff --git a/Labs/Lab8_GesturesLab/app/src/main/res/layout/main.xml b/Labs/Lab8_GesturesLab/app/src/main/res/layout/main.xml new file mode 100644 index 0000000000000000000000000000000000000000..245a8f098a96f30ee849da5f6c40d14603748a23 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/main/res/layout/main.xml @@ -0,0 +1,16 @@ +<?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" + android:background="#222222" > + </FrameLayout> + +</android.gesture.GestureOverlayView> \ No newline at end of file diff --git a/Labs/Lab8_GesturesLab/app/src/main/res/menu/menu.xml b/Labs/Lab8_GesturesLab/app/src/main/res/menu/menu.xml new file mode 100644 index 0000000000000000000000000000000000000000..4631b4ea859377a5c6359d109e965502b558b738 --- /dev/null +++ b/Labs/Lab8_GesturesLab/app/src/main/res/menu/menu.xml @@ -0,0 +1,15 @@ +<?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 + android:id="@+id/menu_single_speed" + android:title="@string/single_speed_mode_string" /> + <item + android:id="@+id/menu_random_mode" + android:title="@string/random_speed_mode_string" /> + <item + android:id="@+id/quit" + android:title="@string/quit_string" /> +</menu> diff --git a/Labs/Lab8_GesturesLab/app/src/main/res/raw/bubble_pop.wav b/Labs/Lab8_GesturesLab/app/src/main/res/raw/bubble_pop.wav new file mode 100644 index 0000000000000000000000000000000000000000..50886e247e01f246e57f388ee4b15f66c47979fb Binary files /dev/null and b/Labs/Lab8_GesturesLab/app/src/main/res/raw/bubble_pop.wav differ diff --git a/Labs/Lab8_GesturesLab/app/src/main/res/raw/gestures b/Labs/Lab8_GesturesLab/app/src/main/res/raw/gestures new file mode 100644 index 0000000000000000000000000000000000000000..5e4dc1d601fc788a28c3a3be52b45cec590e4ce0 Binary files /dev/null and b/Labs/Lab8_GesturesLab/app/src/main/res/raw/gestures differ diff --git a/Labs/Lab8_GesturesLab/app/src/main/res/values/strings.xml b/Labs/Lab8_GesturesLab/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..222867aa9430d87fbf27136ea46be5d04dc0aeb5 --- /dev/null +++ b/Labs/Lab8_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/Labs/Lab8_GesturesLab/build.gradle b/Labs/Lab8_GesturesLab/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..9f5253c3996fc0bbfaaff2b5e02e14f0c25b3794 --- /dev/null +++ b/Labs/Lab8_GesturesLab/build.gradle @@ -0,0 +1,20 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + jcenter() + google() + } + dependencies { + classpath 'com.android.tools.build:gradle:4.0.2' + //noinspection DifferentKotlinGradleVersion,DifferentKotlinGradleVersion + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} diff --git a/Labs/Lab8_GesturesLab/gradle/wrapper/gradle-wrapper.jar b/Labs/Lab8_GesturesLab/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..13372aef5e24af05341d49695ee84e5f9b594659 Binary files /dev/null and b/Labs/Lab8_GesturesLab/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Labs/Lab8_GesturesLab/gradle/wrapper/gradle-wrapper.properties b/Labs/Lab8_GesturesLab/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..4e1cc9db6b597a36bc8555c4148024f7ce580e90 --- /dev/null +++ b/Labs/Lab8_GesturesLab/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Labs/Lab8_GesturesLab/gradlew b/Labs/Lab8_GesturesLab/gradlew new file mode 100644 index 0000000000000000000000000000000000000000..9d82f78915133e1c35a6ea51252590fb38efac2f --- /dev/null +++ b/Labs/Lab8_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/Labs/Lab8_GesturesLab/gradlew.bat b/Labs/Lab8_GesturesLab/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..8a0b282aa6885fb573c106b3551f7275c5f17e8e --- /dev/null +++ b/Labs/Lab8_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/Labs/Lab8_GesturesLab/settings.gradle b/Labs/Lab8_GesturesLab/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..e7b4def49cb53d9aa04228dd3edb14c9e635e003 --- /dev/null +++ b/Labs/Lab8_GesturesLab/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/Project/.DS_Store b/Project/.DS_Store deleted file mode 100644 index 6289557bb9832291749b3636f8097cef0221e00d..0000000000000000000000000000000000000000 Binary files a/Project/.DS_Store and /dev/null differ