diff --git a/.gradle/3.3/taskArtifacts/fileSnapshots.bin b/.gradle/3.3/taskArtifacts/fileSnapshots.bin new file mode 100644 index 0000000..f898986 Binary files /dev/null and b/.gradle/3.3/taskArtifacts/fileSnapshots.bin differ diff --git a/.gradle/3.3/taskArtifacts/taskArtifacts.bin b/.gradle/3.3/taskArtifacts/taskArtifacts.bin new file mode 100644 index 0000000..b328e53 Binary files /dev/null and b/.gradle/3.3/taskArtifacts/taskArtifacts.bin differ diff --git a/.gradle/3.3/taskArtifacts/taskArtifacts.lock b/.gradle/3.3/taskArtifacts/taskArtifacts.lock new file mode 100644 index 0000000..2b0003c Binary files /dev/null and b/.gradle/3.3/taskArtifacts/taskArtifacts.lock differ diff --git a/.travis.yml b/.travis.yml index e59a348..d447b2c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,19 @@ +sudo: false + language: android +jdk: oraclejdk8 android: components: + # Auxiliary tools which have to be installed + - tools + - tools # see https://github.com/travis-ci/travis-ci/issues/6040#issuecomment-219367943 + - platform-tools + # The BuildTools version used - - build-tools-19.1.0 + - build-tools-25.0.3 # The SDK version used to compile the project + - android-25 - android-22 # Additional components @@ -17,5 +26,39 @@ android: - sys-img-armeabi-v7a-android-22 - sys-img-armeabi-v7a-android-17 +before_install: + # copy the licenses to the correct file on the Travis system + - pip install --user codecov #Install codecov + - cp -r licenses/ /usr/local/android-sdk/licenses/ + - cd Hestia + +install: + - bash gradlew tasks --all --stacktrace --info + +before_script: + - android update sdk --no-ui --filter build-tools-25.0.3,android-25,extra-android-m2repository + # Start emulator + - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a + - emulator -avd test -no-audio -no-window & + - android-wait-for-emulator + - adb shell input keyevent 82 & + script: - - bash Hestia/gradlew tasks --all + - bash gradlew app:connectedCheck + - bash gradlew app:createDebugCoverageReport + +after_failure: + # Prints any linting errors after failing + - cat $TRAVIS_BUILD_DIR/app/build/outputs/lint-results-debug.xml + +after_success: + - codecov + - sh set_tags.sh + +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ diff --git a/Hestia/app/build.gradle b/Hestia/app/build.gradle index e923b9f..4f5f71d 100644 --- a/Hestia/app/build.gradle +++ b/Hestia/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { applicationId "com.rugged.application.hestia" minSdkVersion 15 @@ -17,14 +17,26 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } + debug{ + testCoverageEnabled = true + } } } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') + androidTestCompile ('com.android.support.test.espresso:espresso-intents:2.2.2', { + exclude group: 'com.android.support',module: 'support-annotations' + }) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) + androidTestCompile ('com.android.support.test:runner:0.5', { + exclude group: 'com.android.support',module: 'support-annotations' + }) + androidTestCompile ('com.android.support.test:rules:0.5', { + exclude group: 'com.android.support',module: 'support-annotations' + }) compile 'com.android.support:appcompat-v7:25.3.1' testCompile 'junit:junit:4.12' compile 'com.android.support:recyclerview-v7:25.3.1' diff --git a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/ActivatorAndActivatorStateTest.java b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/ActivatorAndActivatorStateTest.java index 493dc0f..e0b66ab 100644 --- a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/ActivatorAndActivatorStateTest.java +++ b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/ActivatorAndActivatorStateTest.java @@ -1,29 +1,19 @@ package com.rugged.application.hestia; -import android.content.Context; -import android.provider.Settings; -import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; -import android.util.Log; -import org.junit.After; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.ArrayList; - import hestia.backend.Activator; import hestia.backend.ActivatorState; -import hestia.backend.BackendInteractor; -import hestia.backend.Device; import static org.junit.Assert.*; @RunWith(AndroidJUnit4.class) public class ActivatorAndActivatorStateTest { - private static final int DEFAULT_ID = 0; + private static final String DEFAULT_ID = "0"; private static final String DEFAULT_NAME = "TEST_ACTIVATOR"; private static ActivatorState floatActivatorState; private static ActivatorState boolActivatorState; @@ -34,10 +24,10 @@ public class ActivatorAndActivatorStateTest { @Before public void setUp(){ floatActivatorState = new ActivatorState(Float.valueOf("122"),"UNSIGNED_BYTE"); - testFloatActivator = new Activator(1,floatActivatorState,"TEST_SLIDER_255"); + testFloatActivator = new Activator("1",0,floatActivatorState,"TEST_SLIDER_255"); boolActivatorState = new ActivatorState(true,"TOGGLE"); - testBoolActivator = new Activator(1,boolActivatorState,"TEST_SWITCH"); + testBoolActivator = new Activator("1",0,boolActivatorState,"TEST_SWITCH"); } @Test @@ -49,6 +39,22 @@ public void activatorStateTest(){ boolean returnedBoolState = (boolean) boolActivatorState.getRawState(); assertEquals(true,returnedBoolState); assertEquals("TOGGLE",boolActivatorState.getType()); + + // Testing rawState getters and setters + boolean newBoolState = false; + boolActivatorState.setRawState(newBoolState); + assertEquals(newBoolState,boolActivatorState.getRawState()); + + float newFloatState = (float) 0.34578; + floatActivatorState.setRawState(newFloatState); + double allowedDelta = 0.00000005; + assertEquals(newFloatState,floatActivatorState.getRawState(),allowedDelta); + + // Testing type setter + String typeString = "HESTIA_SWITCH"; + boolActivatorState.setType(typeString); + assertEquals(typeString,boolActivatorState.getType()); + } @Test @@ -57,4 +63,35 @@ public void activatorConstructorTest(){ assertEquals(boolActivatorState,testBoolActivator.getState()); } + @Test + public void activatorGettersAndSettersTest(){ + // Testing getId, setId + assertEquals("1",testBoolActivator.getId()); + testBoolActivator.setId("0"); + assertEquals(DEFAULT_ID,testBoolActivator.getId()); + assertNotEquals(DEFAULT_ID,testFloatActivator.getId()); + + // Testing setState + assertNotEquals(boolActivatorState,testFloatActivator.getState()); + testFloatActivator.setState(boolActivatorState); + assertEquals(boolActivatorState,testFloatActivator.getState()); + + // Testing getName, setName + assertEquals("TEST_SWITCH",testBoolActivator.getName()); + testBoolActivator.setName(DEFAULT_NAME); + assertEquals(DEFAULT_NAME,testBoolActivator.getName()); + } + + @Test + public void activatorEqualsAndHashTest(){ + // Testing hashCodes + assertNotEquals(testBoolActivator.hashCode(),testFloatActivator.hashCode()); + assertEquals(testBoolActivator.hashCode(),testBoolActivator.hashCode()); + + // Testing equals method + assertTrue(testBoolActivator.equals(testBoolActivator)); + assertFalse(testBoolActivator.equals(testFloatActivator)); + } + + } diff --git a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/BackendInteractorTest.java b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/BackendInteractorTest.java index 01f4a9d..4e7ff66 100644 --- a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/BackendInteractorTest.java +++ b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/BackendInteractorTest.java @@ -1,7 +1,6 @@ package com.rugged.application.hestia; import android.content.Context; -import android.provider.Settings; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.util.Log; @@ -11,20 +10,20 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; - import java.util.ArrayList; - +import hestia.UI.DeviceListFragment; import hestia.backend.Activator; import hestia.backend.ActivatorState; import hestia.backend.BackendInteractor; import hestia.backend.Device; - +import hestia.backend.DevicesChangeListener; import static org.junit.Assert.*; @RunWith(AndroidJUnit4.class) public class BackendInteractorTest { - private static final int TEST_DEVICE_ID = 0; - private static final int TEST_ACTIVATOR_ID = 0; + private static final String TEST_DEVICE_ID = "1"; + + private static final String TEST_ACTIVATOR_ID = "0"; private String TAG = "ClientInteractionTest"; private static BackendInteractor backendInteractor; @@ -32,17 +31,19 @@ public class BackendInteractorTest { public static void runBeforeTests(){ backendInteractor = BackendInteractor.getInstance(); ActivatorState testState = new ActivatorState(false,"TOGGLE"); - Activator testButton = new Activator(0,testState,"testButton"); + Activator testButton = new Activator("0",0,testState,"testButton"); ArrayList arr = new ArrayList<>(); arr.add(testButton); - Device testDevice = new Device(0,"testDevice", "testing",arr); + Device testDevice = new Device("0","testDevice", "testing",arr); backendInteractor.addDevice(testDevice); + } @Before public void addTestDevice(){ ActivatorState testState = new ActivatorState(false,"TOGGLE"); - Activator testButton = new Activator(TEST_ACTIVATOR_ID,testState,"testButton"); + + Activator testButton = new Activator(TEST_ACTIVATOR_ID,0,testState,"testButton"); ArrayList arr = new ArrayList<>(); arr.add(testButton); Device testDevice = new Device(TEST_DEVICE_ID,"testDevice", "testing",arr); @@ -51,18 +52,17 @@ public void addTestDevice(){ @After public void removeTestDevice(){ - backendInteractor.deleteTestDevice(TEST_DEVICE_ID); + backendInteractor.deleteTestDevice(Integer.parseInt(TEST_DEVICE_ID)); } - @Test - public void testPackageName(){ + public void packageNameTest(){ Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.rugged.application.hestia", appContext.getPackageName()); } @Test - public void testGetDevices(){ + public void getDevicesTest(){ StringBuilder sb = new StringBuilder(); for(Device d : backendInteractor.getDevices()){ sb.append(d.toString()); @@ -73,26 +73,78 @@ public void testGetDevices(){ /* * Test of the singleton reference, two different references should refer to the same object. */ + @Test - public void testSingleton(){ + public void singletonTest(){ BackendInteractor copyOfInteractor = BackendInteractor.getInstance(); assertEquals(backendInteractor,copyOfInteractor); } + + @Test + public void ipTest(){ + String testIp = "192.168.0.1"; + backendInteractor.setIp(testIp); + assertEquals(testIp,backendInteractor.getIp()); + } + @Test - public void testSetActivatorState(){ + public void setActivatorStateTest(){ ArrayList testDeviceList = backendInteractor.getDevices(); - Device testDevice = testDeviceList.get(TEST_DEVICE_ID); - ActivatorState state = testDevice.getActivator(TEST_ACTIVATOR_ID).getState(); + Device testDevice = testDeviceList.get(Integer.parseInt(TEST_DEVICE_ID)); + ActivatorState state = testDevice.getActivators().get(Integer.parseInt(TEST_ACTIVATOR_ID)).getState(); boolean testState = (boolean)state.getRawState(); assertEquals(testState,false); - ActivatorState newState = new ActivatorState(true,"TOGGLE"); - backendInteractor.setActivatorState(TEST_DEVICE_ID,TEST_ACTIVATOR_ID,newState); - testDeviceList = backendInteractor.getDevices(); - testDevice = testDeviceList.get(TEST_DEVICE_ID); - assertEquals(testDevice.getActivator(TEST_ACTIVATOR_ID).getState().getRawState(),true); + state.setRawState(true); + + backendInteractor.setActivatorState(testDevice,testDevice.getActivators().get(Integer.parseInt(TEST_ACTIVATOR_ID)),state); + + Activator activator = backendInteractor.getDevices().get(Integer.parseInt(TEST_DEVICE_ID)).getActivators().get(Integer.parseInt(TEST_ACTIVATOR_ID)); + assertEquals(true,activator.getState().getRawState()); } + @Test + public void deleteDeviceTest(){ + Device temp = backendInteractor.getDevices().get(Integer.parseInt(TEST_DEVICE_ID)); + + // Removing a device + backendInteractor.deleteDevice(temp); + backendInteractor.clearDevices(); + + // The list should be empty, updating should leave it empty + backendInteractor.updateDevices(); + assertTrue(backendInteractor.getDevices().isEmpty()); + // Now adding a device, trying the same device twice + backendInteractor.addDevice(temp); + backendInteractor.addDevice(temp); + assertEquals(2,backendInteractor.getDevices().size()); + } + + @Test + public void setDevicesTest(){ + Device temp = backendInteractor.getDevices().get(Integer.parseInt(TEST_DEVICE_ID)); + ArrayList newDevices = new ArrayList<>(); + // Adding the same device three times + newDevices.add(temp); + newDevices.add(temp); + newDevices.add(temp); + + backendInteractor.setDevices(newDevices); + assertEquals(3,backendInteractor.getDevices().size()); + } + + @Test + public void listenerTest(){ + DevicesChangeListener l = new DeviceListFragment(); + + // Testing adding a listener + backendInteractor.addDevicesChangeListener(l); + assertEquals(l,backendInteractor.getListeners().get(0)); + + // Testing removing a listener + backendInteractor.removeDevicesChangeListener(l); + assertEquals(0,backendInteractor.getListeners().size()); + } } \ No newline at end of file diff --git a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/DeserializerTest.java b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/DeserializerTest.java new file mode 100644 index 0000000..d1a7a64 --- /dev/null +++ b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/DeserializerTest.java @@ -0,0 +1,78 @@ +package com.rugged.application.hestia; + + import android.support.test.runner.AndroidJUnit4; + import android.util.Log; + + import com.google.gson.Gson; + import com.google.gson.GsonBuilder; + import com.google.gson.JsonDeserializationContext; + import com.google.gson.JsonElement; + import com.google.gson.JsonObject; + import com.google.gson.JsonParseException; + import com.google.gson.JsonParser; + import com.google.gson.reflect.TypeToken; + + import org.junit.Before; + import org.junit.Test; + import org.junit.runner.RunWith; + + import java.io.BufferedReader; + import java.io.FileNotFoundException; + import java.io.FileReader; + import java.io.IOException; + import java.lang.reflect.Type; + + import static org.junit.Assert.*; + + import hestia.backend.Activator; + import hestia.backend.ActivatorDeserializer; + import hestia.backend.ActivatorState; + +@RunWith(AndroidJUnit4.class) +public class DeserializerTest { + + private static final String testBoolJSON = + "{\"state\": false,\"type\": \"bool\",\"name\": \"On/Off\",\"activatorId\": \"591853a1094c1d2bdcbe9f21\",\"rank\": 0}"; + + + private static String TAG = "DESERIALT"; + private static final String currentPath = "app/src/androidTest/java/com/rugged/application/hestia"; + + @Test + public void deserializeBoolActivatorTest(){ + + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(Activator.class, new ActivatorDeserializer()); + Gson gson = gsonBuilder.create(); + + JsonElement testBool = new JsonParser().parse(testBoolJSON); + Activator deserializedBool = gson.fromJson(testBool, Activator.class); + + + JsonObject testBoolJson = (JsonObject) testBool; + assertTrue(deserializedBool.getId().equals(testBoolJson.get("activatorId").getAsString())); + assertTrue(deserializedBool.getName().equals(testBoolJson.get("name").getAsString())); + + + } + + public String readJSONFile(String target){ + String everything = ""; + + try { + BufferedReader br = new BufferedReader(new FileReader(target)); + StringBuilder sb = new StringBuilder(); + String line = br.readLine(); + + while (line != null) { + sb.append(line); + line = br.readLine(); + } + everything = sb.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + return everything; + } + +} diff --git a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/DeviceTest.java b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/DeviceTest.java new file mode 100644 index 0000000..d8ff79d --- /dev/null +++ b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/DeviceTest.java @@ -0,0 +1,62 @@ +package com.rugged.application.hestia; + +import android.bluetooth.BluetoothClass; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +import hestia.backend.Activator; +import hestia.backend.ActivatorState; +import hestia.backend.Device; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertSame; +import static junit.framework.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + private Device tDev; + + private final String DEFAULT_DEV_ID = "12"; + + + @Before + public void createDevice(){ + ActivatorState testState = new ActivatorState<>(false,"TOGGLE"); + Activator testButton = new Activator("0",0,testState,"testButton"); + ArrayList arr = new ArrayList<>(); + arr.add(testButton); + + tDev = new Device(DEFAULT_DEV_ID,"testDevice","HESTIA_DEVICE",arr); + } + + + @Test + public void getterAndSetterTest(){ + assertEquals(DEFAULT_DEV_ID,tDev.getId()); + tDev.setId("2"); + assertEquals("2",tDev.getId()); + + assertEquals("testDevice",tDev.getName()); + tDev.setName("hestiaDevice"); + assertEquals("hestiaDevice",tDev.getName()); + + assertEquals("HESTIA_DEVICE",tDev.getType()); + tDev.setType("T_DEV"); + assertEquals("T_DEV", tDev.getType()); + + assertTrue(tDev.getSliders() == null); + ActivatorState testSliderState = new ActivatorState<>((float) 0.3,"SLIDER"); + ArrayList arr = new ArrayList<>(); + Activator testSlider = new Activator("0",0,testSliderState,"float"); + arr.add(testSlider); + tDev.setActivators(arr); + + } + +} diff --git a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/ExampleInstrumentedTest.java b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/ExampleInstrumentedTest.java deleted file mode 100644 index 65dde2a..0000000 --- a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.rugged.application.hestia; - -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.rugged.application.hestia", appContext.getPackageName()); - } -} diff --git a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/LoginTest.java b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/LoginTest.java new file mode 100644 index 0000000..8dcf5be --- /dev/null +++ b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/LoginTest.java @@ -0,0 +1,58 @@ +package com.rugged.application.hestia; + + +import android.support.test.espresso.intent.rule.IntentsTestRule; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import hestia.UI.LoginActivity; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; +import static android.support.test.espresso.action.ViewActions.typeText; + +import static android.support.test.espresso.intent.Intents.intended; +import static android.support.test.espresso.intent.matcher.ComponentNameMatchers.hasShortClassName; +import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static android.support.test.espresso.intent.matcher.IntentMatchers.toPackage; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.Matchers.allOf; + +@RunWith(AndroidJUnit4.class) +public class LoginTest { + private final String USERNAME = "admin"; + private final String PASSWORD = "password"; + private final String PACKAGE_NAME = "com.rugged.application.hestia"; + + + + /* Instantiate an IntentsTestRule object. */ + @Rule + public IntentsTestRule mIntentsRule = + new IntentsTestRule<>(LoginActivity.class); + + @Test + public void checkLoginLogout() { + + // Login with 'remember me' + onView(withId(R.id.username)) + .perform(typeText(USERNAME), closeSoftKeyboard()); + + onView(withId(R.id.password)) + .perform(typeText(PASSWORD), closeSoftKeyboard()); + + onView(withId(R.id.rememberButton)).perform(click()); + + onView(withId(R.id.loginButton)).perform(click()); + + intended(allOf( + hasComponent(hasShortClassName("hestia.UI.DeviceListActivity")), + toPackage(PACKAGE_NAME))); + + } +} diff --git a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/LogoutTest.java b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/LogoutTest.java new file mode 100644 index 0000000..9198ae4 --- /dev/null +++ b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/LogoutTest.java @@ -0,0 +1,45 @@ +package com.rugged.application.hestia; + +import android.support.test.espresso.intent.rule.IntentsTestRule; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import hestia.UI.DeviceListActivity; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.intent.Intents.intended; +import static android.support.test.espresso.intent.matcher.ComponentNameMatchers.hasShortClassName; +import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static android.support.test.espresso.intent.matcher.IntentMatchers.toPackage; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static com.rugged.application.hestia.R.id.context_menu; +import static org.hamcrest.Matchers.allOf; + +@RunWith(AndroidJUnit4.class) +public class LogoutTest { + private final String LOGOUT_TEXT = "Logout "; + private final String PACKAGE_NAME = "com.rugged.application.hestia"; + + + @Rule + public IntentsTestRule mIntentsRule = + new IntentsTestRule<>(DeviceListActivity.class); + + @Test + public void logoutTest(){ + // Logout + onView(withId(context_menu)).perform(click()); + + onView(withText(LOGOUT_TEXT)).perform(click()); + + intended(allOf( + hasComponent(hasShortClassName("hestia.UI.LoginActivity")), + toPackage(PACKAGE_NAME))); + } + +} diff --git a/Hestia/app/src/androidTest/java/com/rugged/application/hestia/SetIpTest.java b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/SetIpTest.java new file mode 100644 index 0000000..383a2ac --- /dev/null +++ b/Hestia/app/src/androidTest/java/com/rugged/application/hestia/SetIpTest.java @@ -0,0 +1,53 @@ +package com.rugged.application.hestia; + +import android.support.test.espresso.intent.rule.IntentsTestRule; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import hestia.UI.DeviceListActivity; +import hestia.backend.BackendInteractor; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.clearText; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; +import static android.support.test.espresso.action.ViewActions.typeText; +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.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static junit.framework.Assert.assertEquals; + +@RunWith(AndroidJUnit4.class) +public class SetIpTest { + private final String SET_IP_TEXT = "Set IP "; + private final String TEST_IP = "192.168.0.1"; + + @Rule + public IntentsTestRule mIntentsRule = + new IntentsTestRule<>(DeviceListActivity.class); + + @Before + public void checkDialog() { + onView(withId(R.id.context_menu)).perform(click()); + + onView(withText(SET_IP_TEXT)).perform(click()); + + onView(withText(R.string.ipDialogText)).check(matches(isDisplayed())); + } + + @Test + public void checkEnterIp(){ + onView(withId(R.id.ip)).perform(clearText(),typeText(TEST_IP), closeSoftKeyboard()); + + onView(withId(R.id.confirm_button)).perform(click()); + + assertEquals(BackendInteractor.getInstance().getIp(),TEST_IP); + } + + +} diff --git a/Hestia/app/src/main/ic_exit_to_app-web.png b/Hestia/app/src/main/ic_exit_to_app-web.png new file mode 100644 index 0000000..46ae689 Binary files /dev/null and b/Hestia/app/src/main/ic_exit_to_app-web.png differ diff --git a/Hestia/app/src/main/java/hestia/UI/AddDeviceDialog.java b/Hestia/app/src/main/java/hestia/UI/AddDeviceDialog.java index 1af6fc9..cbacc5c 100644 --- a/Hestia/app/src/main/java/hestia/UI/AddDeviceDialog.java +++ b/Hestia/app/src/main/java/hestia/UI/AddDeviceDialog.java @@ -5,8 +5,11 @@ import android.os.Bundle; import android.view.View; import android.view.Window; +import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; +import android.widget.Toast; + import com.rugged.application.hestia.R; import hestia.backend.BackendInteractor; @@ -27,7 +30,7 @@ public class AddDeviceDialog extends Dialog implements android.view.View.OnClick public AddDeviceDialog(Activity activity) { super(activity); this.context = activity; - this.backendInteractor = BackendInteractor.getInstance();; + this.backendInteractor = BackendInteractor.getInstance(); } @Override @@ -36,6 +39,8 @@ protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.add_device_dialog); organizationField = (EditText) findViewById(R.id.organization); + organizationField.requestFocus(); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); pluginField = (EditText) findViewById(R.id.pluginName); confirm = (Button) findViewById(R.id.confirm_button); cancel = (Button) findViewById(R.id.back_button); @@ -44,13 +49,16 @@ protected void onCreate(Bundle savedInstanceState) { } @Override - public void onClick(View v) { + public void onClick(View view) { String organization = organizationField.getText().toString(); String pluginName = pluginField.getText().toString(); - switch (v.getId()) { + switch (view.getId()) { case R.id.confirm_button: backendInteractor.addDevice(organization, pluginName, context); + //TODO give correct response from server after adding device + Toast.makeText(context, "Added device x. Server should let us what response " + + "it gave", Toast.LENGTH_SHORT).show(); break; case R.id.back_button: dismiss(); diff --git a/Hestia/app/src/main/java/hestia/UI/AddDeviceInfo.java b/Hestia/app/src/main/java/hestia/UI/AddDeviceInfo.java index 689f695..d61c1eb 100644 --- a/Hestia/app/src/main/java/hestia/UI/AddDeviceInfo.java +++ b/Hestia/app/src/main/java/hestia/UI/AddDeviceInfo.java @@ -3,16 +3,22 @@ import android.app.Activity; import android.app.Dialog; import android.os.Bundle; +import android.util.Log; import android.view.View; import android.view.Window; +import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; + +import com.google.gson.JsonObject; import com.rugged.application.hestia.R; import java.util.HashMap; -import hestia.backend.PostDeviceTask; + +import hestia.backend.BackendInteractor; +import hestia.backend.requests.PostRequest; /** * This class dynamically creates the fields for the required information. @@ -24,6 +30,11 @@ public class AddDeviceInfo extends Dialog implements android.view.View.OnClickListener { private HashMap fields; private Activity content; + private final String TAG = "AddDeviceInfo"; + private final String fixedFieldOrg = "organization"; + private final String fixedFieldPlugin = "plugin"; + private final String propReqInfo = "required_info"; + private static final String EMPTY_STRING=""; public AddDeviceInfo(Activity activity, HashMap fields) { super(activity); @@ -40,33 +51,35 @@ protected void onCreate(Bundle savedInstanceState) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); - final LinearLayout lm = (LinearLayout) findViewById(R.id.linearMain); + final LinearLayout mainLayout = (LinearLayout) findViewById(R.id.linearMain); int count = 0; for (String key : fields.keySet()) { - LinearLayout ll = new LinearLayout(content); + LinearLayout subLayout = new LinearLayout(content); // Add text TextView name = new TextView(content); name.setText(key); - ll.addView(name); + subLayout.addView(name); //Add field EditText field = createEditText(key, params , count); - ll.addView(field); + field.requestFocus(); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + subLayout.addView(field); - lm.addView(ll); + mainLayout.addView(subLayout); count++; } LinearLayout ll = generateButtons(params); - lm.addView(ll); + mainLayout.addView(ll); } private EditText createEditText(String key, LinearLayout.LayoutParams params, int count) { final EditText field = new EditText(content); field.setText(fields.get(key)); field.setId(count); - if (key.equals("organization")||key.equals("plugin")) { + if (key.equals(fixedFieldOrg)||key.equals(fixedFieldPlugin)) { field.setFocusable(false); field.setClickable(false); } @@ -79,18 +92,17 @@ private EditText createEditText(String key, LinearLayout.LayoutParams params, in public void onClick(View view) { switch (view.getId()) { case R.id.confirm_button: - HashMap h = getFieldValues(); - if(h==null) { - Toast.makeText(getContext(), "One or more empty values were entered." - , Toast.LENGTH_SHORT).show(); + JsonObject requiredInfo = this.getRequiredInfo(); + if(requiredInfo==null) { + Toast.makeText(getContext(), R.string.emptyValuesEntered, + Toast.LENGTH_SHORT).show(); break; } - new PostDeviceTask(h).execute(); - Toast.makeText(content, h.toString(), Toast.LENGTH_SHORT).show(); + BackendInteractor.getInstance().postDevice(requiredInfo); dismiss(); break; case R.id.back_button: - Toast.makeText(content, "Cancel", Toast.LENGTH_SHORT).show(); + Toast.makeText(content, R.string.cancel, Toast.LENGTH_SHORT).show(); dismiss(); break; default: @@ -98,29 +110,39 @@ public void onClick(View view) { } } - private HashMap getFieldValues() { - int i = 0; - for (String key : fields.keySet()) { - EditText field = (EditText) findViewById(i); - String value = field.getText().toString(); - if(value.equals("")){ + /** + * Creates a JSON object containing the relevant information in + * the "required_information" key. If any field is left empty, it will return a null. + * @return addDeviceJSON json object containing the information needed for adding a new device. + */ + private JsonObject getRequiredInfo() { + int count = 0; + JsonObject requiredInfo = new JsonObject(); + for(String key : this.fields.keySet()) { + EditText field = (EditText) findViewById(count); + String valueField = field.getText().toString(); + + if(EMPTY_STRING.equals(valueField)) { return null; } - fields.put(key, value); - i++; + this.fields.put(key, valueField); + String value = this.fields.get(key); + requiredInfo.addProperty(key, value); + count++; } - return fields; + JsonObject addDeviceJSON = new JsonObject(); + addDeviceJSON.add(propReqInfo, requiredInfo); + return addDeviceJSON; } private LinearLayout generateButtons(LinearLayout.LayoutParams params){ - // Create buttons. LinearLayout layout = new LinearLayout(content); final Button confirm = new Button(content); final Button cancel = new Button(content); confirm.setId(R.id.confirm_button); cancel.setId(R.id.back_button); - confirm.setText("Confirm"); - cancel.setText("Cancel"); + confirm.setText(R.string.confirm); + cancel.setText(R.string.cancel); confirm.setOnClickListener(this); cancel.setOnClickListener(this); layout.addView(confirm); diff --git a/Hestia/app/src/main/java/hestia/UI/DeviceBar.java b/Hestia/app/src/main/java/hestia/UI/DeviceBar.java index c2128d8..94520d5 100644 --- a/Hestia/app/src/main/java/hestia/UI/DeviceBar.java +++ b/Hestia/app/src/main/java/hestia/UI/DeviceBar.java @@ -2,8 +2,9 @@ import android.util.Log; import android.view.View; +import android.widget.Switch; +import android.widget.Toast; -import hestia.UIWidgets.HestiaSwitch; import hestia.backend.Device; /** @@ -27,15 +28,16 @@ public Device getDevice() { return device; } - public void setDevice(Device d) { - this.device = d; + public void setDevice(Device device) { + this.device = device; } - public void setLayout(View v, int layoutId, boolean state) { + public void setLayout(View view, int layoutId, boolean state) { + hestiaSwitch.addLayout(view, layoutId); + hestiaSwitch.getActivatorSwitch().setOnCheckedChangeListener(null); hestiaSwitch.getActivatorSwitch().setChecked(state); - Log.i(TAG, "Layout changed for: " + device.getName() + " And switch truth is: " + - hestiaSwitch.getActivatorSwitch().isChecked()); - hestiaSwitch.addLayout(v, layoutId); + hestiaSwitch.getActivatorSwitch().setOnCheckedChangeListener(hestiaSwitch); + } @Override @@ -43,7 +45,7 @@ public boolean equals(Object object) { boolean equal = false; if (object != null && object instanceof DeviceBar) { - if(this.device.getDeviceId() == ((DeviceBar) object).getDevice().getDeviceId()){ + if(this.device.getId() == ((DeviceBar) object).getDevice().getId()){ equal = true; } } diff --git a/Hestia/app/src/main/java/hestia/UI/DeviceListActivity.java b/Hestia/app/src/main/java/hestia/UI/DeviceListActivity.java index c981ba1..5fd0f93 100644 --- a/Hestia/app/src/main/java/hestia/UI/DeviceListActivity.java +++ b/Hestia/app/src/main/java/hestia/UI/DeviceListActivity.java @@ -3,6 +3,7 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.widget.Toast; import hestia.backend.BackendInteractor; @@ -12,14 +13,23 @@ */ public class DeviceListActivity extends SingleFragmentActivity { - private final static String TAG = "DeviceListActivity"; + private static final String HESTIA_IP = "HESTIA.IP"; + private static final String SERVER_IP = "IP_OF_SERVER"; @Override public void onCreate(Bundle savedInstanceState) { - BackendInteractor backendInteractor = BackendInteractor.getInstance(); - SharedPreferences prefs = getSharedPreferences("HESTIA.IP", 0); - backendInteractor.setIp(prefs.getString("IP_OF_SERVER", backendInteractor.getIp())); super.onCreate(savedInstanceState); + BackendInteractor backendInteractor = BackendInteractor.getInstance(); + SharedPreferences prefs = getSharedPreferences(HESTIA_IP, 0); + } + + /** + * When the app resumes, the list of devices is refreshed automatically using onResume. + */ + @Override + public void onResume(){ + super.onResume(); + BackendInteractor.getInstance().updateDevices(); } @Override @@ -41,7 +51,7 @@ protected void onDestroy() { private void storeIP(){ BackendInteractor backendInteractor = BackendInteractor.getInstance(); - SharedPreferences.Editor prefs = getSharedPreferences("HESTIA.IP", 0).edit(); - prefs.putString("IP_OF_SERVER", backendInteractor.getIp()).apply(); + SharedPreferences.Editor prefs = getSharedPreferences(HESTIA_IP, 0).edit(); + prefs.putString(SERVER_IP, backendInteractor.getIp()).apply(); } } diff --git a/Hestia/app/src/main/java/hestia/UI/DeviceListFragment.java b/Hestia/app/src/main/java/hestia/UI/DeviceListFragment.java index b66729f..eb3fc51 100644 --- a/Hestia/app/src/main/java/hestia/UI/DeviceListFragment.java +++ b/Hestia/app/src/main/java/hestia/UI/DeviceListFragment.java @@ -1,13 +1,17 @@ package hestia.UI; +import android.app.Activity; +import android.content.Context; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.Fragment; +import android.support.v4.widget.SwipeRefreshLayout; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AbsListView; import android.widget.ExpandableListView; -import hestia.UIWidgets.HestiaSwitch; import hestia.backend.Activator; import hestia.backend.BackendInteractor; import hestia.backend.Device; @@ -23,13 +27,14 @@ * @see DeviceListActivity */ public class DeviceListFragment extends Fragment implements DevicesChangeListener{ - - + private SwipeRefreshLayout swipeRefreshLayout; private ExpandableListAdapter listAdapter; private ExpandableListView expListView; private ArrayList> listDataChild; private BackendInteractor backendInteractor = BackendInteractor.getInstance(); - private FloatingActionButton fab; + private FloatingActionButton floatingActionButton; + private final static String TAG = "DeviceListFragment"; + private Activity surroundingActivity; /** * @@ -44,27 +49,61 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, View deviceListView = inflater.inflate(R.layout.fragment_device_list, container, false); createFloatingButton(deviceListView); + swipeRefreshLayout = (SwipeRefreshLayout) deviceListView.findViewById(R.id.swipe_refresh); + swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + swipeRefreshLayout.setRefreshing(true); + Log.i(TAG, "Currently refreshing"); + backendInteractor.updateDevices(); + Log.i(TAG, "Refresh stopped"); + swipeRefreshLayout.setRefreshing(false); + } + }); + listDataChild = new ArrayList<>(); expListView = (ExpandableListView) deviceListView.findViewById(R.id.lvExp); - listAdapter = new ExpandableListAdapter(listDataChild, getActivity()); + listAdapter = new ExpandableListAdapter(listDataChild, surroundingActivity); expListView.setAdapter(listAdapter); + expListView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView absListView, int i) { - backendInteractor.addDevicesChangeListener(this); + } + + @Override + public void onScroll(AbsListView absListView, int i, int i1, int i2) { + int topRowVerticalPosition = (absListView == null || + absListView.getChildCount() == 0) ? 0 : + absListView.getFirstVisiblePosition() == 0 ? + absListView.getChildAt(0).getTop() : - 1; + swipeRefreshLayout.setEnabled(topRowVerticalPosition >= 0); + } + }); + + backendInteractor.addDevicesChangeListener(this); populateUI(); return deviceListView; } + + + @Override + public void onAttach(Context context) { + super.onAttach(context); + + surroundingActivity = context instanceof Activity ? (Activity) context : null; + } + private void populateUI() { listDataChild = new ArrayList<>(); - - backendInteractor = BackendInteractor.getInstance(); ArrayList devices = backendInteractor.getDevices(); for (Device device : devices) { - Activator a = device.getActivator(0); - HestiaSwitch hestiaSwitch = new HestiaSwitch(device, a, getActivity()); + Activator activator = device.getToggle(); + HestiaSwitch hestiaSwitch = new HestiaSwitch(device, activator, surroundingActivity); DeviceBar bar = new DeviceBar(device, hestiaSwitch); if(!listDataChild.contains(bar)) { if (!typeExists(device)) { @@ -108,12 +147,11 @@ private int getDeviceType(Device device) { } private void createFloatingButton(View view) { - fab = (FloatingActionButton)view.findViewById(R.id.floating_action_button); - fab.setOnClickListener(new View.OnClickListener() { + floatingActionButton = (FloatingActionButton)view.findViewById(R.id.floating_action_button); + floatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - new AddDeviceDialog(getActivity()).show(); - populateUI(); + new AddDeviceDialog(surroundingActivity).show(); } }); } diff --git a/Hestia/app/src/main/java/hestia/UI/ExpandableListAdapter.java b/Hestia/app/src/main/java/hestia/UI/ExpandableListAdapter.java index a6393ac..d3fb0b5 100644 --- a/Hestia/app/src/main/java/hestia/UI/ExpandableListAdapter.java +++ b/Hestia/app/src/main/java/hestia/UI/ExpandableListAdapter.java @@ -10,6 +10,7 @@ import android.widget.BaseExpandableListAdapter; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; import hestia.backend.BackendInteractor; @@ -28,12 +29,12 @@ public class ExpandableListAdapter extends BaseExpandableListAdapter{ private ArrayList> listDataChild; private Context context; - private BackendInteractor c; + private BackendInteractor backendInteractor; public ExpandableListAdapter(ArrayList> listChildData, Context context) { this.listDataChild = listChildData; this.context = context; - this.c = BackendInteractor.getInstance(); + this.backendInteractor = BackendInteractor.getInstance(); } @Override @@ -52,9 +53,9 @@ public View getChildView(final int groupPosition, final int childPosition, final DeviceBar dBar = (DeviceBar) getChild(groupPosition, childPosition); if (convertView == null) { - LayoutInflater infalInflater = (LayoutInflater) context + LayoutInflater layoutInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = infalInflater.inflate(R.layout.child_list_item, null); + convertView = layoutInflater.inflate(R.layout.child_list_item, null); } TextView txtListChild = (TextView) convertView.findViewById(R.id.child_item_text); @@ -62,25 +63,36 @@ public View getChildView(final int groupPosition, final int childPosition, ImageView imageview = (ImageView) convertView.findViewById(R.id.imageview); - Boolean state = Boolean.parseBoolean(dBar.getDevice().getActivator(0).getState().toString()); + Boolean state = Boolean.parseBoolean(dBar.getDevice().getToggle().getState().getRawState() + .toString()); dBar.setLayout(convertView, R.id.light_switch,state); + convertView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + final Device device = ((DeviceBar) getChild(groupPosition, childPosition)).getDevice(); + if(device.getSliders()!=null) { + new SlideDialog(context, device).show(); + } + } + }); imageview.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - final Device d = ((DeviceBar) getChild(groupPosition, childPosition)).getDevice(); - PopupMenu popup = createPopupMenu(view,d); + final Device device = ((DeviceBar) getChild(groupPosition, childPosition)) + .getDevice(); + PopupMenu popup = createPopupMenu(view,device); popup.show(); popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { - case R.id.sliders: - new SlideDialog(context,d.getSliders(),d).show(); - break; + //case R.id.sliders: + // new SlideDialog(context, device).show(); + //break; case R.id.delete: - c.deleteDevice(d); + backendInteractor.deleteDevice(device); break; default: break; @@ -119,10 +131,10 @@ public View getGroupView(int groupPosition, boolean isExpanded, DeviceBar dBar = ((ArrayList) getGroup(groupPosition)).get(0); String headerTitle = dBar.getDevice().getType(); if (convertView == null) { - LayoutInflater infalInflater = (LayoutInflater) context + LayoutInflater layoutInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = infalInflater.inflate(R.layout.list_group, null); + convertView = layoutInflater.inflate(R.layout.list_group, null); } TextView lblListHeader = (TextView) convertView @@ -147,12 +159,12 @@ public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } - private PopupMenu createPopupMenu(View view, Device d){ + private PopupMenu createPopupMenu(View view, Device device){ PopupMenu popup = new PopupMenu(context, view); popup.getMenuInflater().inflate(R.menu.popup, popup.getMenu()); - if (d.getSliders()==null) { + if (device.getSliders()==null || device.getSliders() != null) { popup.getMenu().findItem(R.id.sliders).setEnabled(false); popup.getMenu().findItem(R.id.sliders).setVisible(false); } diff --git a/Hestia/app/src/main/java/hestia/UI/HestiaSwitch.java b/Hestia/app/src/main/java/hestia/UI/HestiaSwitch.java new file mode 100644 index 0000000..1841d4b --- /dev/null +++ b/Hestia/app/src/main/java/hestia/UI/HestiaSwitch.java @@ -0,0 +1,46 @@ +package hestia.UI; + +import android.content.Context; +import android.util.Log; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.Switch; +import android.widget.Toast; + +import hestia.backend.Activator; +import hestia.backend.ActivatorState; +import hestia.backend.BackendInteractor; +import hestia.backend.Device; + +public class HestiaSwitch implements CompoundButton.OnCheckedChangeListener { + private final static String TAG = "HestiaSwitch"; + private Device device; + private Activator activator; + private Switch activatorSwitch; + private BackendInteractor backendInteractor = BackendInteractor.getInstance(); + + public HestiaSwitch(Device device, Activator activator, Context context) { + this.device = device; + this.activator = activator; + activatorSwitch = new Switch(context); + activatorSwitch.setChecked(Boolean.valueOf(activator.getState().getRawState().toString())); + Log.i(TAG, "HestiaSwitch created, TOGGLE=" + activatorSwitch.isChecked()); + } + + public Switch getActivatorSwitch() { + return activatorSwitch; + } + + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean currentState) { + ActivatorState state = activator.getState(); + state.setRawState(currentState); + backendInteractor.setActivatorState(device, activator, state); + Log.i(TAG, "Sending a post to the server"); + } + + public void addLayout(View v, int layoutId) { + activatorSwitch = (Switch)v.findViewById(layoutId); + activatorSwitch.setOnCheckedChangeListener(this); + } +} diff --git a/Hestia/app/src/main/java/hestia/UI/IpDialog.java b/Hestia/app/src/main/java/hestia/UI/IpDialog.java index 2181d46..062eb8d 100644 --- a/Hestia/app/src/main/java/hestia/UI/IpDialog.java +++ b/Hestia/app/src/main/java/hestia/UI/IpDialog.java @@ -1,10 +1,12 @@ package hestia.UI; +import android.app.ActionBar; import android.app.Activity; import android.app.Dialog; import android.os.Bundle; import android.view.View; import android.view.Window; +import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; @@ -17,6 +19,7 @@ * This class represents the dialog screen with which the IP-address of the server is asked from the * user. */ + public class IpDialog extends Dialog implements android.view.View.OnClickListener{ private EditText ipField; private Button confirm,cancel; @@ -34,6 +37,9 @@ protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.ip_dialog); ipField = (EditText) findViewById(R.id.ip); + ipField.setText(this.backendInteractor.getIp()); + ipField.requestFocus(); + this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); confirm = (Button) findViewById(R.id.confirm_button); cancel = (Button) findViewById(R.id.back_button); confirm.setOnClickListener(this); @@ -41,14 +47,20 @@ protected void onCreate(Bundle savedInstanceState) { } @Override - public void onClick(View v) { + public void onClick(View view) { ip = ipField.getText().toString(); - switch (v.getId()) { + Toast.makeText(getContext(), "ip is: " + ip, Toast.LENGTH_SHORT).show(); + switch (view.getId()) { case R.id.confirm_button: if(ip!=null) { backendInteractor.setIp(ip); - Toast.makeText(getContext(),"IP Address set to: " + backendInteractor.getIp() + ":" + backendInteractor.updateDevices(); + Toast.makeText(getContext(),R.string.ipSetTo + backendInteractor.getIp() + ":" + backendInteractor.getPort(),Toast.LENGTH_SHORT).show(); + + //TODO give correct response from server after changing ip + Toast.makeText(getContext(), "Server returned message: + serverMessage", + Toast.LENGTH_SHORT).show(); } break; case R.id.back_button: diff --git a/Hestia/app/src/main/java/hestia/UI/LoginActivity.java b/Hestia/app/src/main/java/hestia/UI/LoginActivity.java index 68b6a34..617d27a 100644 --- a/Hestia/app/src/main/java/hestia/UI/LoginActivity.java +++ b/Hestia/app/src/main/java/hestia/UI/LoginActivity.java @@ -30,18 +30,24 @@ public class LoginActivity extends Activity { private TextView attemptsText; private int counter = 10; private String username,password; - public static final String LOGIN_PREFERENCES = "LoginPreferences"; + private final String LOGIN_PREFERENCES = "LoginPreferences"; + private final String intentExtra = "login"; + private final String correctLoginToast = "Correct, redirecting."; + private final String incorrectLoginToast = "Invalid credentials."; + private final String saveLoginString = "saveLogin"; + private final String prefsUser = "username"; + private final String prefsPass = "password"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); - Intent i = getIntent(); - String extra = i.getStringExtra("login"); + Intent fromIntent = getIntent(); + String extra = fromIntent.getStringExtra(intentExtra); loginPreferences = getSharedPreferences(LOGIN_PREFERENCES, MODE_PRIVATE); - Boolean saveLogin = loginPreferences.getBoolean("saveLogin", false); + Boolean saveLogin = loginPreferences.getBoolean(saveLoginString, false); if (saveLogin) { if(extra==null) { gotoMainActivity(); @@ -70,10 +76,10 @@ public void onClick(View v) { } else { clearSaveLogin(); } - showLoginToast("Correct, redirecting..."); + showLoginToast(correctLoginToast); gotoMainActivity(); }else{ - showLoginToast("Wrong credentials."); + showLoginToast(incorrectLoginToast); editLoginAttempts(); } } @@ -85,16 +91,16 @@ private boolean checkCredentials(String username,String password){ } private void gotoMainActivity(){ - Intent i = new Intent(LoginActivity.this, DeviceListActivity.class); - startActivity(i); + Intent toIntent = new Intent(LoginActivity.this, DeviceListActivity.class); + startActivity(toIntent); finish(); } private void setSaveLogin(String username, String password){ loginPrefsEditor = loginPreferences.edit(); - loginPrefsEditor.putBoolean("saveLogin", true); - loginPrefsEditor.putString("username", username); - loginPrefsEditor.putString("password", password); + loginPrefsEditor.putBoolean(saveLoginString, true); + loginPrefsEditor.putString(prefsUser, username); + loginPrefsEditor.putString(prefsPass, password); loginPrefsEditor.apply(); } diff --git a/Hestia/app/src/main/java/hestia/UI/SingleFragmentActivity.java b/Hestia/app/src/main/java/hestia/UI/SingleFragmentActivity.java index 97e90cb..3f1fab2 100644 --- a/Hestia/app/src/main/java/hestia/UI/SingleFragmentActivity.java +++ b/Hestia/app/src/main/java/hestia/UI/SingleFragmentActivity.java @@ -32,17 +32,20 @@ */ public abstract class SingleFragmentActivity extends AppCompatActivity implements OnMenuItemClickListener { - protected abstract Fragment createFragment(); - private static String TAG = "SingleFragmentActivity"; private ContextMenuDialogFragment mMenuDialogFragment; - private FragmentManager fm; + private FragmentManager fragmentManager; private List menuObjects; private BackendInteractor backendInteractor; - + private final String changeIpText = "Set IP "; + private final String logoutText = "Logout "; + private final String extraName = "login"; + private final String logoutExtraValue = "logout"; private final int IP = 1; private final int LOGOUT = 2; + protected abstract Fragment createFragment(); + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -53,28 +56,18 @@ public void onCreate(Bundle savedInstanceState) { showIpDialog(); } - final SwipeRefreshLayout swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh); - swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - swipeRefresh.setRefreshing(true); - Log.i(TAG, "Currently refreshing"); - backendInteractor.updateDevices(); - Log.i(TAG, "Refresh stopped"); - swipeRefresh.setRefreshing(false); - } - }); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayShowTitleEnabled(false); - fm = getSupportFragmentManager(); - Fragment fragment = fm.findFragmentById(R.id.fragment_container); + fragmentManager = getSupportFragmentManager(); + Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_container); if (fragment == null) { fragment = createFragment(); - fm.beginTransaction().add(R.id.fragment_container, fragment).commit(); + fragmentManager.beginTransaction().add(R.id.fragment_container, fragment).commit(); } menuObjects = getMenuObjects(); initMenuFragment(); @@ -96,12 +89,12 @@ private List getMenuObjects() { close.setResource(R.drawable.ic_action); objects.add(close); - MenuObject ip = new MenuObject("Set Ip"); - ip.setResource(R.drawable.ic_router_black_24dp); + MenuObject ip = new MenuObject(changeIpText); + ip.setResource(R.mipmap.ic_router); objects.add(ip); - MenuObject logout = new MenuObject("Logout"); - logout.setResource(R.drawable.ic_exit_to_app_black_24dp); + MenuObject logout = new MenuObject(logoutText); + logout.setResource(R.mipmap.ic_exit_to_app); objects.add(logout); return objects; @@ -117,8 +110,8 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.context_menu: - if (fm.findFragmentByTag(ContextMenuDialogFragment.TAG) == null) { - mMenuDialogFragment.show(fm, "ContextMenuDialogFragment"); + if (fragmentManager.findFragmentByTag(ContextMenuDialogFragment.TAG) == null) { + mMenuDialogFragment.show(fragmentManager, "ContextMenuDialogFragment"); } return true; @@ -129,21 +122,17 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onMenuItemClick(View clickedView, int position) { - switch (position) { - case IP: - showIpDialog(); - break; - case LOGOUT: - gotoLoginActivity(); - break; + if (position == IP){ + showIpDialog(); + } else if(position == LOGOUT) { + gotoLoginActivity(); } } private void gotoLoginActivity() { - Intent i = new Intent(SingleFragmentActivity.this, LoginActivity.class); - String s = "logout"; - i.putExtra("login", s); - startActivity(i); + Intent toIntent = new Intent(SingleFragmentActivity.this, LoginActivity.class); + toIntent.putExtra(extraName, logoutExtraValue); + startActivity(toIntent); finish(); } diff --git a/Hestia/app/src/main/java/hestia/UI/SlideDialog.java b/Hestia/app/src/main/java/hestia/UI/SlideDialog.java index 6bc904b..6acc1e2 100644 --- a/Hestia/app/src/main/java/hestia/UI/SlideDialog.java +++ b/Hestia/app/src/main/java/hestia/UI/SlideDialog.java @@ -5,38 +5,33 @@ import android.os.Bundle; import android.view.View; import android.view.Window; -import android.widget.Button; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; -import android.widget.Toast; - import com.rugged.application.hestia.R; - import java.util.ArrayList; - import hestia.backend.Activator; import hestia.backend.ActivatorState; import hestia.backend.BackendInteractor; import hestia.backend.Device; -/* -** This class handles the dialog which is opened if a Device has the 'slide' option. -** It loads all the slider activators, and sends the new state onRelease. +/** + * This class handles the dialog which is opened if a Device has the 'slide' option. + * It loads all the slider activators, and sends the new state onRelease. */ public class SlideDialog extends Dialog implements android.view.View.OnClickListener{ - private Device d; + private Device device; private ArrayList fields; private Context context; private BackendInteractor backendInteractor; - public SlideDialog(Context a, ArrayList fields, Device d) { - super(a); - this.context = a; + public SlideDialog(Context context, Device device) { + super(context); + this.context = context; this.backendInteractor = BackendInteractor.getInstance(); - this.fields = fields; - this.d = d; + this.device = device; + this.fields = device.getSliders(); } @Override @@ -46,84 +41,52 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.slide_dialog); int count = 0; - final LinearLayout lm = (LinearLayout) findViewById(R.id.linearMain); + final LinearLayout mainLayout = (LinearLayout) findViewById(R.id.linearMain); for (Activator activator : fields) { - LinearLayout ll = new LinearLayout(context); + LinearLayout subLayout = new LinearLayout(context); TextView name = new TextView(context); name.setText(activator.getName()); - ll.addView(name); + subLayout.addView(name); - Float currState = Float.parseFloat(activator.getState().toString()); + Float currState = Float.parseFloat(activator.getState().getRawState().toString()); SeekBar bar = createSeekBar(currState ,count, activator); - ll.addView(bar); + subLayout.addView(bar); - lm.addView(ll); + mainLayout.addView(subLayout); count++; } } - private SeekBar createSeekBar(float progress, int count, Activator a){ - final Activator act = a; + private SeekBar createSeekBar(float progress, int count, Activator activator){ + final Activator act = activator; SeekBar bar = new SeekBar(context); - final int max_int = Integer.MAX_VALUE; - bar.setMax(max_int); - bar.setProgress((int)(progress* max_int)); - bar.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,80)); + final int maxInt = 100; + bar.setMax(maxInt); + bar.setProgress((int)(progress * maxInt)); + bar.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 80)); bar.setId(count); bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - } + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {} @Override - public void onStartTrackingTouch(SeekBar seekBar) { - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) { - float value = (float)seekBar.getProgress()/max_int; + float value = (float)seekBar.getProgress()/maxInt; ActivatorState state = act.getState(); state.setRawState(value); - backendInteractor.setActivatorState(d,act.getId(),state); + backendInteractor.setActivatorState(device,act,state); } }); return bar; } @Override - public void onClick(View v) { - - switch (v.getId()) { - case R.id.confirm_button: - Toast.makeText(getContext(), "Leaving", Toast.LENGTH_SHORT).show(); - dismiss(); - break; - case R.id.back_button: - Toast.makeText(getContext(), "Cancel", Toast.LENGTH_SHORT).show(); - dismiss(); - break; - default: - break; - } + public void onClick(View view) { dismiss(); } - - private LinearLayout generateButtons(){ - LinearLayout ll = new LinearLayout(context); - final Button confirm = new Button(context); - final Button cancel = new Button(context); - confirm.setId(R.id.confirm_button); - cancel.setId(R.id.back_button); - confirm.setText("Confirm"); - cancel.setText("Cancel"); - confirm.setOnClickListener(this); - cancel.setOnClickListener(this); - ll.addView(confirm); - ll.addView(cancel); - return ll; - } } \ No newline at end of file diff --git a/Hestia/app/src/main/java/hestia/UIWidgets/HestiaSwitch.java b/Hestia/app/src/main/java/hestia/UIWidgets/HestiaSwitch.java deleted file mode 100644 index be43b59..0000000 --- a/Hestia/app/src/main/java/hestia/UIWidgets/HestiaSwitch.java +++ /dev/null @@ -1,79 +0,0 @@ -package hestia.UIWidgets; - -import android.content.Context; -import android.util.Log; -import android.view.View; -import android.widget.CompoundButton; -import android.widget.Switch; - -import hestia.backend.Activator; -import hestia.backend.ActivatorState; -import hestia.backend.BackendInteractor; -import hestia.backend.Device; - -public class HestiaSwitch implements UIWidget, CompoundButton.OnCheckedChangeListener { - private final static String TAG = "HestiaSwitch"; - private Device d; - private Activator a; - private Switch activatorSwitch; - private BackendInteractor backendInteractor; - private boolean check; - - public HestiaSwitch(Device d, Activator a, Context c) { - this.a = a; - Log.i(TAG, "HestiaSwitch created"); - activatorSwitch = new Switch(c); - setCheck(Boolean.parseBoolean(a.getState().toString())); - this.d = d; - this.backendInteractor = BackendInteractor.getInstance(); - } - - @Override - public Device getDevice() { - return d; - } - - @Override - public void setActivator(Activator a) { - this.a = a; - } - - @Override - public Activator getActivator() { - return a; - } - - public Switch getActivatorSwitch() { - return activatorSwitch; - } - - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - int activatorId = a.getId(); - - ActivatorState state = a.getState(); - if (b) { - state.setRawState(true); - backendInteractor.setActivatorState(d, activatorId, state); - setCheck(true); - Log.i(TAG, d.getName() + " set to true"); - } else { - state.setRawState(false); - setCheck(false); - backendInteractor.setActivatorState(d, activatorId, state); - Log.i(TAG, d.getName() + " set to false"); - } - } - - public void addLayout(View v, int layoutId) { - activatorSwitch = (Switch)v.findViewById(layoutId); - activatorSwitch.setOnCheckedChangeListener(null); - activatorSwitch.setChecked(check); - activatorSwitch.setOnCheckedChangeListener(this); - } - - private void setCheck(boolean b) { - check = b; - } - -} diff --git a/Hestia/app/src/main/java/hestia/UIWidgets/UIWidget.java b/Hestia/app/src/main/java/hestia/UIWidgets/UIWidget.java deleted file mode 100644 index 0522457..0000000 --- a/Hestia/app/src/main/java/hestia/UIWidgets/UIWidget.java +++ /dev/null @@ -1,12 +0,0 @@ -package hestia.UIWidgets; - -import hestia.backend.Activator; -import hestia.backend.Device; - -public interface UIWidget { - Device getDevice(); - void setActivator(Activator id); - - Activator getActivator(); - -} diff --git a/Hestia/app/src/main/java/hestia/backend/Activator.java b/Hestia/app/src/main/java/hestia/backend/Activator.java index 92e950e..999abdc 100644 --- a/Hestia/app/src/main/java/hestia/backend/Activator.java +++ b/Hestia/app/src/main/java/hestia/backend/Activator.java @@ -3,72 +3,59 @@ /** * This class represents a single activator on a device. A single device can have multiple activators. * The activator has an id so we can reference it on the server. - * Furthermore there is a string name and a string state type, - * which is currently used for casting the generic state variable to an actual type. + * Furthermore there is a string name and a string state name, + * which is currently used for casting the generic state variable to an actual name. * @see Device * @see ActivatorState */ public class Activator { - private int id; + private String activatorId; + private int rank; private ActivatorState state; private String name; /** * Creates an Activator with the specified id, state and name. - * @param id the id of the activator + * @param activatorId the id of the activator * @param state the current state of the activator, this is a wrapper around a generic type * @param name the name of the activator */ - public Activator(int id, ActivatorState state, String name) { - this.id = id; + public Activator(String activatorId, Integer rank, ActivatorState state, String name) { + this.activatorId = activatorId; + this.rank = rank; this.state = state; this.name = name; } - /** - * Returns the id of the activator. - * @return the id of the activator - */ - public int getId() { - return id; + public String getId() { + return activatorId; } - /** - * Replaces the current id of the activator with the specified one. - * @param id the new id of the activator - */ - public void setId(int id) { - this.id = id; + + public void setId(String activatorId) { + this.activatorId = activatorId; + } + + public Integer getRank() { + return rank; + } + + public void setRank(Integer rank) { + this.rank = rank; } - /** - * Returns the current state of the activator. - * @return the current state of the activator - */ public ActivatorState getState() { return state; } - /** - * Replaces the current state of the activator with the specified one. - * @param state the new state of the activator - */ public void setState(ActivatorState state) { this.state = state; } - /** - * Returns the name of the activator. - * @return the name of the activator - */ public String getName() { return name; } - /** - * Replaces the current name of the activator with the specified one. - * @param name the new name of the activator - */ public void setName(String name) { this.name = name; } @@ -76,21 +63,23 @@ public void setName(String name) { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof Activator)) return false; Activator activator = (Activator) o; - if (id != activator.id) return false; - if (!state.equals(activator.state)) return false; - return name.equals(activator.name); + if (getRank() != activator.getRank()) return false; + if (!activatorId.equals(activator.activatorId)) return false; + if (!getState().equals(activator.getState())) return false; + return getName().equals(activator.getName()); } @Override public int hashCode() { - int result = id; - result = 31 * result + state.hashCode(); - result = 31 * result + name.hashCode(); + int result = activatorId.hashCode(); + result = 31 * result + getRank(); + result = 31 * result + getState().hashCode(); + result = 31 * result + getName().hashCode(); return result; } diff --git a/Hestia/app/src/main/java/hestia/backend/ActivatorDeserializer.java b/Hestia/app/src/main/java/hestia/backend/ActivatorDeserializer.java index 424620a..28747c7 100644 --- a/Hestia/app/src/main/java/hestia/backend/ActivatorDeserializer.java +++ b/Hestia/app/src/main/java/hestia/backend/ActivatorDeserializer.java @@ -37,23 +37,18 @@ public Activator deserialize(JsonElement json, Type typeOfT, JsonDeserialization switch (stateType.toLowerCase()) { case "bool": - state = new ActivatorState(Boolean.parseBoolean(rawState),"TOGGLE"); + state = new ActivatorState(Boolean.parseBoolean(rawState),"bool"); break; - case "int": - state = new ActivatorState(Float.parseFloat(rawState),"SLIDER"); - break; - case "unsigned_int8" : - state = new ActivatorState(Float.parseFloat(rawState),"UNSIGNED_BYTE"); - break; - case "unsigned_int16" : - state = new ActivatorState(Float.parseFloat(rawState),"UNSIGNED_INT16"); + case "float" : + state = new ActivatorState(Float.parseFloat(rawState),"float"); break; default : break; } - int activatorId = jsonObject.get("activatorId").getAsInt(); + String activatorId = jsonObject.get("activatorId").getAsString(); + Integer rank = jsonObject.get("rank").getAsInt(); String name = jsonObject.get("name").getAsString(); - return new Activator(activatorId, state, name); + return new Activator(activatorId, rank, state, name); } } diff --git a/Hestia/app/src/main/java/hestia/backend/ActivatorState.java b/Hestia/app/src/main/java/hestia/backend/ActivatorState.java index 911febc..012f5a3 100644 --- a/Hestia/app/src/main/java/hestia/backend/ActivatorState.java +++ b/Hestia/app/src/main/java/hestia/backend/ActivatorState.java @@ -5,7 +5,7 @@ * inferred using a custom JSON deserializer. * @see ActivatorDeserializer * @param Type of the state of the activator. This can be a boolean (for a switch) or a float - * (for a slider) + * (for a slider), or something else, depending on the Activator. */ public class ActivatorState { @@ -58,4 +58,23 @@ public void setType(String type){ public String toString(){ return this.type + " - " + this.rawState.toString(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ActivatorState)) return false; + + ActivatorState that = (ActivatorState) o; + + if (!getRawState().equals(that.getRawState())) return false; + return getType().equals(that.getType()); + + } + + @Override + public int hashCode() { + int result = getRawState().hashCode(); + result = 31 * result + getType().hashCode(); + return result; + } } diff --git a/Hestia/app/src/main/java/hestia/backend/BackendInteractor.java b/Hestia/app/src/main/java/hestia/backend/BackendInteractor.java index 0b07a6f..c07f288 100644 --- a/Hestia/app/src/main/java/hestia/backend/BackendInteractor.java +++ b/Hestia/app/src/main/java/hestia/backend/BackendInteractor.java @@ -2,9 +2,14 @@ import android.app.Activity; import android.app.Application; -import android.os.AsyncTask; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import java.util.ArrayList; import java.util.concurrent.CopyOnWriteArrayList; +import hestia.backend.requests.DeleteRequest; +import hestia.backend.requests.GetDevicesRequest; +import hestia.backend.requests.GetPluginInformationRequest; +import hestia.backend.requests.PostRequest; /** * A singleton class which handles interaction between front and back-end. The facade pattern is @@ -19,13 +24,22 @@ public class BackendInteractor extends Application{ * We use a CopyOnWriteArrayList to avoid ConcurrentModificationExceptions if * a listener attempts to remove itself during event notification. */ - private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList listeners = + new CopyOnWriteArrayList<>(); private static BackendInteractor instance; private ArrayList devices = new ArrayList<>(); private final static String TAG = "BackendInteractor"; - private String ip = "145.97.183.6"; + + private String ip = "82.73.173.179"; + + + //PI: +// private String ip = "82.73.173.179"; +// private int port = 8022; + private int port = 8000; + /** * The empty constructor, which can not be accessed from the outside, * because we want a singleton behavior. @@ -35,7 +49,7 @@ private BackendInteractor(){} /** * Returns the single instance of BackendInteractor. * If there was no instance of this class created previously, - * then it will create one and return it. + * then it will create one and return it * @return the single instance of BackendInteractor */ public static BackendInteractor getInstance(){ @@ -46,126 +60,114 @@ public static BackendInteractor getInstance(){ } /** - * Deletes a device from the server by starting a RemoveDeviceTask, + * Deletes a device from the server by starting a DeleteRequest, * which will send a DELETE request to the server. * @param device the device to be deleted. - * @see RemoveDeviceTask + * @see DeleteRequest */ public void deleteDevice(Device device) { - new RemoveDeviceTask(device).execute(); + String id = device.getId(); + String path = this.getPath() + "devices/" + id; + new DeleteRequest(path).execute(); + this.updateDevices(); } /** - * Adds a device to the server, by starting a PluginInformationRetrieverTask, + * Adds a device to the server, by starting a GetPluginInformationRequest, * which will send a GET request to the server and, based on the data returned from - * the GET request, will create a POST request which will contain additional fields. + * the GET request, will do a POST request with additional information. * @param organisation the organization that has/manufactured the device. (e.g. Philips) * @param pluginName the name of the plugin the contains data of the device to be added * @param activity the current activity - * @see PluginInformationRetrieverTask + * @see GetPluginInformationRequest */ public void addDevice(String organisation, String pluginName, Activity activity) { String path = this.getPath() + "plugins/" + organisation + "/plugins/" + pluginName; - new PluginInformationRetrieverTask(path, activity).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - /** - * This overloaded version of addDevice is used exclusively for testing purposes. - */ - public void addDevice(Device device){ - devices.add(device); + new GetPluginInformationRequest(path, activity).execute(); } /** - * The deleteTestDevice method uses the same + * Executes a POST request, sending a jsonObject containing all information + * needed to add a device to the server. + * @param requiredInfo the jsonObject containing the information relevant to adding a new device. */ - public void deleteTestDevice(int deviceId){ - devices.remove(deviceId); + public void postDevice(JsonObject requiredInfo) { + String path = BackendInteractor.getInstance().getPath() + "devices/"; + new PostRequest(path, requiredInfo).execute(); + BackendInteractor.getInstance().updateDevices(); } /** - * Updates the current list of devices by running the DeviceListRetrieverTask, which + * Updates the current list of devices by running the GetDevicesRequest, which * will execute a GET request for the list of devices from the server. */ public void updateDevices(){ - new DeviceListRetrieverTask().execute(); + String devicesPath = this.getPath() + "devices/"; + new GetDevicesRequest(devicesPath).execute(); } - /** - * This method will return a list of devices, which is possibly empty. If this is the case, then - * an AsyncTask is started to fill the devices so that the devices will eventually be filled. - * @return a list of devices known to the server. - */ public ArrayList getDevices(){ - if(devices.isEmpty()){ - updateDevices(); - } return devices; } /** - * Attempts to replace the list of devices with the specified one. - * If the new list of devices contains different devices, it replaces the old list of devices - * with the new one and it will fire a change event. Otherwise, it will not do anything. + * Replaces the current list of devices with the specified one and will fire a change event. * @param devices the new list of devices */ public void setDevices(ArrayList devices) { - if(!this.devices.equals(devices)) { - this.devices = devices; - fireChangeEvent(); - } + this.devices = devices; + fireChangeEvent(); } /** * This method implements the HTTP POST method for changing the state of an activator on a * device as an AsyncTask. * @param device The target device for the post - * @param activatorId The target activator for the post - * @param newState The new state object to be used by the post - * @see StateModificationTask - */ - public void setActivatorState(Device device, int activatorId, ActivatorState newState){ - Activator activator = device.getActivators().get(activatorId); - activator.setState(newState); - new StateModificationTask(device.getDeviceId(),activatorId,newState).execute(); - } - - public void setActivatorState(int deviceId, int activatorId, ActivatorState newState){ - Activator activator = devices.get(deviceId).getActivators().get(activatorId); - activator.setState(newState); - new StateModificationTask(deviceId,activatorId,newState).execute(); + * @param activator The target activator for the post + * @param newActivatorState The new state object to be used by the post + * @see PostRequest + */ + public void setActivatorState(Device device, Activator activator, ActivatorState newActivatorState){ + activator.setState(newActivatorState); + String deviceId = device.getId(); + String activatorId = activator.getId(); + String path = this.getPath() + "devices/" + deviceId + "/activators/" + activatorId; + JsonObject newState = new JsonObject(); + JsonPrimitive newStateValue = this.getNewStateValue(newActivatorState); + newState.add("state", newStateValue); + new PostRequest(path, newState).execute(); + } + + /** + * Creates a JsonPrimitive containing the raw value of the new state. + * The raw state can be either a Boolean or a Number. In case the state is something else, + * it will return a String. + * @param newActivatorState the new state of the device + * @return JsonPrimitive containing the rawValue + */ + private JsonPrimitive getNewStateValue(ActivatorState newActivatorState) { + switch (newActivatorState.getType().toLowerCase()) { + case "bool": + return new JsonPrimitive(Boolean.valueOf(String.valueOf(newActivatorState.getRawState()))); + case "float": + return new JsonPrimitive(Float.valueOf(String.valueOf(newActivatorState.getRawState()))); + default: + return new JsonPrimitive(String.valueOf(newActivatorState.getRawState())); + } } - /** - * Returns the IP of the server. - * @return the IP of the server - */ public String getIp(){ return this.ip; } - /** - * Replaces the IP of the server with the specified one. - * This is will also cause the list of devices to be updated. - * @param ip the IP of the server - */ public void setIp(String ip){ this.ip = ip; - updateDevices(); } - /** - * Returns the port number of the server. - * @return the port number of the server - */ public int getPort(){ return port; } - /** - * Replaces the port number of the server with the specified one. - * @param port the port number of the server - */ public void setPort(int port){ this.port = port; } @@ -205,4 +207,23 @@ public void addDevicesChangeListener(DevicesChangeListener l) { public void removeDevicesChangeListener(DevicesChangeListener l) { this.listeners.remove(l); } + + public CopyOnWriteArrayList getListeners(){ + return this.listeners; + } + + /** + * This overloaded version of addDevice is used exclusively for testing purposes. + */ + public void addDevice(Device device){ + devices.add(device); + } + + public void deleteTestDevice(int deviceId) { + devices.remove(deviceId); + } + + public void clearDevices(){ + devices = new ArrayList<>(); + } } diff --git a/Hestia/app/src/main/java/hestia/backend/Device.java b/Hestia/app/src/main/java/hestia/backend/Device.java index 7f6c6d3..bce87c6 100644 --- a/Hestia/app/src/main/java/hestia/backend/Device.java +++ b/Hestia/app/src/main/java/hestia/backend/Device.java @@ -9,88 +9,62 @@ * type of the device so a GUI can be generated with the right icons at the correct location. *

