From 8a2dae029e05f0b61ac5ffa71a1a26cb83705ab7 Mon Sep 17 00:00:00 2001 From: Clifton Date: Thu, 4 May 2017 10:12:02 -0700 Subject: [PATCH 01/17] Adds Arduino code --- bluebot.ino | 293 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 bluebot.ino diff --git a/bluebot.ino b/bluebot.ino new file mode 100644 index 0000000..b77940c --- /dev/null +++ b/bluebot.ino @@ -0,0 +1,293 @@ +//BlueBot V1.0 +//2017.05.03 +//Clifton C. Craig + +#include //servo library +Servo myservo; // create servo object to control servo +char getstr; +int AUTONOMOUS = 1; +int MANUAL = 0; +int state = 0; +int Echo = A4; +int Trig = A5; +//Right wheel +int ENA = 11; +int in1 = 9; +int in2 = 8; + +//Left wheel +int ENB = 5; +int in3 = 7; +int in4 = 6; + +int ABS = 150; +int rightDistance = 0,leftDistance = 0,middleDistance = 0; +int THRESHOLD = 30; + +void _mForward() +{ + analogWrite(ENA,ABS); + analogWrite(ENB,ABS); + digitalWrite(in1,LOW); + digitalWrite(in2,HIGH); + digitalWrite(in3,LOW); + digitalWrite(in4,HIGH); + Serial.println("go forward!"); +} + +void _mBack() +{ + analogWrite(ENA,ABS); + analogWrite(ENB,ABS); + digitalWrite(in1,HIGH); + digitalWrite(in2,LOW); + digitalWrite(in3,HIGH); + digitalWrite(in4,LOW); + Serial.println("go back!"); +} + +void _mleft() +{ + analogWrite(ENA,ABS); + analogWrite(ENB,ABS); + digitalWrite(in1,LOW); + digitalWrite(in2,HIGH); + digitalWrite(in3,HIGH); + digitalWrite(in4,LOW); + Serial.println("go left!"); +} + +void _mright() +{ + analogWrite(ENA,ABS); + analogWrite(ENB,ABS); + digitalWrite(in1,HIGH); + digitalWrite(in2,LOW); + digitalWrite(in3,LOW); + digitalWrite(in4,HIGH); + Serial.println("go right!"); +} +void _mStop() +{ + digitalWrite(ENA,LOW); + digitalWrite(ENB,LOW); + Serial.println("Stop!"); +} + /*Ultrasonic distance measurement Sub function*/ +int Distance_test() +{ + delay(90); + digitalWrite(Trig, LOW); + delayMicroseconds(2); + digitalWrite(Trig, HIGH); + delayMicroseconds(30); + digitalWrite(Trig, LOW); + float Fdistance = pulseIn(Echo, HIGH); + Fdistance= Fdistance/58; + return (int)Fdistance; +} + +void setup() +{ + myservo.attach(3);// attach servo on pin 3 to servo object + Serial.begin(9600); + pinMode(Echo, INPUT); + pinMode(Trig, OUTPUT); + pinMode(in1,OUTPUT); + pinMode(in2,OUTPUT); + pinMode(in3,OUTPUT); + pinMode(in4,OUTPUT); + pinMode(ENA,OUTPUT); + pinMode(ENB,OUTPUT); + _mStop(); + lookAhead(); + #ifdef debug + debug(); + #endif +} + +int lookNScan(int pos) +{ + myservo.write(pos);//setservo position according to scaled value + delay(500); + return Distance_test(); +} + +void lookAhead() +{ + //setservo position according to scaled value + middleDistance = lookNScan(90); + #ifdef send + Serial.print("middleDistance="); + Serial.println(middleDistance); + #endif +} + +int lookOffRight() +{ + int offRight = lookNScan(65); + + #ifdef send + Serial.print("offRight="); + Serial.println(offRight); + #endif + return offRight; +} + +int lookOffLeft() +{ + int offLeft = lookNScan(115); + + #ifdef send + Serial.print("offLeft="); + Serial.println(offLeft); + #endif + return offLeft; +} + +void loop() +{ + getstr=Serial.read(); + if(getstr=='A') { + stateChange(); + } else if(state == AUTONOMOUS) { + autonomous(); + } else if(state == MANUAL) { + processCmd(); + } + +} + +void processCmd() +{ + if(getstr=='f') + { + _mForward(); + } + else if(getstr=='b') + { + _mBack(); + delay(200); + } + else if(getstr=='l') + { + _mleft(); + delay(200); + } + else if(getstr=='r') + { + _mright(); + delay(200); + } + else if(getstr=='s') + { + _mStop(); + } +} + +void stateChange() +{ + if(state == MANUAL) + state = AUTONOMOUS; + else if(state == AUTONOMOUS) + state = MANUAL; +} + +void autonomous() +{ + lookAhead(); + int r = lookOffRight(); + int l = lookOffLeft(); + + Serial.print("offRight="); + Serial.print(r); + Serial.print(" offLeft="); + Serial.println(l); + if(middleDistance<=THRESHOLD || l<=THRESHOLD || r<=THRESHOLD) + { + handleCollision(); + } + else + _mForward(); +} + +void handleCollision() +{ + _mStop(); + rightDistance = lookNScan(5); + delay(1000); + + #ifdef send + Serial.print("rightDistance="); + Serial.println(rightDistance); + #endif + + leftDistance = lookNScan(180); + delay(1000); + + #ifdef send + Serial.print("leftDistance="); + Serial.println(leftDistance); + #endif + + lookAhead(); + delay(500); + if(rightDistance>leftDistance) + { + _mBack(); + delay(500); + _mright(); + delay(500); + } + else if(rightDistance 0) + last = Serial.read(); + switch(last) + { + case 'l': + _mleft(); + break; + case 'r': + _mright(); + break; + case 'f': + _mForward(); + break; + case 'b': + _mBack(); + break; + case 'x': + Serial.print("Done Debugging!"); + return; + break; + } + delay(500); + _mStop(); + last = ' '; + } +} + From e7afed9fb629d268028848971d8c4d82e7f8b35f Mon Sep 17 00:00:00 2001 From: George E Fouche Date: Fri, 19 May 2017 09:56:16 -0700 Subject: [PATCH 02/17] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d42bfc7..c455f89 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # ![RC Car Logo](https://github.com/fouliex/AndroidRcCar/blob/master/resources/images/RobotCarLogo.jpg) Android RC Car(In Progress) ## Wiki Contents 1. [**Assemble the RC Car**](https://github.com/fouliex/AndroidRcCar/wiki/Assemble-the-RC-Car) -2. [**DC Motor Construction**](https://github.com/fouliex/AndroidRcCar/wiki/RC-DC-Motor-Construction) \ No newline at end of file +2. [**DC Motor Construction**](https://github.com/fouliex/AndroidRcCar/wiki/RC-DC-Motor-Construction) + +You can get the Arduino Smart Robot Car Kit from [**Amazon**](https://www.amazon.com/gp/product/B01DPH0SWY/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1) From 30b6957002d13dc294f10b8262459e3ebfd3ab4e Mon Sep 17 00:00:00 2001 From: Clifton Date: Fri, 19 May 2017 10:20:18 -0700 Subject: [PATCH 03/17] Feature/blue ide (#4) * Gets rid of example test. * Reverse left/right controls for consistancy with the UI. * Adds proper build support for Espresso. * Adds initial Spec/implementation for on-device BlueBotIDE. * Adds rough/ugly UI via TDD. * Adds basic code execution behavior to the IDE. * Adds generic request processing to UI instead of direct code execution to keep the UI thread free. * Adds response handler binding to the given RequestProcessor. * Adds basic response handling to the Activity. * Improves error handling. * Adds bluebotruntimelib w/ basic code runner. * Adds imposed line number ordering to BlueCode. * Adds feature to stop the bot after the final command & fixes broken tests. * Removes example unnecessary test. * Removes git tracking of project metadata. * Relocates RequestProcessor and ResponseHandler into the Java library. * Adds a multi-threaded CodeRunnerRequestProcessor which runs code in the background. * Cleans up concurrency code using Executor class. * Adds response handling to the CodeRunner. * Adds minor UI adjustments. (Still looks ugly.) * Connects the IDE activity to the main activity. * Wires everything together in a hacky way. * Upgrades android support version and extracts properties for version numbers. * Extracts strings for localization. * Removes globally shared bluetooth object from main activity and introduces shared components in the poor man's injection framework. * Addresses many issues from static analysis. *** Moves run button & error text to top of screen in IDE activity Adds view intent to allow Google app indexing/crawling inverts the name and semantics of the has() method on SharedComponents to doesNotHave() Removes redundant .gitignore entries Adds weaker scope to members marks other members as final addresses unchecked generics warnings Moves anonymous inner class to top level class. Throws Runtme exceptions when reflection instantiation of injection modules fail. Allows application full backup of content. Addresses certain methods with constant parameters by inlining. * Fixes the left/right signals in bluetooth codes. (They were originally backwards in the Arduino code and now they're fixed in both places.) * Adds autonomous mode toggle. --- .gitignore | 3 +- .idea/compiler.xml | 22 --- .idea/copyright/profiles_settings.xml | 3 - .idea/encodings.xml | 6 - .idea/gradle.xml | 18 -- .idea/misc.xml | 46 ----- README.md | 3 + app/build.gradle | 22 ++- .../java/com/bluebot/MainActivitySpec.java | 55 ++++++ .../com/bluebot/droid/ide/BlueBotIDESpec.java | 128 +++++++++++++ .../rccar/ExampleInstrumentedTest.java | 26 --- app/src/main/AndroidManifest.xml | 16 +- .../java/com/bluebot/BlueBotApplication.java | 55 ++++++ .../rccar => bluebot}/MainActivity.java | 91 ++++----- .../bluebot/droid/ide/BlueBotIDEActivity.java | 65 +++++++ .../injection/BlueBotIDEActivityModule.java | 45 +++++ .../bluebot/injection/InjectionModule.java | 12 ++ .../java/com/bluebot/injection/Injector.java | 47 +++++ .../injection/InjectorSharedComponents.java | 27 +++ .../bluebot/injection/MainActivityModule.java | 36 ++++ .../bluebot/injection/SharedComponents.java | 14 ++ .../main/res/layout/activity_blue_bot_ide.xml | 73 ++++++++ app/src/main/res/layout/activity_main.xml | 79 ++++++-- app/src/main/res/values/strings.xml | 20 +- .../fouliex/rccar/ExampleUnitTest.java | 17 -- bluebotruntimelib/.gitignore | 1 + bluebotruntimelib/build.gradle | 9 + .../runtime/bluetooth/BluetoothCodes.java | 17 ++ .../bluetooth/BluetoothCommandSink.java | 10 + .../runtime/runner/BlueCodeRunner.java | 64 +++++++ .../ui/CodeRunnerRequestProcessor.java | 78 ++++++++ .../java/com/bluebot/ui/RequestProcessor.java | 12 ++ .../java/com/bluebot/ui/ResponseHandler.java | 12 ++ .../java/com/bluebot/ui/UIRequestTypes.java | 10 + .../java/com/bluebot/ui/UIResponseType.java | 13 ++ .../runtime/runner/BlueCodeRunnerSpec.java | 140 ++++++++++++++ .../ui/CodeRunnerRequestProcessorSpec.java | 176 ++++++++++++++++++ settings.gradle | 2 +- 38 files changed, 1259 insertions(+), 214 deletions(-) delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/copyright/profiles_settings.xml delete mode 100644 .idea/encodings.xml delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/misc.xml create mode 100644 app/src/androidTest/java/com/bluebot/MainActivitySpec.java create mode 100644 app/src/androidTest/java/com/bluebot/droid/ide/BlueBotIDESpec.java delete mode 100644 app/src/androidTest/java/com/car/rc/example/fouliex/rccar/ExampleInstrumentedTest.java create mode 100644 app/src/main/java/com/bluebot/BlueBotApplication.java rename app/src/main/java/com/{car/rc/example/fouliex/rccar => bluebot}/MainActivity.java (76%) create mode 100644 app/src/main/java/com/bluebot/droid/ide/BlueBotIDEActivity.java create mode 100644 app/src/main/java/com/bluebot/injection/BlueBotIDEActivityModule.java create mode 100644 app/src/main/java/com/bluebot/injection/InjectionModule.java create mode 100644 app/src/main/java/com/bluebot/injection/Injector.java create mode 100644 app/src/main/java/com/bluebot/injection/InjectorSharedComponents.java create mode 100644 app/src/main/java/com/bluebot/injection/MainActivityModule.java create mode 100644 app/src/main/java/com/bluebot/injection/SharedComponents.java create mode 100644 app/src/main/res/layout/activity_blue_bot_ide.xml delete mode 100644 app/src/test/java/com/car/rc/example/fouliex/rccar/ExampleUnitTest.java create mode 100644 bluebotruntimelib/.gitignore create mode 100644 bluebotruntimelib/build.gradle create mode 100644 bluebotruntimelib/src/main/java/com/bluebot/runtime/bluetooth/BluetoothCodes.java create mode 100644 bluebotruntimelib/src/main/java/com/bluebot/runtime/bluetooth/BluetoothCommandSink.java create mode 100644 bluebotruntimelib/src/main/java/com/bluebot/runtime/runner/BlueCodeRunner.java create mode 100644 bluebotruntimelib/src/main/java/com/bluebot/ui/CodeRunnerRequestProcessor.java create mode 100644 bluebotruntimelib/src/main/java/com/bluebot/ui/RequestProcessor.java create mode 100644 bluebotruntimelib/src/main/java/com/bluebot/ui/ResponseHandler.java create mode 100644 bluebotruntimelib/src/main/java/com/bluebot/ui/UIRequestTypes.java create mode 100644 bluebotruntimelib/src/main/java/com/bluebot/ui/UIResponseType.java create mode 100644 bluebotruntimelib/src/test/java/com/bluebot/runtime/runner/BlueCodeRunnerSpec.java create mode 100644 bluebotruntimelib/src/test/java/com/bluebot/ui/CodeRunnerRequestProcessorSpec.java diff --git a/.gitignore b/.gitignore index 39fb081..2c41655 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ *.iml .gradle /local.properties -/.idea/workspace.xml -/.idea/libraries .DS_Store /build /captures .externalNativeBuild +.idea \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 96cc43e..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf3..0000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 97626ba..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 7ac24c7..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 5d19981..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index c455f89..17fdcb3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +**Android BlueBot** + +An Android app that controls an Arduino based bot. # ![RC Car Logo](https://github.com/fouliex/AndroidRcCar/blob/master/resources/images/RobotCarLogo.jpg) Android RC Car(In Progress) ## Wiki Contents 1. [**Assemble the RC Car**](https://github.com/fouliex/AndroidRcCar/wiki/Assemble-the-RC-Car) diff --git a/app/build.gradle b/app/build.gradle index f0589e1..1d9f36a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,7 @@ android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { - applicationId "com.car.rc.example.fouliex.rccar" + applicationId "com.bluebot" minSdkVersion 15 targetSdkVersion 25 versionCode 1 @@ -19,13 +19,21 @@ android { } } +ext { + supportVersion = '25.3.1' + espressoVersion = '2.2.2' +} dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + compile fileTree(include: ['*.jar'], dir: 'libs') + compile "com.android.support:appcompat-v7:$supportVersion" + compile "com.android.support.constraint:constraint-layout:1.0.2" + compile "com.akexorcist:bluetoothspp:1.0.0" + compile "com.android.support:support-v4:$supportVersion" + compile project(':bluebotruntimelib') + testCompile 'junit:junit:4.12' + androidTestCompile "com.android.support:support-v4:$supportVersion" + androidTestCompile("com.android.support.test.espresso:espresso-core:$espressoVersion", { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:25.2.0' - compile 'com.android.support.constraint:constraint-layout:1.0.2' - compile 'com.akexorcist:bluetoothspp:1.0.0' - testCompile 'junit:junit:4.12' + androidTestCompile "com.android.support.test.espresso:espresso-intents:$espressoVersion" } diff --git a/app/src/androidTest/java/com/bluebot/MainActivitySpec.java b/app/src/androidTest/java/com/bluebot/MainActivitySpec.java new file mode 100644 index 0000000..5827be9 --- /dev/null +++ b/app/src/androidTest/java/com/bluebot/MainActivitySpec.java @@ -0,0 +1,55 @@ +package com.bluebot; + +import android.support.test.espresso.intent.Intents; +import android.support.test.filters.LargeTest; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; + +import com.bluebot.droid.ide.BlueBotIDEActivity; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.intent.Intents.intended; +import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static android.support.test.espresso.matcher.ViewMatchers.isClickable; +import static android.support.test.espresso.matcher.ViewMatchers.withText; + +/** + * Created by Clifton Craig on 4/12/17. + * Copyright GE 4/12/17 + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class MainActivitySpec { + @Rule + public ActivityTestRule activityTestRule = new ActivityTestRule<>(MainActivity.class); + + @Before + public void setUp() throws Exception { + Intents.init(); + } + + @After + public void tearDown() throws Exception { + Intents.release(); + + } + + @Test + public void shouldHaveAnIDEButton() throws Exception { + onView(withText("IDE")).check(matches(isClickable())); + } + + @Test + public void clickingTheIDEButtonShouldLaunchTheIDEActivity() throws Exception { + onView(withText("IDE")).perform(click()); + intended(hasComponent(BlueBotIDEActivity.class.getName())); + } +} diff --git a/app/src/androidTest/java/com/bluebot/droid/ide/BlueBotIDESpec.java b/app/src/androidTest/java/com/bluebot/droid/ide/BlueBotIDESpec.java new file mode 100644 index 0000000..38713be --- /dev/null +++ b/app/src/androidTest/java/com/bluebot/droid/ide/BlueBotIDESpec.java @@ -0,0 +1,128 @@ +package com.bluebot.droid.ide; + +import android.support.test.espresso.action.ViewActions; +import android.support.test.filters.LargeTest; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; + +import com.bluebot.ui.RequestProcessor; +import com.bluebot.ui.ResponseHandler; +import com.bluebot.ui.UIRequestTypes; +import com.bluebot.ui.UIResponseType; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Map; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Created by Clifton Craig on 4/8/17. + * Copyright GE 4/8/17 + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class BlueBotIDESpec implements RequestProcessor { + @Rule + public final ActivityTestRule activityTestRule = new ActivityTestRule<>(BlueBotIDEActivity.class); + private String dataPassedToRequestProcessor; + private String requestTypePassedToRequestProcessor; + private ResponseHandler responseHandlerPassedToBind; + + @Test + public void shouldIncludeWindowTitleOnScreen() { + onView(withText("BlueBot IDE")).check(matches(isDisplayed())); + } + + @Test + public void shouldHaveAPlaceToEnterCode() throws Exception { + onView(withContentDescription("Code Entry")).check(matches(isDisplayed())); + } + + @Test + public void shouldHaveAButtonToRunCode() throws Exception { + onView(withContentDescription("Run")).check(matches(isDisplayed())); + } + @Test + public void theIDEShouldDependOnARequestProcessor() throws Exception { + activityTestRule.getActivity().setRequestProcessor(this); + } + + @Override + public void bind(ResponseHandler responseHandler){ + this.responseHandlerPassedToBind = responseHandler; + } + + @Test + public void theIDEActivityShouldBindTheRequestProcessorWhenSet() throws Exception { + activityTestRule.getActivity().setRequestProcessor(this); + assertNotNull(responseHandlerPassedToBind); + } + + @Test + public void boundResponseHandlerShouldShowErrorMEssageInTheUI() throws Exception { + //Given + theIDEActivityShouldBindTheRequestProcessorWhenSet(); + //When we have an error response + Map response = new HashMap(){{ put(UIResponseType.ERROR,"Stupidity Error!"); }}; + //And we send it to the responseHandler... + responseHandlerPassedToBind.responseForRequest(UIRequestTypes.EXECUTE_CODE, response); + //Then the UI should light up with the error + onView(withContentDescription("Error message")).check(matches(isDisplayed())); + } + + @Test + public void boundResponseHandlerShouldNotShowErrorOnSuccess() throws Exception { + //Given + theIDEActivityShouldBindTheRequestProcessorWhenSet(); + //When we have an error response + Map response = new HashMap(){{ put(UIResponseType.SUCCESS,""); }}; + //And we send it to the responseHandler... + responseHandlerPassedToBind.responseForRequest(UIRequestTypes.EXECUTE_CODE, response); + //Then the UI should **NOT** light up with the error + onView(withContentDescription("Error message")).check(matches( not (isDisplayed()))); + } + + @Test + public void theIDEActivityShouldNotShowErrorMessagesByDefault() throws Exception { + onView(withContentDescription("Error message")).check(matches(not(isDisplayed()))); + } + + @Override + public void processRequest(String requestType, Object data) { + this.requestTypePassedToRequestProcessor = requestType; + this.dataPassedToRequestProcessor = String.valueOf(data); + } + + @Test + public void enteringCodeAndPressingRunShouldInvokeCodeExecutor() throws Exception { + //Given this test case pretends to be a code executor + activityTestRule.getActivity().setRequestProcessor(this); + //And some BlueBot code... + String someBlueBotCode = "wake up bot\n" + + "spin around\n" + + "say hello"; + + //When we type the code in... + onView(withContentDescription("Code Entry")) + .perform(ViewActions.typeText(someBlueBotCode)); + + //And click the run button + onView(withContentDescription("Run")).perform(ViewActions.click()); + + //Then our Code Executor should be asked to execute someBlueBotCode + assertEquals("An EXECUTE_CODE request should be given.", UIRequestTypes.EXECUTE_CODE,requestTypePassedToRequestProcessor); + //And our Code Executor should be asked to execute someBlueBotCode + assertEquals("The given code should be executed.", someBlueBotCode, dataPassedToRequestProcessor); + } +} diff --git a/app/src/androidTest/java/com/car/rc/example/fouliex/rccar/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/car/rc/example/fouliex/rccar/ExampleInstrumentedTest.java deleted file mode 100644 index b328ce6..0000000 --- a/app/src/androidTest/java/com/car/rc/example/fouliex/rccar/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.car.rc.example.fouliex.rccar; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.car.rc.example.fouliex.rccar", appContext.getPackageName()); - } -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 621bb03..dcf56b5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,17 +1,19 @@ + package="com.bluebot"> - + + android:theme="@style/AppTheme" + android:fullBackupContent="true"> @@ -19,6 +21,14 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/bluebot/BlueBotApplication.java b/app/src/main/java/com/bluebot/BlueBotApplication.java new file mode 100644 index 0000000..e140f51 --- /dev/null +++ b/app/src/main/java/com/bluebot/BlueBotApplication.java @@ -0,0 +1,55 @@ +package com.bluebot; + +import android.app.Activity; +import android.app.Application; +import android.os.Bundle; + +import com.bluebot.injection.Injector; + +/** + * Created by Clifton Craig on 4/12/17. + * Copyright GE 4/12/17 + */ + +public class BlueBotApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(Activity activity, Bundle bundle) { + Injector.injectEach(activity); + } + + @Override + public void onActivityStarted(Activity activity) { + + } + + @Override + public void onActivityResumed(Activity activity) { + + } + + @Override + public void onActivityPaused(Activity activity) { + + } + + @Override + public void onActivityStopped(Activity activity) { + + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { + + } + + @Override + public void onActivityDestroyed(Activity activity) { + + } + }); + } +} diff --git a/app/src/main/java/com/car/rc/example/fouliex/rccar/MainActivity.java b/app/src/main/java/com/bluebot/MainActivity.java similarity index 76% rename from app/src/main/java/com/car/rc/example/fouliex/rccar/MainActivity.java rename to app/src/main/java/com/bluebot/MainActivity.java index 6e278af..7bb0d8b 100644 --- a/app/src/main/java/com/car/rc/example/fouliex/rccar/MainActivity.java +++ b/app/src/main/java/com/bluebot/MainActivity.java @@ -1,52 +1,53 @@ -package com.car.rc.example.fouliex.rccar; +package com.bluebot; import android.app.Activity; import android.content.Intent; -import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.Toast; +import com.bluebot.droid.ide.BlueBotIDEActivity; + import app.akexorcist.bluetotohspp.library.BluetoothSPP; import app.akexorcist.bluetotohspp.library.BluetoothState; import app.akexorcist.bluetotohspp.library.DeviceList; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.AUTONOMOUS; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.BACKWARD; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.FORWARD; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.LEFT; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.LEFTBACK; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.RIGHT; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.RIGHTBACK; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.STOP; + public class MainActivity extends AppCompatActivity implements View.OnTouchListener { - final String FORWARD = "f"; - final String BACKWARD = "b"; - final String LEFT = "l"; - final String RIGHT = "r"; - final String STOP = "s"; - final String LEFTBACK = "L"; - final String RIGHTBACK = "R"; - final String AUTOPILOT = "a"; - - boolean forward = false; - boolean backward = false; - boolean left = false; - boolean right = false; - boolean auto = false; - boolean stop = false; - - BluetoothSPP bluetooth; - Button connect; + private boolean forward = false; + private boolean backward = false; + private boolean left = false; + private boolean right = false; + private boolean stop = false; + + private BluetoothSPP bluetooth; + private Button connect; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - bluetooth = new BluetoothSPP(this); connect = (Button) findViewById(R.id.connect); - if (!bluetooth.isBluetoothAvailable()) { - Toast.makeText(getApplicationContext(), "Bluetooth is not available", Toast.LENGTH_SHORT).show(); - //close the Activity - finish(); - } - setBluetoothListener(); + findViewById(R.id.ideButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + final Intent intent = new Intent(getApplicationContext(), BlueBotIDEActivity.class); + startActivity(intent); + } + }); setConnectListener(); setButtonsOnTouchListener(); @@ -75,7 +76,7 @@ protected void onStart() { protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == BluetoothState.REQUEST_CONNECT_DEVICE) { if (resultCode == Activity.RESULT_OK && data != null) { - connect.setText("Connecting..."); + connect.setText(R.string.connecting); connect.setEnabled(false); bluetooth.connect(data); } @@ -89,20 +90,14 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } - /** - * Set buttons on touch listener - */ private void setButtonsOnTouchListener() { findViewById(R.id.forward).setOnTouchListener(this); findViewById(R.id.backward).setOnTouchListener(this); findViewById(R.id.left).setOnTouchListener(this); findViewById(R.id.right).setOnTouchListener(this); - findViewById(R.id.auto).setOnTouchListener(this); + findViewById(R.id.autonomous).setOnTouchListener(this); } - /** - * Set connection to phone listener - */ private void setConnectListener() { connect.setOnClickListener(new View.OnClickListener() { @Override @@ -117,27 +112,24 @@ public void onClick(View v) { }); } - /** - * Set Bluetooth Listener - */ private void setBluetoothListener() { bluetooth.setBluetoothConnectionListener(new BluetoothSPP.BluetoothConnectionListener() { @Override public void onDeviceConnected(String name, String address) { connect.setEnabled(true); - connect.setText("Connected to " + name); + connect.setText(getString(R.string.connected_to, name)); } @Override public void onDeviceDisconnected() { connect.setEnabled(true); - connect.setText("Connection lost"); + connect.setText(R.string.connection_lost); } @Override public void onDeviceConnectionFailed() { connect.setEnabled(true); - connect.setText("Unable to connect"); + connect.setText(R.string.unable_to_connect); } }); } @@ -161,9 +153,8 @@ public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) right = true; else if (event.getAction() == MotionEvent.ACTION_UP) right = false; break; - case R.id.auto: - if (event.getAction() == MotionEvent.ACTION_DOWN) auto = true; - else if (event.getAction() == MotionEvent.ACTION_UP) auto = false; + case R.id.autonomous: + if (event.getAction() == MotionEvent.ACTION_UP) send(AUTONOMOUS); break; } @@ -174,6 +165,7 @@ public boolean onTouch(View v, MotionEvent event) { } private void command() { + if (stop) { send(STOP); } else if (forward) { @@ -188,7 +180,6 @@ private void command() { else send(STOP); } else if (left && !right) send(LEFT); else if (right) send(RIGHT); - else if(auto)send(AUTOPILOT); else send(STOP); } @@ -196,4 +187,14 @@ private void command() { private void send(String command) { bluetooth.send(command,true); } + + public void setBluetooth(BluetoothSPP bluetooth) { + this.bluetooth = bluetooth; + if (!bluetooth.isBluetoothAvailable()) { + Toast.makeText(getApplicationContext(), "Bluetooth is not available", Toast.LENGTH_SHORT).show(); + //close the Activity + finish(); + } + setBluetoothListener(); + } } diff --git a/app/src/main/java/com/bluebot/droid/ide/BlueBotIDEActivity.java b/app/src/main/java/com/bluebot/droid/ide/BlueBotIDEActivity.java new file mode 100644 index 0000000..ce60890 --- /dev/null +++ b/app/src/main/java/com/bluebot/droid/ide/BlueBotIDEActivity.java @@ -0,0 +1,65 @@ +package com.bluebot.droid.ide; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.text.Editable; +import android.util.Log; +import android.view.View; +import android.widget.EditText; + +import com.bluebot.R; +import com.bluebot.ui.RequestProcessor; +import com.bluebot.ui.ResponseHandler; +import com.bluebot.ui.UIRequestTypes; +import com.bluebot.ui.UIResponseType; + +import java.util.Map; + +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; + + +public class BlueBotIDEActivity extends AppCompatActivity implements ResponseHandler { + + private RequestProcessor requestProcessor; + private View errorMessage; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getIntentData(); + setContentView(R.layout.activity_blue_bot_ide); + findViewById(R.id.runButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + final Editable code = ((EditText) findViewById(R.id.codeEntry)).getText(); + requestProcessor.processRequest(UIRequestTypes.EXECUTE_CODE, code.toString()); + } + }); + errorMessage = findViewById(R.id.errorMessage); + errorMessage.setVisibility(INVISIBLE); + } + + private void getIntentData() { + if (getIntent().getAction()!=null && getIntent().getAction().equals(Intent.ACTION_VIEW)) { + Log.d(getLocalClassName(), "Intent data: " + getIntent().getData()); + } + } + + public void setRequestProcessor(RequestProcessor requestProcessor) { + this.requestProcessor = requestProcessor; + requestProcessor.bind(this); + } + + @Override + public void responseForRequest(String requestType, final Map response) { + runOnUiThread(new Runnable() { + public void run() { + if (response.containsKey(UIResponseType.ERROR)) { + errorMessage.setVisibility(VISIBLE); + } + } + }); + } +} diff --git a/app/src/main/java/com/bluebot/injection/BlueBotIDEActivityModule.java b/app/src/main/java/com/bluebot/injection/BlueBotIDEActivityModule.java new file mode 100644 index 0000000..0f169c0 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/BlueBotIDEActivityModule.java @@ -0,0 +1,45 @@ +package com.bluebot.injection; + +import com.bluebot.droid.ide.BlueBotIDEActivity; +import com.bluebot.runtime.bluetooth.BluetoothCommandSink; +import com.bluebot.runtime.runner.BlueCodeRunner; +import com.bluebot.ui.CodeRunnerRequestProcessor; + +import app.akexorcist.bluetotohspp.library.BluetoothSPP; + +/** + * Created by Clifton Craig on 4/12/17. + * Copyright GE 4/12/17 + */ + +class BlueBotIDEActivityModule implements InjectionModule{ + private SharedComponents sharedComponents; + + @Override + public void inject(Object object) { + injectActivity((BlueBotIDEActivity)object); + } + + @Override + public void setSharedComponents(SharedComponents sharedComponents) { + this.sharedComponents = sharedComponents; + } + + private void injectActivity(final BlueBotIDEActivity blueBotIDEActivity) { + final CodeRunnerRequestProcessor requestProcessor = new CodeRunnerRequestProcessor(new BlueCodeRunner(new BluetoothCommandSink() { + private final BluetoothSPP bluetooth = sharedComponents.get(BluetoothSPP.class); + + @Override + public void send(String command) { + bluetooth.send(command, true); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + })); + requestProcessor.setRunIndefinitely(true); + blueBotIDEActivity.setRequestProcessor(requestProcessor); + } +} diff --git a/app/src/main/java/com/bluebot/injection/InjectionModule.java b/app/src/main/java/com/bluebot/injection/InjectionModule.java new file mode 100644 index 0000000..92f6e44 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/InjectionModule.java @@ -0,0 +1,12 @@ +package com.bluebot.injection; + +/** + * Created by Clifton Craig on 4/12/17. + * Copyright GE 4/12/17 + */ + +interface InjectionModule { + void inject(Object object); + + void setSharedComponents(SharedComponents sharedComponents); +} diff --git a/app/src/main/java/com/bluebot/injection/Injector.java b/app/src/main/java/com/bluebot/injection/Injector.java new file mode 100644 index 0000000..c4b7712 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/Injector.java @@ -0,0 +1,47 @@ +package com.bluebot.injection; + +import android.app.Activity; +import android.content.Context; + +import com.bluebot.MainActivity; +import com.bluebot.droid.ide.BlueBotIDEActivity; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Clifton Craig on 4/12/17. + * Copyright GE 4/12/17 + */ + +public class Injector { + private static final SharedComponents share = new InjectorSharedComponents(); + + private static final MapmoduleMap = new HashMap(){{ + put(BlueBotIDEActivity.class, BlueBotIDEActivityModule.class); + put(MainActivity.class, MainActivityModule.class); + }}; + public static void injectEach(Activity activity) { + if(share.doesNotHave(Context.class)) { + share.put(Context.class, activity.getApplicationContext()); + } + if(moduleMap.containsKey(activity.getClass())) { + InjectionModule module = inflate(moduleMap.get(activity.getClass())); + module.setSharedComponents(share); + module.inject(activity); + } + } + + private static InjectionModule inflate(Class aClass) { + Object instance = null; + try { + instance = aClass.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException("Cannot instantiate " + aClass, e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot access " + aClass, e); + } + return (InjectionModule) instance; + } + +} diff --git a/app/src/main/java/com/bluebot/injection/InjectorSharedComponents.java b/app/src/main/java/com/bluebot/injection/InjectorSharedComponents.java new file mode 100644 index 0000000..ba7a373 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/InjectorSharedComponents.java @@ -0,0 +1,27 @@ +package com.bluebot.injection; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Clifton Craig on 4/13/17. + * Copyright GE 4/13/17 + */ +class InjectorSharedComponents implements SharedComponents { + private Map internalShare = new HashMap<>(); + + @Override + public boolean doesNotHave(Class aClass) { + return ! internalShare.containsKey(aClass); + } + + @Override + public T get(Class aClass) { + return (T) internalShare.get(aClass); + } + + @Override + public void put(Class aClass, T instance) { + internalShare.put(aClass, instance); + } +} diff --git a/app/src/main/java/com/bluebot/injection/MainActivityModule.java b/app/src/main/java/com/bluebot/injection/MainActivityModule.java new file mode 100644 index 0000000..292b2b4 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/MainActivityModule.java @@ -0,0 +1,36 @@ +package com.bluebot.injection; + +import android.content.Context; + +import com.bluebot.MainActivity; + +import app.akexorcist.bluetotohspp.library.BluetoothSPP; + +/** + * Created by Clifton Craig on 4/13/17. + * Copyright GE 4/13/17 + */ + +class MainActivityModule implements InjectionModule{ + private SharedComponents sharedComponents; + + @Override + public void inject(Object object) { + injectActivity((MainActivity)object); + } + + private void injectActivity(MainActivity mainActivity) { + mainActivity.setBluetooth(lazyGetBluetoothSPP()); + } + + private BluetoothSPP lazyGetBluetoothSPP() { + if(sharedComponents.doesNotHave(BluetoothSPP.class)) + sharedComponents.put(BluetoothSPP.class, new BluetoothSPP(sharedComponents.get(Context.class))); + return sharedComponents.get(BluetoothSPP.class); + } + + @Override + public void setSharedComponents(SharedComponents sharedComponents) { + this.sharedComponents = sharedComponents; + } +} diff --git a/app/src/main/java/com/bluebot/injection/SharedComponents.java b/app/src/main/java/com/bluebot/injection/SharedComponents.java new file mode 100644 index 0000000..47f8e8e --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/SharedComponents.java @@ -0,0 +1,14 @@ +package com.bluebot.injection; + +/** + * Created by Clifton Craig on 4/13/17. + * Copyright GE 4/13/17 + */ + +interface SharedComponents { + boolean doesNotHave(Class aClass); + + void put(Class aClass, T instance); + + T get(Class aClass); +} diff --git a/app/src/main/res/layout/activity_blue_bot_ide.xml b/app/src/main/res/layout/activity_blue_bot_ide.xml new file mode 100644 index 0000000..60e0064 --- /dev/null +++ b/app/src/main/res/layout/activity_blue_bot_ide.xml @@ -0,0 +1,73 @@ + + + + + + + +