--- /dev/null
+#built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Windows thumbnail db
+Thumbs.db
+
+# OSX files
+.DS_Store
+
+# Eclipse project files
+.classpath
+.project
+
+# Android Studio
+*.iml
+.idea
+#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
+.gradle
+build/
+
+#NDK
+obj/
--- /dev/null
+BASE := ../../../..
+
+include $(BASE)/common.mk
+
+all:
+ chmod +x gradlew
+ ./gradlew assembleDebug
+
+install:
+ adb -d install ./app/build/outputs/apk/app-debug.apk
+
+clean:
+ ./gradlew clean
+
--- /dev/null
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.3"
+ useLibrary 'org.apache.http.legacy'
+ defaultConfig {
+ applicationId "com.example.xubin.irrigation"
+ minSdkVersion 15
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:appcompat-v7:23.3.0'
+ compile 'com.android.support:design:23.3.0'
+ compile files('libs/httpclient-android-4.3.3.jar')
+}
--- /dev/null
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/xubin/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
--- /dev/null
+package com.example.xubin.irrigation;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.xubin.irrigation">
+ <uses-permission android:name="android.permission.INTERNET"></uses-permission>
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIJAJEv9gf3BRGPMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UEBwwGSXJ2aW5lMQwwCgYDVQQKDANV
+Q0kxDTALBgNVBAsMBEVFQ1MxFjAUBgNVBAMMDTE5Mi4xNjguMi4xMDgwHhcNMTYw
+NTE2MjAzOTIzWhcNMTcwNTExMjAzOTIzWjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCQ0ExDzANBgNVBAcMBklydmluZTEMMAoGA1UECgwDVUNJMQ0wCwYDVQQLDARF
+RUNTMRYwFAYDVQQDDA0xOTIuMTY4LjIuMTA4MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA5azgPSycemBypBji0+dMvGUku8bCNxOI5t9rg3eTuDo/Sy1N
+nQD58jluSsUWf74bBAayZjn0i0lnRWggKXqLJiPLXY1kXxV0TT0+SEynSNC408PV
+8hZVYMd7pk6TqZw3MvDm2oHOKmpKW1bTBuNz6xpJhskckLyGM1QAQD9rvNrZII5a
+knINejARv3aKRvSRkNaunulXJztY2kOJnpXlO+HvTG8+Dzr8BTKiVU7wu1RwWtUP
+ha93eV+g0pbiJ/QUQ9mBScG1+iswOVA1HWnwEpV3GDJltGf1WTN4YsKRrXnfzwRf
+gAmTI9SU6KugddHMCTD+/DBn4X4SPxAVc+eqywIDAQABo1AwTjAdBgNVHQ4EFgQU
+oAvtU2TVp1KXB1ugGXIdt5zwmJcwHwYDVR0jBBgwFoAUoAvtU2TVp1KXB1ugGXId
+t5zwmJcwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAbPa1JIGxmJmd
+LQ3cM/nfLWn4BjkUFW7VSlsheL8zxt69uK6diwe3JFPNMHc4ZPey3N9KnlR5v/ax
+qCVi4cdm9wA+20uRV3ay6FXNuCTK1YFpFfAJ8c/ZVX4megKLcnb1QHOiYRJIjUp+
+jr36Qvewf/bpRxHZMkmNGx2rQWRUPUUxOlT8mQnxQk+28/FlSLtSRPDNhfnHTahT
+ujICes4XEq5/1fMhZKsjGo5JwzHp9KBg/RS1g7Q+gigPZZXCL8ml4pVBt3LYUGEA
+7GQoNEL6eXkSvv5jzMwVUwniZmDL6VoHWMZtAHRlFsjT/KRGWBx05shBk2zRuloT
+a9vK41EiSw==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIJAMzNAyNJVyYjMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UEBwwGSXJ2aW5lMQwwCgYDVQQKDANV
+Q0kxDTALBgNVBAsMBEVFQ1MxFjAUBgNVBAMMDTE5Mi4xNjguMi4xOTEwHhcNMTYw
+NTE3MTY0NTIzWhcNMTcwNTEyMTY0NTIzWjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCQ0ExDzANBgNVBAcMBklydmluZTEMMAoGA1UECgwDVUNJMQ0wCwYDVQQLDARF
+RUNTMRYwFAYDVQQDDA0xOTIuMTY4LjIuMTkxMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAwC8c83mhdfluocrTG2tMvwPR6GDiPuDLBAl1nHgmdttHdn1+
+IzaFoeXYa/96DROt7Sq5FiTao1zDsKuIoBulgmmlqwHbCdkbmA2nyk9on9g3KEGm
+Q9K6r1IX4gwDnD050uPu3OE6ygDmActgHnlUc65NEgHOCZDztN+TIGCBDVYcAmqH
+Ill0oPu2nsHpZBeRc0QoWW9cMBbyt5fFlIsYAV3BZjjU1UBx09R4etPLsO0V6+oV
+UT/Vgq11/V5yPwsQdPw5PCFuwnzBeGEKmkPXtgrxZCwD0m9nCrezaALGAtXaI+yk
+GkH2KbTDe1lizmL21IsFP1nFY28ux3reZ1obmQIDAQABo1AwTjAdBgNVHQ4EFgQU
+O3aAOMYwJ33gMwC1TxpJkj8NU/4wHwYDVR0jBBgwFoAUO3aAOMYwJ33gMwC1TxpJ
+kj8NU/4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEJpPABRerq90
+rZKaI5aYR71j2zlzTmp0GpJ/SmuAXrn83m0B9JOVIK7sAVY99ugjWS+eJtjmDAJc
+bb38OFu0hjH9U6W6TVTzNVVt5L6IMH6gMZTEVY61Yze3t+RHFXk2ggrxAgHnNVlo
+M5r9Thw427UwHN2P+Du6guEurCJmT3Rw9rDYtHBjuef3Q6KjzuzGwsvLw+pRS5F0
+jlFQgtINvfoZlHmQvy+6jEQe7lyp1nmJPNH421CPpYrfflsIeuG35rHbH0IQ1NNv
+aEzUbbvc0CKgIqEj9Spk3YFGMv7BZIcBviy3Jn7VWt57Xexm9hCEkzpkSiWAh/jB
+YMuEz6aLJw==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIJAKtV1MPWFk4tMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UEBwwGSXJ2aW5lMQwwCgYDVQQKDANV
+Q0kxDTALBgNVBAsMBEVFQ1MxFjAUBgNVBAMMDTE5Mi4xNjguMi4xOTIwHhcNMTYw
+NTE3MTY1NjE4WhcNMTcwNTEyMTY1NjE4WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCQ0ExDzANBgNVBAcMBklydmluZTEMMAoGA1UECgwDVUNJMQ0wCwYDVQQLDARF
+RUNTMRYwFAYDVQQDDA0xOTIuMTY4LjIuMTkyMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAyRi5B1CRo2kVBxJOA+Z1PRqw/yeh7zU8x7Pak5Vg2T56hd12
+iUk3V2IrgWh5PYdhrs8ucf17gmsjA+k/kF2GIkzZVNKEakWg1ggqeUbWr/aRP0oO
+10uUss4bcbghVoAKrJYZqJ7TjmQZWTcCFzpNzmAFWpcizVbQWF42p3tc7f3zsPk9
+rTOCTj6W2TJWCRHABkYfEExtf7T+JxG1cI/bY7YD/xLCtwykSBYrzDR0gtDxu7OD
+in38jO++kTEhkbjUtxnwBzz1bjaiGIBMqt5sufODWqaU5QekFZQz5Ru04gBYRcd+
+jnHEI6SqAY8MkWtp7fo/vYxTK7iuflkbqBkFkQIDAQABo1AwTjAdBgNVHQ4EFgQU
+/6CKy031CDWH+lrKn3BtHU+v2O0wHwYDVR0jBBgwFoAU/6CKy031CDWH+lrKn3Bt
+HU+v2O0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkDQVG+AvrmBI
+JGIGuAarNH/QeA7s4KI3B2BGvvSOOb9is1OuKrJ8TubTiEvbe9aGiKcqsZyt1abu
+DvQ5GZh9m2aRIGb3ByDg5Qk8iipA/oIuFWexiqa8IoFS2cFu+4nM0CHsQBpdMRNX
+tPWvV2CvQuzJRlALSucv4KOenzZmiiHBzBy1Bnbo6UFXl5DgYbOB+Mg82BlqenY5
+C2PpTEsrDmyy0A4IOnPQW9iRaNV7yC2i8T1bbFpSd95f64egb92hzGjf8os1r/5A
+OpOccHlP6zFQ7MFCVcUzhKjrVrHFtj89Evag7KwVqFpMIwqRga9DtIaJlxYszPPn
+m8woeEp1mA==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIJAJIi1p3z84ZfMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UEBwwGSXJ2aW5lMQwwCgYDVQQKDANV
+Q0kxDTALBgNVBAsMBEVFQ1MxFjAUBgNVBAMMDTE5Mi4xNjguMi4yNDQwHhcNMTYw
+NTE3MTY1ODQxWhcNMTcwNTEyMTY1ODQxWjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCQ0ExDzANBgNVBAcMBklydmluZTEMMAoGA1UECgwDVUNJMQ0wCwYDVQQLDARF
+RUNTMRYwFAYDVQQDDA0xOTIuMTY4LjIuMjQ0MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA36ufE9vlUNViOL24BSXGYjPrJMz0og55KFP+r9Z7QTz3uXuP
+onTkHb4zwnjT7lDfe6Fc0h8mwLsXrgzRGnEa31ywrJS5U1GDN9dlO2x6IO0E5xd6
+wEE+47qSxZ2Kv8f1UfJWgi7Wtla6tkjBqq+Gwj8qodO82km+vkHgqbsEtfFoUlvm
+KUfXdEkdigsSwxl2duCvZvDGWQ1j5k+Pr0oVJzTXiTWp0U1scQAtDccKDBzkIG7R
+vJ6Ne8xhdaGxu+92bECPp52edOwbD7S5c3ayDr50qHW7Jff+Wd9LaXjpQDxIeMf4
+RUPlbLbpQ0qWEUk2phGUha1j/Tank6V5TKwM1wIDAQABo1AwTjAdBgNVHQ4EFgQU
+6gIveONq1Pasrr1DKE92Qq9wpcAwHwYDVR0jBBgwFoAU6gIveONq1Pasrr1DKE92
+Qq9wpcAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAhy90ipsOXmeM
+AqXEKc4YA6G9ew/99fYlBaML8qvDyojZDL6Ur7uB2X+eOhB60H/WHCTs0kaWYnnq
+dGNfJnaV5ubQwrBOUfpmNdd5HQnxQXcdXev3CqMjz/Sdf9ckdoRkHnZNuqPCF2LO
+CB4x9tAo0BPBpnWO5g6O9JEowUv1Zw+hjKudcv8qZXixxuamaDGALZfbG7EG5oK9
+nhpnpb77JBu9WFTRsJfrAXfVo9+s8M38nLOp76Jy9O9jGlUpmG72KTAnqmgOqRzT
+C+X74eOUf02JV37kpPYLYnsKYjHqJRtBESgu/SE9CgHIMHbNkqUQTHoStBQxah7I
+zb9fkDRHsQ==
+-----END CERTIFICATE-----
--- /dev/null
+package com.example.xubin.irrigation;
+
+import android.util.Log;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.BufferedInputStream;
+import java.util.List;
+import java.security.cert.CertificateFactory;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.KeyStore;
+
+/**
+ * Created by xubin on 4/26/16.
+ */
+public class Helper {
+ private static final int Driver_port = 8000;
+ private static final String Tag = "http";
+ private static final String KEYEXT = ".pem";
+
+ HttpClient httpclient;
+ //Set up
+ void setConnection(String destIP) {
+
+ httpclient = createClient(destIP);
+ }
+
+ HttpClient createClient(String destIP) {
+ HttpParams params = new BasicHttpParams();
+ params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
+ params.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, HTTP.DEFAULT_CONTENT_CHARSET);
+ params.setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, true);
+ params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30 * 1000);
+ params.setParameter(CoreConnectionPNames.SO_TIMEOUT, 30 * 1000);
+
+ SchemeRegistry schReg = new SchemeRegistry();
+ schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), Driver_port));
+ schReg.register(new Scheme("https", newSslSocketFactory(destIP), 443));
+ ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
+
+ return new DefaultHttpClient(conMgr, params);
+ }
+
+ //Make http request
+ public void makeRequest(String destIP, List<Object> paramObjects, String methodName) {
+ String url = "https://"+ destIP + ":"+Driver_port+ "/"+methodName;
+ System.out.println("URL: " + url);
+ InputStream inputStream = null;
+ String result = "";
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ JSONArray params = new JSONArray();
+
+ JSONObject parent = new JSONObject();
+ for (int i = 0; i < paramObjects.size(); i++) {
+ JSONObject content = new JSONObject();
+ content.put("type", paramObjects.get(i).getClass().getName());
+ content.put("value", paramObjects.get(i));
+ params.put(i, content);
+ }
+ parent.put("params", params);
+ StringEntity se = new StringEntity(parent.toString());
+ httpPost.setEntity(se);
+ httpPost.setHeader("Accept", "application/json");
+ httpPost.setHeader("Content-type", "application/json");
+ HttpResponse httpResponse = httpclient.execute(httpPost);
+ // 9. receive response as inputStream
+ inputStream = httpResponse.getEntity().getContent();
+
+ // 10. convert inputstream to string
+ if(inputStream != null)
+ result = convertInputStreamToString(inputStream);
+ else
+ result = "Did not work!";
+ Log.v(Tag, result);
+ } catch (Exception ex) {
+ if (ex.getMessage() != null) {
+ Log.v(Tag, ex.getMessage());
+ }
+ ex.printStackTrace();
+ }
+ }
+
+ private SSLSocketFactory newSslSocketFactory(String destIP) {
+ try {
+ // Load CAs from an InputStream
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ InputStream caInput = new
+ BufferedInputStream(MainActivity.context.getAssets().open(destIP + KEYEXT));
+ Certificate ca;
+ try {
+ ca = cf.generateCertificate(caInput);
+ System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
+ } finally {
+ caInput.close();
+ }
+ String keyStoreType = KeyStore.getDefaultType();
+ KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+ keyStore.load(null, null);
+ keyStore.setCertificateEntry("ca", ca);
+ SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore);
+ return socketFactory;
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static String convertInputStreamToString(InputStream inputStream) throws IOException {
+ BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(inputStream));
+ String line = "";
+ String result = "";
+ while((line = bufferedReader.readLine()) != null)
+ result += line;
+
+ inputStream.close();
+ return result;
+
+ }
+}
--- /dev/null
+package com.example.xubin.irrigation;
+
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Button;
+import android.widget.EditText;
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainActivity extends AppCompatActivity {
+
+ private EditText gatewayIP;
+ private EditText inchesPerWeek;
+ private EditText weatherZipCode;
+ private EditText daysToWaterOn;
+ private EditText inchesPerMinute;
+ private Button submit_button;
+ protected static Context context;
+ private Helper helper = new Helper();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ context = getApplicationContext();
+ gatewayIP = (EditText) findViewById(R.id.gatewayip);
+ inchesPerWeek = (EditText) findViewById(R.id.week);
+ weatherZipCode = (EditText) findViewById(R.id.zip);
+ daysToWaterOn = (EditText) findViewById(R.id.water);
+ inchesPerMinute = (EditText) findViewById(R.id.minute);
+ submit_button = (Button) findViewById(R.id.submit);
+
+ submit_button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ List<Object> params = new ArrayList<>();
+ params.add(Double.parseDouble(inchesPerWeek.getText().toString()));
+ params.add(Integer.parseInt(weatherZipCode.getText().toString()));
+ params.add(Integer.parseInt(daysToWaterOn.getText().toString()));
+ params.add(Double.parseDouble(inchesPerMinute.getText().toString()));
+
+ String ip = gatewayIP.getText().toString();
+ new MakeRequestTask(params).execute(ip, "getIrrigationInfo");
+
+ }
+ });
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
+ .setAction("Action", null).show();
+ }
+ });
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+ private class MakeRequestTask extends AsyncTask<String, String, Void>{
+ private List<Object> params;
+ public MakeRequestTask(List<Object> argus) {
+ this.params = argus;
+ }
+ @Override
+ protected Void doInBackground(String... argus) {
+ if (helper.httpclient == null) {
+ helper.setConnection(argus[0]);
+ }
+
+ helper.makeRequest(argus[0],params, argus[1]);
+
+ return null;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ tools:context="com.example.xubin.irrigation.MainActivity">
+
+ <android.support.design.widget.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="@style/AppTheme.AppBarOverlay">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ app:popupTheme="@style/AppTheme.PopupOverlay" />
+
+ </android.support.design.widget.AppBarLayout>
+
+ <include layout="@layout/content_main" />
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@android:drawable/ic_dialog_email" />
+
+</android.support.design.widget.CoordinatorLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"
+ tools:context="com.example.xubin.irrigation.MainActivity"
+ tools:showIn="@layout/activity_main">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Inches/Week"
+ android:id="@+id/textView1"
+ android:layout_marginTop="43dp"
+ android:layout_below="@+id/gatewayip"
+ android:layout_toLeftOf="@+id/water"
+ android:layout_toStartOf="@+id/water" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Zip Code"
+ android:id="@+id/textView2"
+ android:layout_marginTop="52dp"
+ android:layout_below="@+id/water"
+ android:layout_toLeftOf="@+id/minute"
+ android:layout_toStartOf="@+id/minute" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Water Days"
+ android:id="@+id/textView3"
+ android:layout_marginTop="52dp"
+ android:layout_below="@+id/textView1"
+ android:layout_toLeftOf="@+id/zip"
+ android:layout_toStartOf="@+id/zip" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Inches/Minute"
+ android:id="@+id/textView"
+ android:layout_marginTop="50dp"
+ android:layout_below="@+id/textView2"
+ android:layout_toLeftOf="@+id/minute"
+ android:layout_toStartOf="@+id/minute" />
+
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="number|numberDecimal"
+ android:ems="10"
+ android:id="@+id/week"
+ android:text="20.01"
+ android:layout_alignBottom="@+id/textView1"
+ android:layout_toRightOf="@+id/textView1"
+ android:layout_toEndOf="@+id/textView1" />
+
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="number"
+ android:ems="10"
+ android:id="@+id/water"
+ android:layout_toRightOf="@+id/textView3"
+ android:layout_alignBottom="@+id/textView3"
+ android:text="255">
+ </EditText>
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="number"
+ android:ems="10"
+ android:id="@+id/zip"
+ android:layout_alignBottom="@+id/textView2"
+ android:layout_toRightOf="@+id/textView2"
+ android:text="92612" />
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="number|text|numberDecimal"
+ android:ems="10"
+ android:id="@+id/minute"
+ android:text="1.50"
+ android:layout_above="@+id/submit"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Submit"
+ android:id="@+id/submit"
+ android:layout_marginTop="26dp"
+ android:layout_below="@+id/textView"
+ android:layout_alignLeft="@+id/minute"
+ android:layout_alignStart="@+id/minute" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Gateway IP"
+ android:id="@+id/textView4"
+ android:layout_marginTop="69dp"
+ android:layout_alignParentTop="true"
+ android:layout_toLeftOf="@+id/gatewayip"
+ android:layout_toStartOf="@+id/gatewayip" />
+
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="number|numberDecimal"
+ android:ems="10"
+ android:id="@+id/gatewayip"
+ android:text="192.168.2.191"
+ android:layout_alignBottom="@+id/textView4"
+ android:layout_alignLeft="@+id/week"
+ android:layout_alignStart="@+id/week" />
+</RelativeLayout>
--- /dev/null
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context="com.example.xubin.irrigation.MainActivity">
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:title="@string/action_settings"
+ app:showAsAction="never" />
+</menu>
--- /dev/null
+<resources>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ </style>
+</resources>
--- /dev/null
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
--- /dev/null
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="fab_margin">16dp</dimen>
+</resources>
--- /dev/null
+<resources>
+ <string name="app_name">Irrigation</string>
+ <string name="action_settings">Settings</string>
+ <string name="destination_ip">192.168.2.244</string>
+</resources>
--- /dev/null
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+</resources>
--- /dev/null
+package com.example.xubin.irrigation;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
--- /dev/null
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.2.3'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
--- /dev/null
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
--- /dev/null
+#Mon Feb 20 10:57:04 PST 2017\r
+distributionBase=GRADLE_USER_HOME\r
+distributionPath=wrapper/dists\r
+zipStoreBase=GRADLE_USER_HOME\r
+zipStorePath=wrapper/dists\r
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip\r
--- /dev/null
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
--- /dev/null
+@if "%DEBUG%" == "" @echo off\r
+@rem ##########################################################################\r
+@rem\r
+@rem Gradle startup script for Windows\r
+@rem\r
+@rem ##########################################################################\r
+\r
+@rem Set local scope for the variables with windows NT shell\r
+if "%OS%"=="Windows_NT" setlocal\r
+\r
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r
+set DEFAULT_JVM_OPTS=\r
+\r
+set DIRNAME=%~dp0\r
+if "%DIRNAME%" == "" set DIRNAME=.\r
+set APP_BASE_NAME=%~n0\r
+set APP_HOME=%DIRNAME%\r
+\r
+@rem Find java.exe\r
+if defined JAVA_HOME goto findJavaFromJavaHome\r
+\r
+set JAVA_EXE=java.exe\r
+%JAVA_EXE% -version >NUL 2>&1\r
+if "%ERRORLEVEL%" == "0" goto init\r
+\r
+echo.\r
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r
+echo.\r
+echo Please set the JAVA_HOME variable in your environment to match the\r
+echo location of your Java installation.\r
+\r
+goto fail\r
+\r
+:findJavaFromJavaHome\r
+set JAVA_HOME=%JAVA_HOME:"=%\r
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe\r
+\r
+if exist "%JAVA_EXE%" goto init\r
+\r
+echo.\r
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r
+echo.\r
+echo Please set the JAVA_HOME variable in your environment to match the\r
+echo location of your Java installation.\r
+\r
+goto fail\r
+\r
+:init\r
+@rem Get command-line arguments, handling Windowz variants\r
+\r
+if not "%OS%" == "Windows_NT" goto win9xME_args\r
+if "%@eval[2+2]" == "4" goto 4NT_args\r
+\r
+:win9xME_args\r
+@rem Slurp the command line arguments.\r
+set CMD_LINE_ARGS=\r
+set _SKIP=2\r
+\r
+:win9xME_args_slurp\r
+if "x%~1" == "x" goto execute\r
+\r
+set CMD_LINE_ARGS=%*\r
+goto execute\r
+\r
+:4NT_args\r
+@rem Get arguments from the 4NT Shell from JP Software\r
+set CMD_LINE_ARGS=%$\r
+\r
+:execute\r
+@rem Setup the command line\r
+\r
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar\r
+\r
+@rem Execute Gradle\r
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r
+\r
+:end\r
+@rem End local scope for the variables with windows NT shell\r
+if "%ERRORLEVEL%"=="0" goto mainEnd\r
+\r
+:fail\r
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r
+rem the _cmd.exe /c_ return code!\r
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1\r
+exit /b 1\r
+\r
+:mainEnd\r
+if "%OS%"=="Windows_NT" endlocal\r
+\r
+:omega\r
--- /dev/null
+include ':app'
--- /dev/null
+#built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Windows thumbnail db
+Thumbs.db
+
+# OSX files
+.DS_Store
+
+# Eclipse project files
+.classpath
+.project
+
+# Android Studio
+*.iml
+.idea
+#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
+.gradle
+build/
+
+#NDK
+obj/
--- /dev/null
+BASE := ../../../..
+
+include $(BASE)/common.mk
+
+all:
+ chmod +x gradlew
+ ./gradlew assembleDebug
+
+install:
+ adb -d install -r ./app/build/outputs/apk/app-debug.apk
+
+clean:
+ ./gradlew clean
+
--- /dev/null
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.3"
+ useLibrary 'org.apache.http.legacy'
+ defaultConfig {
+ applicationId "com.example.xub3.speakerlocator"
+ minSdkVersion 15
+ targetSdkVersion 22
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ compile 'com.nanohttpd:nanohttpd-webserver:2.1.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:appcompat-v7:23.3.0'
+ compile 'com.android.support:design:23.3.0'
+ compile files('Libs/indoor-positioning-1.0.jar')
+ compile 'com.google.android.gms:play-services-appindexing:8.1.0'
+}
--- /dev/null
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/xub3/Android/Sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
--- /dev/null
+package com.example.xub3.speakerlocator;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+ public ApplicationTest() {
+ super(Application.class );
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.xub3.speakerlocator">
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"></uses-permission>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <receiver android:name=".CallReceiver" >
+ <intent-filter>
+ <action android:name="android.intent.action.PHONE_STATE" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
+ </intent-filter>
+ </receiver>
+ <service
+ android:name=".LocationService"
+ android:exported="false"/>
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity><!-- ATTENTION: This was auto-generated to add Google Play services to your project for
+ App Indexing. See https://g.co/AppIndexing/AndroidStudio for more information. -->
+ <meta-data
+ android:name="com.google.android.gms.version"
+ android:value="@integer/google_play_services_version" />
+ </application>
+
+</manifest>
\ No newline at end of file
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIJAJEv9gf3BRGPMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UEBwwGSXJ2aW5lMQwwCgYDVQQKDANV
+Q0kxDTALBgNVBAsMBEVFQ1MxFjAUBgNVBAMMDTE5Mi4xNjguMi4xMDgwHhcNMTYw
+NTE2MjAzOTIzWhcNMTcwNTExMjAzOTIzWjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCQ0ExDzANBgNVBAcMBklydmluZTEMMAoGA1UECgwDVUNJMQ0wCwYDVQQLDARF
+RUNTMRYwFAYDVQQDDA0xOTIuMTY4LjIuMTA4MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA5azgPSycemBypBji0+dMvGUku8bCNxOI5t9rg3eTuDo/Sy1N
+nQD58jluSsUWf74bBAayZjn0i0lnRWggKXqLJiPLXY1kXxV0TT0+SEynSNC408PV
+8hZVYMd7pk6TqZw3MvDm2oHOKmpKW1bTBuNz6xpJhskckLyGM1QAQD9rvNrZII5a
+knINejARv3aKRvSRkNaunulXJztY2kOJnpXlO+HvTG8+Dzr8BTKiVU7wu1RwWtUP
+ha93eV+g0pbiJ/QUQ9mBScG1+iswOVA1HWnwEpV3GDJltGf1WTN4YsKRrXnfzwRf
+gAmTI9SU6KugddHMCTD+/DBn4X4SPxAVc+eqywIDAQABo1AwTjAdBgNVHQ4EFgQU
+oAvtU2TVp1KXB1ugGXIdt5zwmJcwHwYDVR0jBBgwFoAUoAvtU2TVp1KXB1ugGXId
+t5zwmJcwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAbPa1JIGxmJmd
+LQ3cM/nfLWn4BjkUFW7VSlsheL8zxt69uK6diwe3JFPNMHc4ZPey3N9KnlR5v/ax
+qCVi4cdm9wA+20uRV3ay6FXNuCTK1YFpFfAJ8c/ZVX4megKLcnb1QHOiYRJIjUp+
+jr36Qvewf/bpRxHZMkmNGx2rQWRUPUUxOlT8mQnxQk+28/FlSLtSRPDNhfnHTahT
+ujICes4XEq5/1fMhZKsjGo5JwzHp9KBg/RS1g7Q+gigPZZXCL8ml4pVBt3LYUGEA
+7GQoNEL6eXkSvv5jzMwVUwniZmDL6VoHWMZtAHRlFsjT/KRGWBx05shBk2zRuloT
+a9vK41EiSw==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIJAMzNAyNJVyYjMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UEBwwGSXJ2aW5lMQwwCgYDVQQKDANV
+Q0kxDTALBgNVBAsMBEVFQ1MxFjAUBgNVBAMMDTE5Mi4xNjguMi4xOTEwHhcNMTYw
+NTE3MTY0NTIzWhcNMTcwNTEyMTY0NTIzWjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCQ0ExDzANBgNVBAcMBklydmluZTEMMAoGA1UECgwDVUNJMQ0wCwYDVQQLDARF
+RUNTMRYwFAYDVQQDDA0xOTIuMTY4LjIuMTkxMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAwC8c83mhdfluocrTG2tMvwPR6GDiPuDLBAl1nHgmdttHdn1+
+IzaFoeXYa/96DROt7Sq5FiTao1zDsKuIoBulgmmlqwHbCdkbmA2nyk9on9g3KEGm
+Q9K6r1IX4gwDnD050uPu3OE6ygDmActgHnlUc65NEgHOCZDztN+TIGCBDVYcAmqH
+Ill0oPu2nsHpZBeRc0QoWW9cMBbyt5fFlIsYAV3BZjjU1UBx09R4etPLsO0V6+oV
+UT/Vgq11/V5yPwsQdPw5PCFuwnzBeGEKmkPXtgrxZCwD0m9nCrezaALGAtXaI+yk
+GkH2KbTDe1lizmL21IsFP1nFY28ux3reZ1obmQIDAQABo1AwTjAdBgNVHQ4EFgQU
+O3aAOMYwJ33gMwC1TxpJkj8NU/4wHwYDVR0jBBgwFoAUO3aAOMYwJ33gMwC1TxpJ
+kj8NU/4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEJpPABRerq90
+rZKaI5aYR71j2zlzTmp0GpJ/SmuAXrn83m0B9JOVIK7sAVY99ugjWS+eJtjmDAJc
+bb38OFu0hjH9U6W6TVTzNVVt5L6IMH6gMZTEVY61Yze3t+RHFXk2ggrxAgHnNVlo
+M5r9Thw427UwHN2P+Du6guEurCJmT3Rw9rDYtHBjuef3Q6KjzuzGwsvLw+pRS5F0
+jlFQgtINvfoZlHmQvy+6jEQe7lyp1nmJPNH421CPpYrfflsIeuG35rHbH0IQ1NNv
+aEzUbbvc0CKgIqEj9Spk3YFGMv7BZIcBviy3Jn7VWt57Xexm9hCEkzpkSiWAh/jB
+YMuEz6aLJw==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIJAKtV1MPWFk4tMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UEBwwGSXJ2aW5lMQwwCgYDVQQKDANV
+Q0kxDTALBgNVBAsMBEVFQ1MxFjAUBgNVBAMMDTE5Mi4xNjguMi4xOTIwHhcNMTYw
+NTE3MTY1NjE4WhcNMTcwNTEyMTY1NjE4WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCQ0ExDzANBgNVBAcMBklydmluZTEMMAoGA1UECgwDVUNJMQ0wCwYDVQQLDARF
+RUNTMRYwFAYDVQQDDA0xOTIuMTY4LjIuMTkyMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAyRi5B1CRo2kVBxJOA+Z1PRqw/yeh7zU8x7Pak5Vg2T56hd12
+iUk3V2IrgWh5PYdhrs8ucf17gmsjA+k/kF2GIkzZVNKEakWg1ggqeUbWr/aRP0oO
+10uUss4bcbghVoAKrJYZqJ7TjmQZWTcCFzpNzmAFWpcizVbQWF42p3tc7f3zsPk9
+rTOCTj6W2TJWCRHABkYfEExtf7T+JxG1cI/bY7YD/xLCtwykSBYrzDR0gtDxu7OD
+in38jO++kTEhkbjUtxnwBzz1bjaiGIBMqt5sufODWqaU5QekFZQz5Ru04gBYRcd+
+jnHEI6SqAY8MkWtp7fo/vYxTK7iuflkbqBkFkQIDAQABo1AwTjAdBgNVHQ4EFgQU
+/6CKy031CDWH+lrKn3BtHU+v2O0wHwYDVR0jBBgwFoAU/6CKy031CDWH+lrKn3Bt
+HU+v2O0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkDQVG+AvrmBI
+JGIGuAarNH/QeA7s4KI3B2BGvvSOOb9is1OuKrJ8TubTiEvbe9aGiKcqsZyt1abu
+DvQ5GZh9m2aRIGb3ByDg5Qk8iipA/oIuFWexiqa8IoFS2cFu+4nM0CHsQBpdMRNX
+tPWvV2CvQuzJRlALSucv4KOenzZmiiHBzBy1Bnbo6UFXl5DgYbOB+Mg82BlqenY5
+C2PpTEsrDmyy0A4IOnPQW9iRaNV7yC2i8T1bbFpSd95f64egb92hzGjf8os1r/5A
+OpOccHlP6zFQ7MFCVcUzhKjrVrHFtj89Evag7KwVqFpMIwqRga9DtIaJlxYszPPn
+m8woeEp1mA==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIJAJIi1p3z84ZfMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UEBwwGSXJ2aW5lMQwwCgYDVQQKDANV
+Q0kxDTALBgNVBAsMBEVFQ1MxFjAUBgNVBAMMDTE5Mi4xNjguMi4yNDQwHhcNMTYw
+NTE3MTY1ODQxWhcNMTcwNTEyMTY1ODQxWjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCQ0ExDzANBgNVBAcMBklydmluZTEMMAoGA1UECgwDVUNJMQ0wCwYDVQQLDARF
+RUNTMRYwFAYDVQQDDA0xOTIuMTY4LjIuMjQ0MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA36ufE9vlUNViOL24BSXGYjPrJMz0og55KFP+r9Z7QTz3uXuP
+onTkHb4zwnjT7lDfe6Fc0h8mwLsXrgzRGnEa31ywrJS5U1GDN9dlO2x6IO0E5xd6
+wEE+47qSxZ2Kv8f1UfJWgi7Wtla6tkjBqq+Gwj8qodO82km+vkHgqbsEtfFoUlvm
+KUfXdEkdigsSwxl2duCvZvDGWQ1j5k+Pr0oVJzTXiTWp0U1scQAtDccKDBzkIG7R
+vJ6Ne8xhdaGxu+92bECPp52edOwbD7S5c3ayDr50qHW7Jff+Wd9LaXjpQDxIeMf4
+RUPlbLbpQ0qWEUk2phGUha1j/Tank6V5TKwM1wIDAQABo1AwTjAdBgNVHQ4EFgQU
+6gIveONq1Pasrr1DKE92Qq9wpcAwHwYDVR0jBBgwFoAU6gIveONq1Pasrr1DKE92
+Qq9wpcAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAhy90ipsOXmeM
+AqXEKc4YA6G9ew/99fYlBaML8qvDyojZDL6Ur7uB2X+eOhB60H/WHCTs0kaWYnnq
+dGNfJnaV5ubQwrBOUfpmNdd5HQnxQXcdXev3CqMjz/Sdf9ckdoRkHnZNuqPCF2LO
+CB4x9tAo0BPBpnWO5g6O9JEowUv1Zw+hjKudcv8qZXixxuamaDGALZfbG7EG5oK9
+nhpnpb77JBu9WFTRsJfrAXfVo9+s8M38nLOp76Jy9O9jGlUpmG72KTAnqmgOqRzT
+C+X74eOUf02JV37kpPYLYnsKYjHqJRtBESgu/SE9CgHIMHbNkqUQTHoStBQxah7I
+zb9fkDRHsQ==
+-----END CERTIFICATE-----
--- /dev/null
+package com.example.xub3.speakerlocator;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.util.Log;
+
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.util.Date;
+import java.io.*;
+
+
+/**
+ * Created by xub3 on 4/14/16.
+ */
+public class CallReceiver extends PhonecallReceiver {
+
+
+
+
+ /*
+ ** Interrupt the playing music when phone is ringing
+ */
+ @Override
+ protected void onIncomingCallReceived(Context ctx, String number, Date start) {
+ String driverIP = ctx.getResources().getString(R.string.gateway_ip);
+ new MakeRequestTask(driverIP, true, "setRingStatus").start();
+ }
+
+ @Override
+ protected void onIncomingCallAnswered(Context ctx, String number, Date start) {
+
+ }
+ /*
+ ** Resume the music after the phone call
+ */
+ @Override
+ protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end) {
+ String driverIP = ctx.getResources().getString(R.string.gateway_ip);
+ new MakeRequestTask(driverIP, false, "setRingStatus").start();
+ }
+ /*
+ ** Interrupt the music when starting an outgoing call
+ */
+ @Override
+ protected void onOutgoingCallStarted(Context ctx, String number, Date start) {
+
+ String driverIP = ctx.getResources().getString(R.string.gateway_ip);
+ new MakeRequestTask(driverIP, true, "setRingStatus").start();
+ }
+ /*
+ ** Resume the music when finishing calls
+ */
+ @Override
+ protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end) {
+ String driverIP = ctx.getResources().getString(R.string.gateway_ip);
+ new MakeRequestTask(driverIP, false, "setRingStatus").start();
+ }
+
+ @Override
+ protected void onMissedCall(Context ctx, String number, Date start) {
+ String driverIP = ctx.getResources().getString(R.string.gateway_ip);
+ new MakeRequestTask(driverIP, false, "setRingStatus").start();
+ }
+
+
+}
+class MakeRequestTask implements Runnable {
+
+ private Helper helper = MainActivity.helper;
+ private String driverIP;
+ private boolean status;
+ private String methodName;
+ private Thread t;
+ MakeRequestTask(String ip, boolean ring, String name) {
+ driverIP = ip;
+ status = ring;
+ methodName = name;
+ }
+
+ @Override
+ public void run() {
+ helper.makeRequest(driverIP, status, methodName);
+ }
+ public void start() {
+ if (t == null) {
+ t = new Thread(this, "makeRequest");
+ }
+ t.start();
+ }
+}
--- /dev/null
+package com.example.xub3.speakerlocator;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.widget.TextView;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.hadizadeh.positioning.controller.ExclusionTechnology;
+import de.hadizadeh.positioning.controller.Technology;
+import de.hadizadeh.positioning.model.SignalInformation;
+
+/**
+ * Created by xubin on 4/28/16.
+ */
+public class CompassTechnology extends ExclusionTechnology {
+
+ private float bearing;
+ private Context context;
+ private TextView compassTv;
+
+ public CompassTechnology(Context context, String name, double allowedDelta) {
+ super(name, allowedDelta / 2);
+ this.context = context;
+ compassTv = (TextView)((Activity)context).findViewById(R.id.hellotext);
+ SensorManager mySensorManager = (SensorManager) context.
+ getSystemService(Context.SENSOR_SERVICE);
+ List<Sensor> mySensors = mySensorManager.getSensorList(
+ Sensor.TYPE_ORIENTATION);
+ if (mySensors.size() > 0) {
+ mySensorManager.registerListener(mySensorEventListener, mySensors.get(0),
+ SensorManager.SENSOR_DELAY_UI);
+ }
+ }
+
+ @Override
+ public Map<String, SignalInformation> getSignalData() {
+ Map<String, SignalInformation> signalData = new HashMap<String, SignalInformation>();
+ signalData.put("compassSignal", new SignalInformation(bearing));
+ return signalData;
+ }
+
+ private SensorEventListener mySensorEventListener = new SensorEventListener() {
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ float compassBearing = (float) event.values[0];
+ if (Math.abs(bearing - compassBearing) > 2) {
+ compassTv.setText(String.valueOf(compassBearing));
+ }
+ bearing = compassBearing;
+
+ }
+ };
+
+ @Override
+ protected boolean isValueOutOfExclusionRange(
+ Map<String, SignalInformation> signalData, double persistedValue) {
+ boolean inRange = true;
+ for (Map.Entry<String, SignalInformation> data : signalData.entrySet()) {
+ double currentValue = data.getValue().getStrength();
+ double min = persistedValue - allowedDelta;
+ double max = persistedValue + allowedDelta;
+ if (max >= 360) {
+ max -= 360;
+ }
+ if (min <= 0) {
+ min = 360 - min;
+ }
+
+ if (min > max) {
+ if (!(currentValue >= min || currentValue <= max)) {
+ inRange = false;
+ }
+ } else {
+ if (!(currentValue >= min && currentValue <= max)) {
+ inRange = false;
+ }
+ }
+ }
+ return inRange;
+ }
+}
+
--- /dev/null
+package com.example.xub3.speakerlocator;
+
+import android.util.Log;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+
+/**
+ * Created by xub3 on 4/14/16.
+ */
+public class Helper {
+ private static final int Driver_port = 8000;
+ private static final String Tag = "CallReceiver";
+ private static final String KEYEXT = ".pem";
+ HttpClient httpclient;
+ //Set up
+ //Set up
+ void setConnection(String destIP) {
+
+ httpclient = createClient(destIP);
+ }
+
+ HttpClient createClient(String destIP) {
+ HttpParams params = new BasicHttpParams();
+ params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
+ params.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, HTTP.DEFAULT_CONTENT_CHARSET);
+ params.setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, true);
+ params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30 * 1000);
+ params.setParameter(CoreConnectionPNames.SO_TIMEOUT, 30 * 1000);
+
+ SchemeRegistry schReg = new SchemeRegistry();
+ schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), Driver_port));
+ schReg.register(new Scheme("https", newSslSocketFactory(destIP), 443));
+ ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
+
+ return new DefaultHttpClient(conMgr, params);
+ }
+
+ private SSLSocketFactory newSslSocketFactory(String destIP) {
+ try {
+ // Load CAs from an InputStream
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ InputStream caInput = new
+ BufferedInputStream(MainActivity.context.getAssets().open(destIP + KEYEXT));
+ Certificate ca;
+ try {
+ ca = cf.generateCertificate(caInput);
+ System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
+ } finally {
+ caInput.close();
+ }
+ String keyStoreType = KeyStore.getDefaultType();
+ KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+ keyStore.load(null, null);
+ keyStore.setCertificateEntry("ca", ca);
+ SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore);
+ return socketFactory;
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ //Make http request
+ public void makeRequest(String destIP, Object contentStr, String methodName) {
+ String url = "https://"+ destIP+":" + Driver_port + "/"+methodName;
+ System.out.println("URL: " + url);
+
+ InputStream inputStream = null;
+ String result = "";
+ StringBuilder sb = new StringBuilder();
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ JSONArray params = new JSONArray();
+
+ JSONObject content = new JSONObject();
+ JSONObject parent = new JSONObject();
+ content.put("type", contentStr.getClass().getName());
+ content.put("value", contentStr);
+ params.put(0,content);
+ parent.put("params", params);
+ StringEntity se = new StringEntity(parent.toString());
+ httpPost.setEntity(se);
+ httpPost.setHeader("Accept", "application/json");
+ httpPost.setHeader("Content-type", "application/json");
+ HttpResponse httpResponse = httpclient.execute(httpPost);
+ // 9. receive response as inputStream
+ inputStream = httpResponse.getEntity().getContent();
+
+ // 10. convert inputstream to string
+ if(inputStream != null)
+ result = convertInputStreamToString(inputStream);
+ else
+ result = "Did not work!";
+ Log.v(Tag, result);
+ } catch (Exception ex) {
+ if (ex.getMessage() != null) {
+ Log.v(Tag, ex.getMessage());
+ }
+ ex.printStackTrace();
+ }
+ }
+ private static String convertInputStreamToString(InputStream inputStream) throws IOException{
+ BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(inputStream));
+ String line = "";
+ String result = "";
+ while((line = bufferedReader.readLine()) != null)
+ result += line;
+
+ inputStream.close();
+ return result;
+ }
+}
+
--- /dev/null
+package com.example.xub3.speakerlocator;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.google.android.gms.appindexing.Action;
+import com.google.android.gms.appindexing.AppIndex;
+import com.google.android.gms.common.api.GoogleApiClient;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.hadizadeh.positioning.controller.PositionListener;
+import de.hadizadeh.positioning.controller.PositionManager;
+import de.hadizadeh.positioning.controller.Technology;
+import de.hadizadeh.positioning.exceptions.PositioningException;
+import de.hadizadeh.positioning.exceptions.PositioningPersistenceException;
+import de.hadizadeh.positioning.model.PositionInformation;
+
+
+public class MainActivity extends AppCompatActivity implements PositionListener {
+
+ static Helper helper = new Helper();
+ private TextView tv;
+ private Button button;
+ private PositionManager positionManager;
+ private String curr_Loc = "";
+ private static String roomIDbuffer = "0";
+ protected static Context context;
+ // Storage Permissions
+ private static final int REQUEST_EXTERNAL_STORAGE = 1;
+ private static String[] PERMISSIONS_STORAGE = {
+ android.Manifest.permission.READ_EXTERNAL_STORAGE,
+ android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+ };
+
+ /**
+ * ATTENTION: This was auto-generated to implement the App Indexing API.
+ * See https://g.co/AppIndexing/AndroidStudio for more information.
+ */
+ private GoogleApiClient client;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ context = getApplicationContext();
+ setContentView(R.layout.activity_main);
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ //System.out.println("Status: " + Environment.getExternalStorageState());
+ initializePositioning();
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ tv = (TextView) findViewById(R.id.hellotext);
+ if (helper.httpclient == null) {
+ helper.setConnection(getApplicationContext().getResources().getString(R.string.gateway_ip));
+ }
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
+ .setAction("Action", null).show();
+ }
+ });
+
+
+ // ATTENTION: This was auto-generated to implement the App Indexing API.
+ // See https://g.co/AppIndexing/AndroidStudio for more information.
+ client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void initializePositioning() {
+
+ int permission = ActivityCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (permission != PackageManager.PERMISSION_GRANTED) {
+ // We don't have permission so prompt the user
+ ActivityCompat.requestPermissions(
+ this,
+ PERMISSIONS_STORAGE,
+ REQUEST_EXTERNAL_STORAGE
+ );
+ }
+ File file = new File(Environment.getExternalStorageDirectory(), "positioningPersistence.xml");
+ try {
+ positionManager = new PositionManager(file);
+ Log.d("positionManager", "initialized");
+ System.out.println("PositionManager: Initialized successfully!");
+ } catch (PositioningPersistenceException e) {
+ e.printStackTrace();
+ }
+
+ List<String> keyWhiteList = new ArrayList<String>();
+ keyWhiteList.add(getApplicationContext().getResources().getString(R.string.mac_1).toLowerCase());
+ keyWhiteList.add(getApplicationContext().getResources().getString(R.string.mac_2).toLowerCase());
+ keyWhiteList.add(getApplicationContext().getResources().getString(R.string.mac_3).toLowerCase());
+ keyWhiteList.add(getApplicationContext().getResources().getString(R.string.mac_4).toLowerCase());
+ keyWhiteList.add(getApplicationContext().getResources().getString(R.string.mac_5).toLowerCase());
+ keyWhiteList.add(getApplicationContext().getResources().getString(R.string.mac_6).toLowerCase());
+
+ Technology wifiTechnology = new WifiTechnology(this, "WIFI", keyWhiteList);
+ CompassTechnology compassTechnology = new CompassTechnology(this, "compass", 80
+ );
+ try {
+ //positionManager.addTechnology(compassTechnology);
+ positionManager.addTechnology(wifiTechnology);
+ } catch (PositioningException e) {
+ e.printStackTrace();
+ }
+ positionManager.registerPositionListener(this);
+ final EditText mapName = (EditText) findViewById(R.id.mapname_et);
+ Button mapBtn = (Button) findViewById(R.id.map_btn);
+ Button startBtn = (Button) findViewById(R.id.start_btn);
+ Button stopBtn = (Button) findViewById(R.id.stop_btn);
+
+ mapBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ positionManager.map(mapName.getText().toString());
+ }
+ });
+ startBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ positionManager.startPositioning(2000);
+ }
+ });
+ stopBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ positionManager.stopPositioning();
+ }
+ });
+ }
+
+ @Override
+ public void positionReceived(final PositionInformation positionInformation) {
+
+ System.out.println("This is called! Single!");
+ String positioningText = positionInformation.getName();
+ if (!positioningText.equals(curr_Loc)) {
+ final String room_id;
+ // we need to handle a situation where
+ // positionInformation.getName() returns ""
+ if (positionInformation.getName().equals("")) {
+ room_id = roomIDbuffer;
+ } else {
+ room_id = positionInformation.getName();
+ roomIDbuffer = room_id;
+ }
+ new Thread() {
+ public void run() {
+ String driver_IP = getApplicationContext().getResources().getString(R.string.gateway_ip);
+ helper.makeRequest(driver_IP, Integer.parseInt(room_id), "setRoomID");
+ curr_Loc = room_id;
+ System.out.println("room changed to " + room_id);
+ }
+ }.start();
+ curr_Loc = positioningText;
+ }
+
+
+ // Do nothing
+ }
+
+ @Override
+ public void positionReceived(final List<PositionInformation> positionInformation) {
+
+ System.out.println("This is called! List!");
+ String positioningText = "";
+ int count = 0;
+ for (int i = 0; i < positionInformation.size(); i++) {
+ if (!positionInformation.get(i).getName().equals(curr_Loc)) {
+ count += 1;
+ if (count > positionInformation.size() / 2) {
+ final String room_id;
+ // we need to handle a situation where
+ // positionInformation.getName() returns ""
+ if (positionInformation.get(i).getName().equals("")) {
+ room_id = roomIDbuffer;
+ } else {
+ room_id = positionInformation.get(i).getName();
+ roomIDbuffer = room_id;
+ }
+ new Thread() {
+ public void run() {
+ String driver_IP = getApplicationContext().getResources().getString(R.string.gateway_ip);
+ helper.makeRequest(driver_IP, Integer.parseInt(room_id), "setRoomID");
+ curr_Loc = room_id;
+ System.out.println("room changed to " + room_id);
+ }
+ }.start();
+ positioningText = curr_Loc;
+ break;
+ }
+ }
+ }
+
+
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ // ATTENTION: This was auto-generated to implement the App Indexing API.
+ // See https://g.co/AppIndexing/AndroidStudio for more information.
+ client.connect();
+ Action viewAction = Action.newAction(
+ Action.TYPE_VIEW, // TODO: choose an action type.
+ "Main Page", // TODO: Define a title for the content shown.
+ // TODO: If you have web page content that matches this app activity's content,
+ // make sure this auto-generated web page URL is correct.
+ // Otherwise, set the URL to null.
+ Uri.parse("http://host/path"),
+ // TODO: Make sure this auto-generated app URL is correct.
+ Uri.parse("android-app://com.example.xub3.speakerlocator/http/host/path")
+ );
+ AppIndex.AppIndexApi.start(client, viewAction);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ // ATTENTION: This was auto-generated to implement the App Indexing API.
+ // See https://g.co/AppIndexing/AndroidStudio for more information.
+ Action viewAction = Action.newAction(
+ Action.TYPE_VIEW, // TODO: choose an action type.
+ "Main Page", // TODO: Define a title for the content shown.
+ // TODO: If you have web page content that matches this app activity's content,
+ // make sure this auto-generated web page URL is correct.
+ // Otherwise, set the URL to null.
+ Uri.parse("http://host/path"),
+ // TODO: Make sure this auto-generated app URL is correct.
+ Uri.parse("android-app://com.example.xub3.speakerlocator/http/host/path")
+ );
+ AppIndex.AppIndexApi.end(client, viewAction);
+ client.disconnect();
+ }
+}
+
--- /dev/null
+package com.example.xub3.speakerlocator;
+
+/**
+ * Created by xub3 on 4/14/16.
+ */
+import java.util.Date;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.telephony.TelephonyManager;
+
+public abstract class PhonecallReceiver extends BroadcastReceiver {
+
+ //The receiver will be recreated whenever android feels like it. We need a static variable to remember data between instantiations
+
+ private static int lastState = TelephonyManager.CALL_STATE_IDLE;
+ private static Date callStartTime;
+ private static boolean isIncoming;
+ private static String savedNumber;
+ protected Helper helper = MainActivity.helper;
+ //because the passed incoming is only valid in ringing
+
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ //We listen to two intents. The new outgoing call only tells us of an outgoing call. We use it to get the number.
+ if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
+ savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
+ } else {
+ String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
+ String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
+ int state = 0;
+ if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
+ state = TelephonyManager.CALL_STATE_IDLE;
+ } else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
+ state = TelephonyManager.CALL_STATE_OFFHOOK;
+ } else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
+ state = TelephonyManager.CALL_STATE_RINGING;
+ }
+
+
+ onCallStateChanged(context, state, number);
+ }
+ }
+
+ //Derived classes should override these to respond to specific events of interest
+ protected abstract void onIncomingCallReceived(Context ctx, String number, Date start);
+ protected abstract void onIncomingCallAnswered(Context ctx, String number, Date start);
+ protected abstract void onIncomingCallEnded(Context ctx, String number, Date start, Date end);
+
+ protected abstract void onOutgoingCallStarted(Context ctx, String number, Date start);
+ protected abstract void onOutgoingCallEnded(Context ctx, String number, Date start, Date end);
+
+ protected abstract void onMissedCall(Context ctx, String number, Date start);
+
+ //Deals with actual events
+
+ //Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
+ //Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
+ public void onCallStateChanged(Context context, int state, String number) {
+ if(lastState == state) {
+ //No change, debounce extras
+ return;
+ }
+ switch (state) {
+ case TelephonyManager.CALL_STATE_RINGING:
+ isIncoming = true;
+ callStartTime = new Date();
+ savedNumber = number;
+ onIncomingCallReceived(context, number, callStartTime);
+ break;
+
+ case TelephonyManager.CALL_STATE_OFFHOOK:
+ //Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
+ if(lastState != TelephonyManager.CALL_STATE_RINGING) {
+ isIncoming = false;
+ callStartTime = new Date();
+ onOutgoingCallStarted(context, savedNumber, callStartTime);
+ } else
+ {
+ isIncoming = true;
+ callStartTime = new Date();
+ onIncomingCallAnswered(context, savedNumber, callStartTime);
+ }
+
+ break;
+
+ case TelephonyManager.CALL_STATE_IDLE:
+ //Went to idle- this is the end of a call. What type depends on previous state(s)
+ if(lastState == TelephonyManager.CALL_STATE_RINGING) {
+ //Ring but no pickup- a miss
+ onMissedCall(context, savedNumber, callStartTime);
+ } else if(isIncoming) {
+ onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
+ } else {
+ onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
+ }
+ break;
+ }
+ lastState = state;
+ }
+}
\ No newline at end of file
--- /dev/null
+package com.example.xub3.speakerlocator;
+
+/**
+ * Created by xubin on 4/27/16.
+ */
+
+import de.hadizadeh.positioning.model.SignalInformation;
+import android.content.Context;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import de.hadizadeh.positioning.controller.Technology;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class WifiTechnology extends Technology {
+ private WifiManager wifiManager;
+
+ public WifiTechnology(Context context, String name, List<String> keyWhiteList) {
+ super(name, keyWhiteList);
+ wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ @Override
+ public Map<String, SignalInformation> getSignalData() {
+ Map<String, SignalInformation> signalData =
+ new HashMap<String, SignalInformation>();
+
+ wifiManager.startScan();
+ List<ScanResult> scanResults = wifiManager.getScanResults();
+ for (ScanResult scanResult : scanResults) {
+ signalData.put(scanResult.BSSID, new SignalInformation(scanResult.level));
+ }
+ return signalData;
+ }
+}
+
+
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ tools:context="com.example.xub3.speakerlocator.MainActivity"
+ tools:showIn="@layout/content_main">
+
+ <android.support.design.widget.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="@style/AppTheme.AppBarOverlay">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ app:popupTheme="@style/AppTheme.PopupOverlay" />
+
+ </android.support.design.widget.AppBarLayout>
+
+ <include layout="@layout/content_main" />
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@android:drawable/ic_dialog_email" />
+
+</android.support.design.widget.CoordinatorLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"
+ tools:context="com.example.xub3.speakerlocator.MainActivity"
+ tools:showIn="@layout/activity_main">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Hello World!"
+ android:id="@+id/hellotext" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="map"
+ android:id="@+id/map_btn"
+ android:layout_below="@+id/hellotext"
+ android:layout_centerHorizontal="true" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="start"
+ android:id="@+id/start_btn"
+ android:layout_below="@+id/map_btn"
+ android:layout_alignLeft="@+id/map_btn"
+ android:layout_alignStart="@+id/map_btn"
+ android:layout_marginTop="50dp" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Stop"
+ android:id="@+id/stop_btn"
+ android:layout_centerVertical="true"
+ android:layout_alignLeft="@+id/start_btn"
+ android:layout_alignStart="@+id/start_btn" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:id="@+id/current_position_tv"
+ android:layout_below="@+id/stop_btn"
+ android:layout_alignLeft="@+id/stop_btn"
+ android:layout_alignStart="@+id/stop_btn"
+ android:layout_marginTop="32dp" />
+
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/mapname_et"
+ android:layout_below="@+id/map_btn"
+ android:layout_alignRight="@+id/map_btn"
+ android:layout_alignEnd="@+id/map_btn"
+ android:layout_toRightOf="@+id/current_position_tv"
+ android:layout_toEndOf="@+id/current_position_tv" />
+
+</RelativeLayout>
--- /dev/null
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context="com.example.xub3.speakerlocator.MainActivity">
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:title="@string/action_settings"
+ app:showAsAction="never" />
+</menu>
--- /dev/null
+<resources>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ </style>
+</resources>
--- /dev/null
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
--- /dev/null
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="fab_margin">16dp</dimen>
+</resources>
--- /dev/null
+<resources>
+ <string name="app_name">SpeakerLocator</string>
+ <string name="action_settings">Settings</string>
+ <string name="gateway_ip">192.168.2.191</string>
+ <string name="mac_1">74:da:38:68:72:84</string>
+ <string name="mac_2">00:24:98:9a:92:ee</string>
+ <string name="mac_3">00:24:98:9a:92:ef</string>
+ <string name="mac_4">00:24:98:98:8a:51</string>
+ <string name="mac_5">00:24:98:98:8a:50</string>
+ <string name="mac_6">00:24:98:9a:89:df</string>
+
+</resources>
--- /dev/null
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+</resources>
--- /dev/null
+package com.example.xub3.speakerlocator;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
--- /dev/null
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.2.3'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
--- /dev/null
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
--- /dev/null
+#Mon Feb 20 08:45:44 PST 2017\r
+distributionBase=GRADLE_USER_HOME\r
+distributionPath=wrapper/dists\r
+zipStoreBase=GRADLE_USER_HOME\r
+zipStorePath=wrapper/dists\r
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip\r
--- /dev/null
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
--- /dev/null
+@if "%DEBUG%" == "" @echo off\r
+@rem ##########################################################################\r
+@rem\r
+@rem Gradle startup script for Windows\r
+@rem\r
+@rem ##########################################################################\r
+\r
+@rem Set local scope for the variables with windows NT shell\r
+if "%OS%"=="Windows_NT" setlocal\r
+\r
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r
+set DEFAULT_JVM_OPTS=\r
+\r
+set DIRNAME=%~dp0\r
+if "%DIRNAME%" == "" set DIRNAME=.\r
+set APP_BASE_NAME=%~n0\r
+set APP_HOME=%DIRNAME%\r
+\r
+@rem Find java.exe\r
+if defined JAVA_HOME goto findJavaFromJavaHome\r
+\r
+set JAVA_EXE=java.exe\r
+%JAVA_EXE% -version >NUL 2>&1\r
+if "%ERRORLEVEL%" == "0" goto init\r
+\r
+echo.\r
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r
+echo.\r
+echo Please set the JAVA_HOME variable in your environment to match the\r
+echo location of your Java installation.\r
+\r
+goto fail\r
+\r
+:findJavaFromJavaHome\r
+set JAVA_HOME=%JAVA_HOME:"=%\r
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe\r
+\r
+if exist "%JAVA_EXE%" goto init\r
+\r
+echo.\r
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r
+echo.\r
+echo Please set the JAVA_HOME variable in your environment to match the\r
+echo location of your Java installation.\r
+\r
+goto fail\r
+\r
+:init\r
+@rem Get command-line arguments, handling Windowz variants\r
+\r
+if not "%OS%" == "Windows_NT" goto win9xME_args\r
+if "%@eval[2+2]" == "4" goto 4NT_args\r
+\r
+:win9xME_args\r
+@rem Slurp the command line arguments.\r
+set CMD_LINE_ARGS=\r
+set _SKIP=2\r
+\r
+:win9xME_args_slurp\r
+if "x%~1" == "x" goto execute\r
+\r
+set CMD_LINE_ARGS=%*\r
+goto execute\r
+\r
+:4NT_args\r
+@rem Get arguments from the 4NT Shell from JP Software\r
+set CMD_LINE_ARGS=%$\r
+\r
+:execute\r
+@rem Setup the command line\r
+\r
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar\r
+\r
+@rem Execute Gradle\r
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r
+\r
+:end\r
+@rem End local scope for the variables with windows NT shell\r
+if "%ERRORLEVEL%"=="0" goto mainEnd\r
+\r
+:fail\r
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r
+rem the _cmd.exe /c_ return code!\r
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1\r
+exit /b 1\r
+\r
+:mainEnd\r
+if "%OS%"=="Windows_NT" endlocal\r
+\r
+:omega\r
--- /dev/null
+include ':app'