* Finally, there is a list of activators. These activators represent all the actions which can - * be performed remotely on the device. An activator can be for instance an On/Off switch, or - * an intensity slider. + * be performed remotely on the device. An activator can be, for instance, + * an On/Off switch (Toggle), or an intensity slider. *

* @see Activator */ public class Device { - private int deviceId; + private String deviceId; private String name; private String type; private ArrayList activators; - public Device(int deviceId, String name, String type, ArrayList activator) { + public Device(String deviceId, String name, String type, ArrayList activator) { this.deviceId = deviceId; this.name = name; this.type = type; this.activators = activator; } - /** - * Gets the deviceId. - * @return the remote deviceId - */ - public int getDeviceId() { + public String getId() { return deviceId; } - /** - * Sets the local deviceId. - * @param deviceId the Id to be set - */ - public void setDeviceId(int deviceId) { + public void setId(String deviceId) { this.deviceId = deviceId; } - /** - * Gets the remote device name. - * @return the name of the device as stored on the server - */ public String getName() { return name; } - /** - * Sets the local name of the device. - * @param name the local name of the device - */ public void setName(String name) { this.name = name; } - /** - * Gets the type of the device. - * @return a string with the type of the device - */ public String getType() { return type; } - /** - * Sets the local type of the device. - * @param type the new local type - */ public void setType(String type) { this.type = type; } /** - * Returns an activator based on its Id. - * @param activatorId the Id of the activator - * @return the activator with the specified Id - */ - public Activator getActivator(int activatorId){ - return activators.get(activatorId); - } - - /** - * Gets the complete list of activators. - * @return the list of activators + * Returns the main toggle activator, which has the rank 0. + * @return the toggle activator. */ - public ArrayList getActivators() { - return activators; + public Activator getToggle() { + Activator toggle = null; + for(Activator activator : activators) { + Integer rank = activator.getRank(); + if(rank == 0) { + toggle = activator; + } + } + return toggle; } /** @@ -99,16 +73,24 @@ public ArrayList getActivators() { * @return the activators if the array is not empty, null otherwise */ public ArrayList getSliders() { - ArrayList sliders = new ArrayList(); - for(Activator a : activators){ - String type = a.getState().getType(); - if(type.equals("SLIDER")||type.equals("UNSIGNED_BYTE")||type.equals("UNSIGNED_INT16")){ - sliders.add(a); + ArrayList sliders = new ArrayList<>(); + for(Activator activator : activators){ + String type = activator.getState().getType(); + if(type.equals("float")){ + sliders.add(activator); } } return (sliders.isEmpty() ? null : sliders); } + /** + * Gets the complete list of activators. + * @return the list of activators + */ + public ArrayList getActivators() { + return activators; + } + /** * Sets the local activators to a different list. * @param activators the list of activators which will be set @@ -124,23 +106,23 @@ public String toString(){ @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof Device)) return false; Device device = (Device) o; - if (deviceId != device.deviceId) return false; - if (!name.equals(device.name)) return false; - if (!type.equals(device.type)) return false; - return activators.equals(device.activators); + if (!getId().equals(device.getId())) return false; + if (!getName().equals(device.getName())) return false; + if (!getType().equals(device.getType())) return false; + return getActivators().equals(device.getActivators()); } @Override public int hashCode() { - int result = deviceId; - result = 31 * result + name.hashCode(); - result = 31 * result + type.hashCode(); - result = 31 * result + activators.hashCode(); + int result = getId().hashCode(); + result = 31 * result + getName().hashCode(); + result = 31 * result + getType().hashCode(); + result = 31 * result + getActivators().hashCode(); return result; } } diff --git a/Hestia/app/src/main/java/hestia/backend/DeviceListRetrieverTask.java b/Hestia/app/src/main/java/hestia/backend/DeviceListRetrieverTask.java deleted file mode 100644 index adb9a86..0000000 --- a/Hestia/app/src/main/java/hestia/backend/DeviceListRetrieverTask.java +++ /dev/null @@ -1,104 +0,0 @@ -package hestia.backend; - -import android.os.AsyncTask; -import android.util.Log; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Type; -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.util.ArrayList; - -/** - * This performs the task of getting the devices, by sending a GET request to the server. - * It runs in the background as an AsyncTask - * @see android.os.AsyncTask - */ - -public class DeviceListRetrieverTask extends AsyncTask> { - private static final String TAG = "DeviceListRetrieverTask"; - private BackendInteractor backendInteractor; - - /** - * Creates a new DeviceListRetrieverTask. - */ - public DeviceListRetrieverTask() { - this.backendInteractor = BackendInteractor.getInstance(); - } - - /** - * This method runs in the background of the app looking for the devices. - * @return an ArrayList containing the devices known to the server - */ - @Override - protected ArrayList doInBackground(Void... voids) { - String devicesPath = this.backendInteractor.getPath() + "devices/"; - URL url = null; - HttpURLConnection urlConnection = null; - ArrayList devices = new ArrayList<>(); - try { - url = new URL(devicesPath); - urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setReadTimeout(2000); - urlConnection.setConnectTimeout(2000); - urlConnection.connect(); - InputStream input = new BufferedInputStream(urlConnection.getInputStream()); - devices = readStream(input); - } catch (SocketTimeoutException e) { - Log.e(TAG, "SocketTimeoutException"); - Log.e(TAG, e.toString()); - } catch (ConnectException e) { - Log.e(TAG, "ConnectExeption"); - Log.e(TAG, e.toString()); - } catch (IOException e) { - Log.e(TAG, e.toString()); - } finally { - if (urlConnection != null) { - urlConnection.disconnect(); - } - } - return devices; - } - - /** - * When the task is done, if the list of devices is not NULL, then the current - * list of devices is replaced by the new one. - * @param devices the new list of devices - */ - @Override - public void onPostExecute(ArrayList devices) { - if(devices != null) { - backendInteractor.setDevices(devices); - } else { - Log.e(TAG, "DEVICES ARRAY WAS ABOUT TO BE SET TO NULL"); - } - } - - /** - * Retrieves the list of devices from the server, once the HTTP connection was established - * with the server. - * @param is the input stream which will accept the data coming from the server. - * @return the list of devices from the server - * @throws IOException if the list cannot be retrieved - */ - private ArrayList readStream(InputStream is) throws IOException { - GsonBuilder gsonBuilder = new GsonBuilder(); - gsonBuilder.registerTypeAdapter(Activator.class, new ActivatorDeserializer()); - Gson gson = gsonBuilder.create(); - - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - Type deviceListType= new TypeToken>() {}.getType(); - ArrayList responseDev = gson.fromJson(gson.newJsonReader(reader), deviceListType); - Log.i(TAG, "ResponseDev: " + (responseDev != null ? responseDev.toString() : "NULL")); - reader.close(); - return responseDev; - } -} diff --git a/Hestia/app/src/main/java/hestia/backend/PluginInformationRetrieverTask.java b/Hestia/app/src/main/java/hestia/backend/PluginInformationRetrieverTask.java deleted file mode 100644 index d4cc21b..0000000 --- a/Hestia/app/src/main/java/hestia/backend/PluginInformationRetrieverTask.java +++ /dev/null @@ -1,111 +0,0 @@ -package hestia.backend; - - -import java.net.ConnectException; -import java.net.SocketTimeoutException; -import java.util.HashMap; -import android.app.Activity; -import android.os.AsyncTask; -import android.util.Log; -import android.widget.Toast; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Type; -import java.net.HttpURLConnection; -import java.net.URL; -import hestia.UI.AddDeviceInfo; - - -/** - * Subclass of the AsyncTask, it is used to start a task that will send a GET request - * for the specified plugin to the server. - */ - -public class PluginInformationRetrieverTask extends AsyncTask> { - private static final String TAG = "PluginRetrieverTask"; - private String pluginPath; - private Activity a; - - /** - * Creates an instance of PluginInformationRetrieverTask, storing the pluginPath and - * the current activity. - * @param pluginPath the pluginPath to the required plugin - * @param a the current activity - */ - public PluginInformationRetrieverTask(String pluginPath, Activity a) { - this.pluginPath = pluginPath; - this.a = a; - } - - /** - * This method runs in a separate background thread. It establishes a connection to the server - * and then calls the readStream method to obtain the actual plugin map - * @return the hashmap containing the fields which are required for the plugin. - */ - @Override - public HashMap doInBackground(Void... params) { - URL url = null; - HttpURLConnection urlConnection = null; - HashMap plugin = null; - try { - url = new URL(this.pluginPath); - urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setReadTimeout(2000); - urlConnection.setConnectTimeout(2000); - urlConnection.connect(); - InputStream input = new BufferedInputStream(urlConnection.getInputStream()); - plugin = readStream(input); - } catch (SocketTimeoutException e) { - Log.e(TAG, "SocketTimeoutException"); - Log.e(TAG, e.toString()); - } catch (ConnectException e) { - Log.e(TAG, "ConnectException"); - Log.e(TAG, e.toString()); - } catch (IOException e) { - Log.e(TAG, e.toString()); - } finally { - if(urlConnection != null) { - urlConnection.disconnect(); - } - } - return plugin; - } - - /** - * At the end of the task, open a window that process the plugin's data. - * If the plugin is NULL, show a Toast to the user. - * @see AddDeviceInfo - */ - @Override - public void onPostExecute(HashMap plugin) { - if(plugin != null) { - new AddDeviceInfo(a, plugin).show(); - } else { - Toast.makeText(a, "Wrong Input", Toast.LENGTH_SHORT).show(); - } - } - - /** - * Reads the data of the plugin sent by the Server. - * @param is the input stream which accepts data from the Server - * @return the plugin from the server - * @throws IOException if the data could not be received - */ - private HashMap readStream(InputStream is) throws IOException { - GsonBuilder gsonBuilder = new GsonBuilder(); - Gson gson = gsonBuilder.create(); - - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - Type pluginType= new TypeToken>() {}.getType(); - HashMap plugin = gson.fromJson(gson.newJsonReader(reader), pluginType); - Log.i(TAG, plugin.toString()); - reader.close(); - return plugin; - } -} diff --git a/Hestia/app/src/main/java/hestia/backend/PostDeviceTask.java b/Hestia/app/src/main/java/hestia/backend/PostDeviceTask.java deleted file mode 100644 index e75039d..0000000 --- a/Hestia/app/src/main/java/hestia/backend/PostDeviceTask.java +++ /dev/null @@ -1,103 +0,0 @@ -package hestia.backend; - -import android.os.AsyncTask; -import android.util.Log; -import com.google.gson.JsonObject; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.util.HashMap; - -/** - * Sends a POST request to the server with a JSON object containing - * the data of the device to be added. Initially, the data is stored in a HashMap, - * which will be converted in a JSON object. - */ - -public class PostDeviceTask extends AsyncTask { - - private final String TAG = "PostDeviceTask"; - private HashMap deviceHashMap; - private BackendInteractor backendInteractor; - - /** - * Creates an instance of the PostDeviceTask class with the HashMap. - * @param deviceHashMap the device to be added - */ - public PostDeviceTask(HashMap deviceHashMap) { - this.deviceHashMap = deviceHashMap; - this.backendInteractor = BackendInteractor.getInstance(); - } - - /** - * Runs as a thread in the background. Tries to establish a connection with the server and then - * invokes the writeStream method to perform the actual POST. - * @return the HTTP response code of the POST. - */ - @Override - protected Integer doInBackground(Void... params) { - String postPath = this.backendInteractor.getPath() + "devices/"; - Integer response = null; - URL url = null; - HttpURLConnection urlConnection = null; - try { - url = new URL(postPath); - urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setReadTimeout(2000); - urlConnection.setConnectTimeout(2000); - urlConnection.setRequestMethod("POST"); - urlConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); - urlConnection.setDoOutput(true); - urlConnection.connect(); - OutputStream deviceOutputStream = urlConnection.getOutputStream(); - writeStream(deviceOutputStream); - response = urlConnection.getResponseCode(); - Log.i(TAG, "Response code: " + response); - } catch (SocketTimeoutException e) { - Log.e(TAG, "SocketTimeoutException"); - Log.e(TAG, e.toString()); - } catch (ConnectException e) { - Log.e(TAG, "ConnectExeption"); - Log.e(TAG, e.toString()); - } catch (IOException e) { - Log.e(TAG, "Connection failed: could not realize the POST request for adding a new device"); - Log.e(TAG, e.toString()); - } finally { - if(urlConnection != null) { - urlConnection.disconnect(); - } - } - return response; - } - - /** - * At the end of the task, updates the list of deviecs. - * @param result holds the response code of the POST request - */ - @Override - protected void onPostExecute(Integer result) { - backendInteractor.updateDevices(); - } - - /** - * Write the JSON for the new device to the output stream, which is sent over the urlConnection. - */ - private void writeStream(OutputStream os) throws IOException { - JsonObject requiredInfo = new JsonObject(); - for(String key : this.deviceHashMap.keySet()) { - String value = this.deviceHashMap.get(key); - requiredInfo.addProperty(key, value); - } - JsonObject json = new JsonObject(); - json.addProperty("required_info", requiredInfo.toString()); - Log.i(TAG, "JSON created" + json.toString()); - OutputStreamWriter outputStreamWriter = new OutputStreamWriter(os); - outputStreamWriter.write(json.toString()); - outputStreamWriter.flush(); - outputStreamWriter.close(); - } -} diff --git a/Hestia/app/src/main/java/hestia/backend/RemoveDeviceTask.java b/Hestia/app/src/main/java/hestia/backend/RemoveDeviceTask.java deleted file mode 100644 index ff3d73d..0000000 --- a/Hestia/app/src/main/java/hestia/backend/RemoveDeviceTask.java +++ /dev/null @@ -1,77 +0,0 @@ -package hestia.backend; - -import android.os.AsyncTask; -import android.util.Log; -import java.io.IOException; -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.SocketTimeoutException; -import java.net.URL; - -/** - * This class will send a DELETE request to the Server in order to delete the target device. - */ - -public class RemoveDeviceTask extends AsyncTask { - private final String TAG = "RemoveDeviceTask"; - private Device device; - private BackendInteractor backendInteractor; - - /** - * Creates an instance of the RemoveDeviceTask class with the device to be removed. - * @param device the device to be removed. - */ - public RemoveDeviceTask(Device device) { - this.device = device; - this.backendInteractor = BackendInteractor.getInstance(); - } - - /** - * Send the DELETE request to the server - * @param params parameters used for the background activity. - * @return the response code of the DELETE request - */ - @Override - public Integer doInBackground(Void... params) { - // Update the general path to match the one for the device to be deleted. - int id = this.device.getDeviceId(); - String path = this.backendInteractor.getPath() + "devices/" + id; - - Integer responseCode = null; - HttpURLConnection urlConnection = null; - try { - URL url = new URL(path); - urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setReadTimeout(2000); - urlConnection.setConnectTimeout(2000); - urlConnection.setRequestMethod("DELETE"); - urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - urlConnection.setRequestProperty("charset", "utf-8"); - urlConnection.connect(); - responseCode = urlConnection.getResponseCode(); - } catch (SocketTimeoutException e) { - Log.e(TAG, "SocketTimeoutException"); - Log.e(TAG, e.toString()); - } catch (ConnectException e) { - Log.e(TAG, "ConnectExeption"); - Log.e(TAG, e.toString()); - } catch (IOException e) { - Log.e(TAG, "Connection failed: could not realize the DELETE request"); - Log.e(TAG, e.toString()); - } finally { - if (urlConnection != null) { - urlConnection.disconnect(); - } - } - return responseCode; - } - - /** - * Once the task is finished, it updates the list of devices. - * @param result the response code - */ - @Override - protected void onPostExecute(Integer result) { - backendInteractor.updateDevices(); - } -} diff --git a/Hestia/app/src/main/java/hestia/backend/StateModificationTask.java b/Hestia/app/src/main/java/hestia/backend/StateModificationTask.java deleted file mode 100644 index 5f1cbcf..0000000 --- a/Hestia/app/src/main/java/hestia/backend/StateModificationTask.java +++ /dev/null @@ -1,92 +0,0 @@ -package hestia.backend; - -import android.os.AsyncTask; -import android.util.Log; - -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.SocketTimeoutException; -import java.net.URL; - -/** - * Subclass of AsyncTask which is used to send a POST request to the server, containing data - * about the device's activator, whose state is changed. - */ - -public class StateModificationTask extends AsyncTask { - private String TAG = "StateModificationTask"; - private BackendInteractor backendInteractor; - private int deviceId; - private int activatorId; - private ActivatorState newState; - - /** - * Creates an instance of the StateModificationTask class, storing the device's Id, - * the activator's id and the new state of the activator. - * @param deviceId the id of the device - * @param activatorId the id of the activator - * @param newState the new state - */ - public StateModificationTask(int deviceId, int activatorId, ActivatorState newState) { - this.deviceId = deviceId; - this.activatorId = activatorId; - this.newState = newState; - this.backendInteractor = BackendInteractor.getInstance(); - } - - /** - * Establishes a connection with the server and invokes the writeStream method. - * @return The HTTP response code to the POST method - */ - @Override - public Integer doInBackground(Void... params) { - Integer response = null; - String activatorPath = backendInteractor.getPath() + "devices/" + deviceId + "/activators/" + activatorId; - URL url = null; - HttpURLConnection urlConnection = null; - try { - url = new URL(activatorPath); - urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setRequestMethod("POST"); - urlConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); - urlConnection.setDoOutput(true); - urlConnection.connect(); - OutputStream deviceOutputStream = urlConnection.getOutputStream(); - writeStream(deviceOutputStream); - response = urlConnection.getResponseCode(); - } catch (SocketTimeoutException e) { - Log.e(TAG, "SocketTimeoutException"); - Log.e(TAG, e.toString()); - } catch (ConnectException e) { - Log.e(TAG, "ConnectExeption"); - Log.e(TAG, e.toString()); - } catch (IOException e) { - Log.e(TAG, e.toString()); - } finally { - if(urlConnection != null) { - urlConnection.disconnect(); - } - } - return response; - } - - /** - * Write the new state to the output stream, which is sent over the urlConnection. - */ - private void writeStream(OutputStream os) throws IOException { - JsonObject json = new JsonObject(); - JsonPrimitive jPrimitive = new JsonPrimitive(String.valueOf(newState)); - json.add("state", jPrimitive); - Log.i(TAG,json.toString()); - OutputStreamWriter outputStreamWriter = new OutputStreamWriter(os); - outputStreamWriter.write(json.toString()); - outputStreamWriter.flush(); - outputStreamWriter.close(); - } -} diff --git a/Hestia/app/src/main/java/hestia/backend/requests/DeleteRequest.java b/Hestia/app/src/main/java/hestia/backend/requests/DeleteRequest.java new file mode 100644 index 0000000..823cb42 --- /dev/null +++ b/Hestia/app/src/main/java/hestia/backend/requests/DeleteRequest.java @@ -0,0 +1,23 @@ +package hestia.backend.requests; + +import java.io.IOException; +import java.net.HttpURLConnection; + +/** + * This class does a DELETE request. It extends Request class, but does not do + * additional IO actions, therefore the methods will be left empty. + * @see Request + */ + +public class DeleteRequest extends Request { + + public DeleteRequest(String path) { + super("DELETE", path); + } + + @Override + protected void setDoIO(HttpURLConnection connector) {} + + @Override + protected void performIOAction(HttpURLConnection connector) throws IOException {} +} diff --git a/Hestia/app/src/main/java/hestia/backend/requests/GetDevicesRequest.java b/Hestia/app/src/main/java/hestia/backend/requests/GetDevicesRequest.java new file mode 100644 index 0000000..6d23162 --- /dev/null +++ b/Hestia/app/src/main/java/hestia/backend/requests/GetDevicesRequest.java @@ -0,0 +1,44 @@ +package hestia.backend.requests; + +import android.util.Log; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.net.HttpURLConnection; +import java.util.ArrayList; +import hestia.backend.Activator; +import hestia.backend.ActivatorDeserializer; +import hestia.backend.BackendInteractor; +import hestia.backend.Device; + +/** + * This class will do a GET request, expecting an ArrayList containing the list of devices. + * @see GetRequest + */ + +public class GetDevicesRequest extends GetRequest> { + + public GetDevicesRequest(String path) { + super(path); + } + + @Override + protected void setRegisterTypeAdapter(GsonBuilder gsonBuilder) { + gsonBuilder.registerTypeAdapter(Activator.class, new ActivatorDeserializer()); + } + + @Override + protected Type getReturnType() { + return new TypeToken>(){}.getType(); + } + + @Override + protected void onPostExecute(HttpURLConnection urlConnection) { + ArrayList devices = super.getReturnValue(); + if(devices != null) { + BackendInteractor.getInstance().setDevices(devices); + } else { + Log.e("GetDevicesRequest", "DEVICES ARRAY IS NULL"); + } + } +} diff --git a/Hestia/app/src/main/java/hestia/backend/requests/GetPluginInformationRequest.java b/Hestia/app/src/main/java/hestia/backend/requests/GetPluginInformationRequest.java new file mode 100644 index 0000000..14077f8 --- /dev/null +++ b/Hestia/app/src/main/java/hestia/backend/requests/GetPluginInformationRequest.java @@ -0,0 +1,49 @@ +package hestia.backend.requests; + +import android.app.Activity; +import android.widget.Toast; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.net.HttpURLConnection; +import java.util.HashMap; +import hestia.UI.AddDeviceInfo; + +/** + * This class will do a GET request, expecting a HashMap containing the information + * of the selected Plugin. + * @see GetRequest + */ + +public class GetPluginInformationRequest extends GetRequest> { + private Activity activity; + + public GetPluginInformationRequest(String path, Activity activity) { + super(path); + this.activity = activity; + } + + @Override + protected void setRegisterTypeAdapter(GsonBuilder gsonBuilder) {} + + @Override + protected Type getReturnType() { + return new TypeToken>(){}.getType(); + } + + /** + * When the task is over, if the HashMap is complete, it will create an AddDeviceInfo window. + * Otherwise, it will show an error message on the screen via Toast. + * @param connector The connector between the Client and the Server. + * @see AddDeviceInfo + */ + @Override + protected void onPostExecute(HttpURLConnection connector) { + HashMap plugins = super.getReturnValue(); + if(plugins != null) { + new AddDeviceInfo(this.activity, plugins).show(); + } else { + Toast.makeText(this.activity, "Wrong Input", Toast.LENGTH_SHORT).show(); + } + } +} diff --git a/Hestia/app/src/main/java/hestia/backend/requests/GetRequest.java b/Hestia/app/src/main/java/hestia/backend/requests/GetRequest.java new file mode 100644 index 0000000..4bbd852 --- /dev/null +++ b/Hestia/app/src/main/java/hestia/backend/requests/GetRequest.java @@ -0,0 +1,68 @@ +package hestia.backend.requests; + +import android.util.Log; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.net.HttpURLConnection; + +/** + * This is an abstract class used to do a GET request. + * @param the type of the requested object + * @see Request + */ + +abstract class GetRequest extends Request { + private T returnValue; + + GetRequest(String path) { + super("GET", path); + } + + @Override + protected void setDoIO(HttpURLConnection connector) { + connector.setDoInput(true); + } + + /** + * Retrieves a JSON file from the Server containing the object of the requested type T. + * @param connector The connector between the Client and the Server + * @throws IOException exception is caught in the body of the Request superclass. + * @see Request + */ + @Override + protected void performIOAction(HttpURLConnection connector) throws IOException { + GsonBuilder gsonBuilder = new GsonBuilder(); + this.setRegisterTypeAdapter(gsonBuilder); + Gson gson = gsonBuilder.create(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(connector.getInputStream())); + Type returnType = this.getReturnType(); + this.returnValue = gson.fromJson(gson.newJsonReader(reader), returnType); + Log.d("GetRequest", "RETURN VALUE IS: \n" + returnValue.toString()); + reader.close(); + } + + /** + * Sets the register type adapter of gsonBuilder object to accommodate the type + * of the expected object + * @param gsonBuilder the object that will convert the JSON file to the expected type + */ + protected abstract void setRegisterTypeAdapter(GsonBuilder gsonBuilder); + + /** + * Returns the type of the expected object, i.e. the class of the object. + * @return Type of the expected object + */ + protected abstract Type getReturnType(); + + @Override + protected abstract void onPostExecute(HttpURLConnection connector); + + protected T getReturnValue() { + return this.returnValue; + } +} diff --git a/Hestia/app/src/main/java/hestia/backend/requests/PostRequest.java b/Hestia/app/src/main/java/hestia/backend/requests/PostRequest.java new file mode 100644 index 0000000..d7ae067 --- /dev/null +++ b/Hestia/app/src/main/java/hestia/backend/requests/PostRequest.java @@ -0,0 +1,38 @@ +package hestia.backend.requests; + +import android.util.Log; + +import com.google.gson.JsonObject; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; + +/** + * This class does a POST request. It contains the Path to the Server and the Object to be send. + * @see Request + */ + +public class PostRequest extends Request { + private JsonObject object; + private String TAG = "PostRequest"; + + public PostRequest(String path, JsonObject object) { + super("POST", path); + this.object = object; + } + + @Override + protected void setDoIO(HttpURLConnection connector) { + connector.setDoOutput(true); + } + + @Override + protected void performIOAction(HttpURLConnection connector) throws IOException { + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(connector.getOutputStream()); + outputStreamWriter.write(this.object.toString()); + Log.d(TAG, object.toString()); + outputStreamWriter.flush(); + outputStreamWriter.close(); + } +} diff --git a/Hestia/app/src/main/java/hestia/backend/requests/Request.java b/Hestia/app/src/main/java/hestia/backend/requests/Request.java new file mode 100644 index 0000000..0d0a711 --- /dev/null +++ b/Hestia/app/src/main/java/hestia/backend/requests/Request.java @@ -0,0 +1,79 @@ +package hestia.backend.requests; + +import android.os.AsyncTask; +import android.util.Log; +import java.io.IOException; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.SocketTimeoutException; +import java.net.URL; + +/** + * This super class will send a HTTP request to the Server. + */ + +abstract class Request extends AsyncTask{ + private String requestMethod; + private String path; + + /** + * Creates an instance of the Request class of the given type. + * @param requestMethod the type of the HTTP request. + * @param path the path of to send the request + */ + Request(String requestMethod, String path) { + this.requestMethod = requestMethod; + this.path = path; + } + + /** + * Send the request to the server. + * @return the result of the task. If the type T is Void, it will return a null. + */ + @Override + protected HttpURLConnection doInBackground(Void... params) { + HttpURLConnection connector = null; + final String TAG = "Request"; + try { + URL url = new URL(this.path); + connector = (HttpURLConnection) url.openConnection(); + connector.setReadTimeout(2000); + connector.setConnectTimeout(2000); + connector.setRequestMethod(this.requestMethod); + connector.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + this.setDoIO(connector); + connector.connect(); + this.performIOAction(connector); + Log.d(TAG, "Response Code: " + String.valueOf(connector.getResponseCode())); + } catch (SocketTimeoutException e) { + Log.e(TAG, "SocketTimeoutException"); + Log.e(TAG, e.toString()); + } catch (ConnectException e) { + Log.e(TAG, "ConnectException"); + Log.e(TAG, e.toString()); + } catch (IOException e) { + Log.e(TAG, "IOException: could not realize the request"); + Log.e(TAG, e.toString()); + e.printStackTrace(); + } finally { + if (connector != null) { + connector.disconnect(); + } + } + return connector; + } + + /** + * Depending on the type of request, the connector parameter must setDoInput or + * setDoOutput to TRUE to allow I/O data transfer. + * @param connector the connector between the Client and the Server + */ + protected abstract void setDoIO(HttpURLConnection connector); + + /** + * This method contains all the Input/Output data transfer. It will be used especially + * for a GET request (Input) and for a POST request (Output). + * @param connector the connector between the Client and the Server + */ + protected abstract void performIOAction(HttpURLConnection connector) throws IOException; +} diff --git a/Hestia/app/src/main/res/layout/activity_fragment.xml b/Hestia/app/src/main/res/layout/activity_fragment.xml index e48c9af..4bc3158 100644 --- a/Hestia/app/src/main/res/layout/activity_fragment.xml +++ b/Hestia/app/src/main/res/layout/activity_fragment.xml @@ -1,17 +1,11 @@ - - - - - - - - - \ No newline at end of file diff --git a/Hestia/app/src/main/res/layout/activity_login.xml b/Hestia/app/src/main/res/layout/activity_login.xml index d09d115..0f74951 100644 --- a/Hestia/app/src/main/res/layout/activity_login.xml +++ b/Hestia/app/src/main/res/layout/activity_login.xml @@ -1,8 +1,10 @@ - - + - + + + + /> +