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/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0595d93 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +language: android +jdk: oraclejdk8 + +android: + components: + - tools + - platform-tools + - build-tools-25.0.0 + - android-25 + - extra-android-m2repository + - sys-img-armeabi-v7a-android-16 + +before_install: + - mkdir "$ANDROID_HOME/licenses" || true + - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" + - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" + +before_script: + - echo no | android create avd --force -n test -t android-16 --abi armeabi-v7a + - emulator -avd test -no-skin -no-audio -no-window & + - android-wait-for-emulator + - sleep 10 + - adb shell input keyevent 82 \ No newline at end of file diff --git a/README.md b/README.md index d42bfc7..61c27e7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ -# ![RC Car Logo](https://github.com/fouliex/AndroidRcCar/blob/master/resources/images/RobotCarLogo.jpg) Android RC Car(In Progress) +**BlueBot** + +A **Blue**tooth Android app that controls an Arduino based Ro**bot**. +# ![RC Car Logo](https://github.com/fouliex/AndroidRcCar/blob/master/resources/images/RobotCarLogo.jpg) Android BlueBot ## 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) + +[![Build Status](https://travis-ci.org/fouliex/BlueBot.svg?branch=master)](https://travis-ci.org/fouliex/BlueBot) diff --git a/app/build.gradle b/app/build.gradle index f0589e1..9030c2b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,9 +2,9 @@ apply plugin: 'com.android.application' android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.0" 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..24e7539 --- /dev/null +++ b/app/src/androidTest/java/com/bluebot/MainActivitySpec.java @@ -0,0 +1,56 @@ +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.Ignore; +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. + */ +@Ignore +@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..7b21b5b --- /dev/null +++ b/app/src/androidTest/java/com/bluebot/droid/ide/BlueBotIDESpec.java @@ -0,0 +1,132 @@ +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. + */ +@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..e8b305d --- /dev/null +++ b/app/src/main/java/com/bluebot/BlueBotApplication.java @@ -0,0 +1,54 @@ +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. + */ + +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/bluebot/BluetoothControl.java b/app/src/main/java/com/bluebot/BluetoothControl.java new file mode 100644 index 0000000..c01f1e3 --- /dev/null +++ b/app/src/main/java/com/bluebot/BluetoothControl.java @@ -0,0 +1,35 @@ +package com.bluebot; + +import android.content.Intent; + +/** + * Created by Clifton Craig on 6/30/17. + * Copyright GE 6/30/17 + */ + +public interface BluetoothControl { + void stopService(); + + boolean isBluetoothEnabled(); + + void enable(); + + boolean isServiceAvailable(); + + void setupService(); + + void startService(boolean deviceOther); + + void connect(Intent data); + + int getServiceState(); + + void disconnect(); + + + void setBluetoothConnectionListener(BluetoothControllerConnectionListener bluetoothControllerConnectionListener); + + void send(String command, boolean crLF); + + boolean isBluetoothAvailable(); +} diff --git a/app/src/main/java/com/bluebot/BluetoothControllerConnectionListener.java b/app/src/main/java/com/bluebot/BluetoothControllerConnectionListener.java new file mode 100644 index 0000000..eba7560 --- /dev/null +++ b/app/src/main/java/com/bluebot/BluetoothControllerConnectionListener.java @@ -0,0 +1,15 @@ +package com.bluebot; + +/** + * Created by Clifton Craig on 6/30/17. + * Copyright GE 6/30/17 + */ + +public interface BluetoothControllerConnectionListener { + + void onDeviceConnected(String name, String address); + + void onDeviceDisconnected(); + + void onDeviceConnectionFailed(); +} 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 64% 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..a633953 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,65 @@ -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.LOOKLEFT; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.LOOKRIGHT; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.LOOKSTRAIGHT; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.RIGHT; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.RIGHTBACK; +import static com.bluebot.runtime.bluetooth.BluetoothCodes.STOP; + +/** + * Created by George Fouche on 4/5/2017. + */ 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; + + //Servo + private boolean lookRight = false; + private boolean lookLeft = false; + private boolean lookStraight = false; + + + private BluetoothControl 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 +88,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 +102,19 @@ 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); + //Servo + findViewById(R.id.sensroRight).setOnTouchListener(this); + findViewById(R.id.sensorLeft).setOnTouchListener(this); + findViewById(R.id.sensorCenter).setOnTouchListener(this); + } - /** - * Set connection to phone listener - */ private void setConnectListener() { connect.setOnClickListener(new View.OnClickListener() { @Override @@ -117,27 +129,24 @@ public void onClick(View v) { }); } - /** - * Set Bluetooth Listener - */ private void setBluetoothListener() { - bluetooth.setBluetoothConnectionListener(new BluetoothSPP.BluetoothConnectionListener() { + bluetooth.setBluetoothConnectionListener(new BluetoothControllerConnectionListener() { @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 +170,17 @@ 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.sensroRight: + if (event.getAction() == MotionEvent.ACTION_DOWN) lookRight = true; + else if (event.getAction() == MotionEvent.ACTION_UP) lookRight = false; + break; + case R.id.sensorLeft: + if (event.getAction() == MotionEvent.ACTION_DOWN) lookLeft = true; + else if (event.getAction() == MotionEvent.ACTION_UP) lookLeft = false; + break; + case R.id.sensorCenter: + if (event.getAction() == MotionEvent.ACTION_DOWN)lookStraight = true; + else if (event.getAction() == MotionEvent.ACTION_UP) lookStraight = false; break; } @@ -188,12 +205,22 @@ private void command() { else send(STOP); } else if (left && !right) send(LEFT); else if (right) send(RIGHT); - else if(auto)send(AUTOPILOT); + else if (lookRight) send(LOOKRIGHT); + else if (lookLeft) send(LOOKLEFT); + else if (lookStraight)send(LOOKSTRAIGHT); else send(STOP); } private void send(String command) { - bluetooth.send(command,true); + bluetooth.send(command, true); + } + + public void setBluetooth(BluetoothControl bluetooth) { + this.bluetooth = bluetooth; + if (!bluetooth.isBluetoothAvailable()) { + Toast.makeText(getApplicationContext(), "Bluetooth is not available", Toast.LENGTH_SHORT).show(); + } + 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..ebf7c5c --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/BlueBotIDEActivityModule.java @@ -0,0 +1,44 @@ +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. + */ + +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..d68abd0 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/InjectionModule.java @@ -0,0 +1,11 @@ +package com.bluebot.injection; + +/** + * Created by Clifton Craig on 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..27219b9 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/Injector.java @@ -0,0 +1,46 @@ +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. + */ + +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..2750cf3 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/InjectorSharedComponents.java @@ -0,0 +1,26 @@ +package com.bluebot.injection; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Clifton Craig on 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..33a3263 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/MainActivityModule.java @@ -0,0 +1,39 @@ +package com.bluebot.injection; + +import android.content.Context; + +import com.bluebot.BluetoothControl; +import com.bluebot.MainActivity; +import com.bluebot.wrappers.BluetoothControlWrapper; + +import app.akexorcist.bluetotohspp.library.BluetoothSPP; + +/** + * Created by Clifton Craig on 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(lazyGetBluetoothControl()); + } + + private BluetoothControl lazyGetBluetoothControl() { + if(sharedComponents.doesNotHave(BluetoothSPP.class)) { + final BluetoothSPP bluetoothSPP = new BluetoothSPP(sharedComponents.get(Context.class)); + sharedComponents.put(BluetoothControl.class, new BluetoothControlWrapper(bluetoothSPP)); + } + return sharedComponents.get(BluetoothControl.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..56abfe9 --- /dev/null +++ b/app/src/main/java/com/bluebot/injection/SharedComponents.java @@ -0,0 +1,13 @@ +package com.bluebot.injection; + +/** + * Created by Clifton Craig on 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/java/com/bluebot/wrappers/BluetoothConnectionListenerWrapper.java b/app/src/main/java/com/bluebot/wrappers/BluetoothConnectionListenerWrapper.java new file mode 100644 index 0000000..5214d3e --- /dev/null +++ b/app/src/main/java/com/bluebot/wrappers/BluetoothConnectionListenerWrapper.java @@ -0,0 +1,32 @@ +package com.bluebot.wrappers; + +import com.bluebot.BluetoothControllerConnectionListener; + +import app.akexorcist.bluetotohspp.library.BluetoothSPP; + +/** + * Created by Clifton Craig on 7/6/17. + * Copyright GE 7/6/17 + */ +class BluetoothConnectionListenerWrapper implements BluetoothSPP.BluetoothConnectionListener { + private final BluetoothControllerConnectionListener bluetoothControllerConnectionListener; + + public BluetoothConnectionListenerWrapper(BluetoothControllerConnectionListener bluetoothControllerConnectionListener) { + this.bluetoothControllerConnectionListener = bluetoothControllerConnectionListener; + } + + @Override + public void onDeviceConnected(String name, String address) { + bluetoothControllerConnectionListener.onDeviceConnected(name, address); + } + + @Override + public void onDeviceDisconnected() { + bluetoothControllerConnectionListener.onDeviceDisconnected(); + } + + @Override + public void onDeviceConnectionFailed() { + bluetoothControllerConnectionListener.onDeviceConnectionFailed(); + } +} diff --git a/app/src/main/java/com/bluebot/wrappers/BluetoothControlWrapper.java b/app/src/main/java/com/bluebot/wrappers/BluetoothControlWrapper.java new file mode 100644 index 0000000..037995a --- /dev/null +++ b/app/src/main/java/com/bluebot/wrappers/BluetoothControlWrapper.java @@ -0,0 +1,82 @@ +package com.bluebot.wrappers; + +import android.content.Intent; + +import com.bluebot.BluetoothControl; +import com.bluebot.BluetoothControllerConnectionListener; + +import app.akexorcist.bluetotohspp.library.BluetoothSPP; + +/** + * Created by Clifton Craig on 6/30/17. + * Copyright GE 6/30/17 + */ + +public class BluetoothControlWrapper implements BluetoothControl { + private final BluetoothSPP bluetoothSPP; + + public BluetoothControlWrapper(BluetoothSPP bluetoothSPP) { + this.bluetoothSPP = bluetoothSPP; + } + + @Override + public boolean isBluetoothAvailable() { + return bluetoothSPP.isBluetoothAvailable(); + } + + @Override + public boolean isBluetoothEnabled() { + return bluetoothSPP.isBluetoothEnabled(); + } + + @Override + public boolean isServiceAvailable() { + return bluetoothSPP.isServiceAvailable(); + } + + @Override + public void setupService() { + bluetoothSPP.setupService(); + } + + @Override + public int getServiceState() { + return bluetoothSPP.getServiceState(); + } + + @Override + public void startService(boolean isAndroid) { + bluetoothSPP.startService(isAndroid); + } + + @Override + public void stopService() { + bluetoothSPP.stopService(); + } + + @Override + public void connect(Intent data) { + bluetoothSPP.connect(data); + } + + @Override + public void disconnect() { + bluetoothSPP.disconnect(); + } + + @Override + public void setBluetoothConnectionListener(final BluetoothControllerConnectionListener bluetoothControllerConnectionListener) { + bluetoothSPP.setBluetoothConnectionListener(new BluetoothConnectionListenerWrapper(bluetoothControllerConnectionListener)); + } + + @Override + public void enable() { + bluetoothSPP.enable(); + } + + @Override + public void send(String data, boolean CRLF) { + bluetoothSPP.send(data, CRLF); + } + +} 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 @@ + + + + + + + +