]> hydra-www.ietfng.org Git - acmetensortoys-teled/commitdiff
Some initial progress?
authorNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Fri, 29 Jan 2016 07:31:04 +0000 (02:31 -0500)
committerNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Fri, 29 Jan 2016 07:31:04 +0000 (02:31 -0500)
Learning Android is hard.

mobile/build.gradle
mobile/src/main/AndroidManifest.xml
mobile/src/main/java/com/acmetensortoys/android/teled/MainActivity.java
mobile/src/main/java/com/acmetensortoys/android/teled/SMSRecv.java [new file with mode: 0644]
mobile/src/main/java/com/acmetensortoys/android/teled/TeleDIOIOService.java [new file with mode: 0644]
mobile/src/main/java/com/acmetensortoys/android/teled/Thoughts [new file with mode: 0644]
wear/build.gradle

index 11d0d790806546752e6af12ce3a3c26bd2ebae7d..416738e62262bd345e0dae06fc7a3b31316533af 100644 (file)
@@ -19,11 +19,19 @@ android {
     }
 }
 
+// IOIO support
+dependencies {
+    compile 'com.github.ytai.ioio:IOIOLibAndroid:5.07'
+    compile 'com.github.ytai.ioio:IOIOLibAndroidBluetooth:5.07'
+    compile 'com.github.ytai.ioio:IOIOLibAndroidAccessory:5.07'
+    compile 'com.github.ytai.ioio:IOIOLibAndroidDevice:5.07'
+}
+
 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
     wearApp project(':wear')
     testCompile 'junit:junit:4.12'
     compile 'com.android.support:appcompat-v7:23.1.1'
-    compile 'com.google.android.gms:play-services:8.4.0'
+    compile 'com.google.android.gms:play-services:+'
     compile 'com.android.support:design:23.1.1'
 }
index ad8d91459c32ca05f710365627b7249dac568e61..201b22f451e10b2a7e8d3a63f0ad63420f804066 100644 (file)
@@ -3,6 +3,10 @@
     package="com.acmetensortoys.android.teled">
 
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+    <uses-permission android:name="android.permission.BROADCAST_SMS" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.INTERNET" />
 
     <application
         android:allowBackup="true"
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <service
+            android:name=".TeleDIOIOService"
+            android:enabled="true"></service>
+
+        <receiver
+            android:name=".SMSRecv"
+            android:enabled="true"
+            android:exported="true"
+            android:permission="android.permission.BROADCAST_SMS">
+            <intent-filter>
+                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
+            </intent-filter>
+        </receiver>
     </application>
 
 </manifest>
index 284a3abbdd2309662fede843e08c848ee3a33263..d96a71df4048858f9ffec0f87f7bda06e1fc75f7 100644 (file)
@@ -1,5 +1,10 @@
 package com.acmetensortoys.android.teled;
 
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.ComponentName;
+import android.os.IBinder;
 import android.os.Bundle;
 import android.support.design.widget.FloatingActionButton;
 import android.support.design.widget.Snackbar;
@@ -12,6 +17,7 @@ import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.util.Log;
 
 public class MainActivity extends AppCompatActivity
         implements NavigationView.OnNavigationItemSelectedListener {
@@ -74,6 +80,20 @@ public class MainActivity extends AppCompatActivity
         return super.onOptionsItemSelected(item);
     }
 
+    private TeleDIOIOService tdis = null;
+    private final ServiceConnection tdisSC = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName cn, IBinder service) {
+            Log.d("Main", "TDIS conn");
+            tdis = ((TeleDIOIOService.LocalBinder) service).getService();
+        }
+        @Override
+        public void onServiceDisconnected(ComponentName cn) {
+            Log.d("Main", "TDIS discon");
+            tdis = null;
+        }
+    };
+
     @SuppressWarnings("StatementWithEmptyBody")
     @Override
     public boolean onNavigationItemSelected(MenuItem item) {
@@ -89,13 +109,35 @@ public class MainActivity extends AppCompatActivity
         } else if (id == R.id.nav_manage) {
 
         } else if (id == R.id.nav_share) {
-
+            if(tdis != null) {
+                Log.d("Main", "Share w/ non-null...");
+                tdis.setForceFeedbackBehavior(true);
+            }
         } else if (id == R.id.nav_send) {
-
+            Intent i = new Intent(this, TeleDIOIOService.class);
+            Log.d("Main", "Send...");
+            startService(i);
+            bindService(i, tdisSC, Context.BIND_AUTO_CREATE);
         }
 
         DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
         drawer.closeDrawer(GravityCompat.START);
         return true;
     }
+
+    @Override
+    public void onDestroy(){
+        unbindService(tdisSC);
+        super.onDestroy();
+    }
 }
+
+/* TODO:
+ *
+ *  * A settings activity with SMS configuration, including authentication
+ *    information and an enable button.
+ *
+ *  * A settings activity with Bluetooth configuration and maybe USB?
+ *
+ *  * Spawn the IOIO service on startup if it isn't and see it work.
+ */
diff --git a/mobile/src/main/java/com/acmetensortoys/android/teled/SMSRecv.java b/mobile/src/main/java/com/acmetensortoys/android/teled/SMSRecv.java
new file mode 100644 (file)
index 0000000..4a4b5b0
--- /dev/null
@@ -0,0 +1,31 @@
+package com.acmetensortoys.android.teled;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class SMSRecv extends BroadcastReceiver {
+    public SMSRecv() {
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        // TODO: This method is called when the BroadcastReceiver is receiving
+        // an Intent broadcast.
+        throw new UnsupportedOperationException("Not yet implemented");
+
+        /* NWF planning aloud here.
+         *
+         * We should check that we should even be paying attention to SMS;
+         * they may be disabled.
+         *
+         * We're going to check some authentication present in the incoming
+         * message -- source number, signed message hash, etc. -- and are then
+         * going to decode it in full.
+         *
+         * The message should contain a sequence number that we echo back,
+         * Since SMS's may reorder on the wire, let's just not support
+         * multiple outstanding messages; simpler on us this way...
+         */
+    }
+}
diff --git a/mobile/src/main/java/com/acmetensortoys/android/teled/TeleDIOIOService.java b/mobile/src/main/java/com/acmetensortoys/android/teled/TeleDIOIOService.java
new file mode 100644 (file)
index 0000000..5d34acc
--- /dev/null
@@ -0,0 +1,184 @@
+// Based on IOIOService example from ytai's repository
+
+package com.acmetensortoys.android.teled;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Binder;
+import android.util.Log;
+
+import ioio.lib.api.AnalogInput;
+import ioio.lib.api.DigitalOutput;
+import ioio.lib.api.IOIO;
+import ioio.lib.api.PwmOutput;
+import ioio.lib.api.exception.ConnectionLostException;
+import ioio.lib.util.BaseIOIOLooper;
+import ioio.lib.util.IOIOLooper;
+import ioio.lib.util.android.IOIOService;
+
+/**
+ * An example IOIO service. While this service is alive, it will attempt to
+ * connect to a IOIO and blink the LED. A notification will appear on the
+ * notification bar, enabling the user to stop the service.
+ */
+public class TeleDIOIOService extends IOIOService {
+    private static final int NOTIFY_ID = 0;
+
+
+    /*
+    public abstract class Command {
+        abstract public void behave()
+                throws ConnectionLostException, InterruptedException;
+    };
+
+
+    public class BlinkOnce extends Command {
+        private int duration;
+        public BlinkOnce(int dur) { duration = dur; }
+        public void behave()
+                throws ConnectionLostException, InterruptedException {
+            led_.write(true);
+            Thread.sleep(duration);
+            led_.write(false);
+        }
+    }
+
+    private LinkedBlockingQueue<Command> ioioq;
+    */
+
+    private boolean forceFeedbackBehavior = false;
+    private synchronized void awaitAnyBehavior() {
+        while(!forceFeedbackBehavior) {
+            try {
+                this.wait();
+            } catch (InterruptedException e) {
+                // ignored
+            }
+        }
+    }
+
+    public void setForceFeedbackBehavior(boolean nv) {
+        Log.d("TDIS", "sffb");
+        if(forceFeedbackBehavior && !nv) {
+            synchronized (this) {
+                forceFeedbackBehavior = false;
+            }
+        } else if (!forceFeedbackBehavior && nv) {
+            synchronized(this) {
+                forceFeedbackBehavior = true;
+                this.notifyAll();
+            }
+        }
+    }
+
+    @Override
+    public IOIOLooper createIOIOLooper(String type, Object info) {
+        return new BaseIOIOLooper() {
+            private DigitalOutput boardLED;
+
+            private AnalogInput forceSensor1;
+            private long forceSensor1dix;
+
+            // XXX Warmer controls?
+
+            // XXX This is not how it will be in the final version
+            private DigitalOutput motorEnable;
+            private PwmOutput motorPwm;
+
+            @Override
+            protected void setup() throws ConnectionLostException,
+                    InterruptedException {
+                Log.d("TDIS", "Looper setup");
+
+                boardLED = ioio_.openDigitalOutput(IOIO.LED_PIN, false);
+
+                forceSensor1 = ioio_.openAnalogInput(31);
+                forceSensor1.setBuffer(50);
+
+                motorEnable = ioio_.openDigitalOutput(1,false); // active low
+                motorPwm = ioio_.openPwmOutput(2,100);
+
+                forceSensor1dix = 0;
+
+                Log.d("TDIS", "Looper setup finish");
+            }
+
+            @Override
+            public void loop() throws ConnectionLostException,
+                    InterruptedException {
+
+                awaitAnyBehavior();
+                if (forceFeedbackBehavior) {
+                    boolean did = forceSensor1.available() > 0;
+                    float v = 0.0f;
+                    while (forceSensor1.available() > 0) {
+                        v = forceSensor1.getVoltageBuffered();
+                        forceSensor1dix++;
+                    }
+                    if (did) {
+                        motorPwm.setDutyCycle(v / 3.3f);
+                    }
+                }
+            }
+
+            @Override
+            public void disconnected() { }
+        };
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d("TDIS", "onStartCommand");
+
+        int result = super.onStartCommand(intent, flags, startId);
+        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+        if (intent != null && intent.getAction() != null
+                && intent.getAction().equals("stop")) {
+            // User clicked the notification. Need to stop the service.
+            Log.d("TDIS", "stopping...");
+
+            setForceFeedbackBehavior(false);
+
+            // nm.cancel(0);
+            // stopSelf();
+        } else {
+            Notification.Builder nb = new Notification.Builder(this);
+
+            nm.notify(NOTIFY_ID,
+                    nb.setContentIntent(PendingIntent.getService(this, 0, new Intent(
+                            "stop", null, this, this.getClass()), 0))
+                            .setSmallIcon(R.drawable.ic_menu_send)
+                            .setContentTitle("TeleD IOIO service running")
+                            .setWhen(System.currentTimeMillis())
+                            .setOngoing(true)
+                            .build());
+        }
+        return result;
+    }
+
+    protected final class LocalBinder extends Binder {
+        TeleDIOIOService getService() { return TeleDIOIOService.this; }
+    }
+    private final IBinder mBinder = new LocalBinder();
+
+    @Override
+    public IBinder onBind(Intent arg0) {
+        Log.d("TDIS", "onBind");
+        return mBinder;
+    }
+
+
+
+
+    /*
+    public void doNotify() {
+        try {
+            //ioioq.put(new BlinkOnce(500));
+        } catch (InterruptedException e) {
+        }
+    }
+    */
+}
\ No newline at end of file
diff --git a/mobile/src/main/java/com/acmetensortoys/android/teled/Thoughts b/mobile/src/main/java/com/acmetensortoys/android/teled/Thoughts
new file mode 100644 (file)
index 0000000..c48c803
--- /dev/null
@@ -0,0 +1,75 @@
+Service Design
+##############
+
+The TeleD IOIO management service is slave to several masters:
+
+   - The IOIO library and connectivity events
+   - Any messages sent its way via the UI or via SMS or...
+
+The IOIO library provides a pretty decent vocabulary of low-level behaviors
+in a relatively generic way.  The TeleD service should probably use a
+higher-level notion of behavior of specific components of the system;
+that is, it should know about the hardware at hand (or elsewhere) and
+expose only high-level directives.
+
+Failure cases are important to consider.  Right now, the policy is likely
+to be thus:
+
+  - If the IOIO goes away, all behaviors are cancelled and callbacks are
+    fired, informing whoever cares to know (SMS, UI, ...).  We cannot
+    shut down due to how Android works, thoug
+
+  - If a behavior times out, that behavior will be stopped (and again
+    callbacks will be fired?)
+
+Protocol Notes
+##############
+
+We should assume that SMS is high-latency and out-of-order.
+
+Every message should have an identifier at its top and be a series
+of commands which will be replied to in order (as a single message,
+after the whole thing has been executed?)
+
+That also means we should probably require that every command, other than those
+that quiesce some activity, have a duration of up to a minute or so? explicitly
+contained within them. (If you want longer commands, send many messages?)
+Let's see.... what do we want in the control stream?
+
+One answer, I suppose, is straightfoward on/off commands and some kind of readback::
+
+  PWM_SET <channel> <level> <duration if level != 0>
+  WAIT <duration>
+  READ_VAR <name>
+
+  PWM_OK ?
+  VAR_READ <value>
+
+Perhaps a little better, rather than something quite that simple, is an
+IOIO-Sequencer like approach, where we can send over a stream of motion
+commands at once, which are *enqueued*, with the head taking effect either
+immediately (and the rest of the queue being jettisoned) or are appended
+to the command queue. ::
+
+  SEQ_STALL <channel> <stall behavior>
+  SEQ_CMD <channel> <append?> {<level> <duration>}*
+  READ_VAR <name>
+
+  SEQ_OK ?
+  VAR_READ <name> <value>
+
+An answer I kind of like better is, of course, to borrow from the
+hyperbola-chiptunes project of years gone by and to have a notion of
+instruments / tracks / measures / scores with channels and conditionals.
+
+* Instruments are like (pre-loaded) sequencer command chains at
+  a fine level (they're like individual waveforms)
+
+* Tracks are like sequencer command chains at a coarser level
+  and reference instruments for their moment-to-moment behavior.
+  That is, a track is a sequence of instruments (and other data?)
+
+* Measures step several tracks in tandem.
+
+* Scores are a sequence of frames together with a channel-to-actuator
+  map and conditional control flow and some vague notion of interrupts...
\ No newline at end of file
index 8c198d37eea130d85635dc3c95b7bc2d803e5bd5..327aad24776368046a6767414af07f2b3a131747 100644 (file)
@@ -20,8 +20,16 @@ android {
     }
 }
 
+// IOIO support
+dependencies {
+    compile 'com.github.ytai.ioio:IOIOLibAndroid:5.07'
+    compile 'com.github.ytai.ioio:IOIOLibAndroidBluetooth:5.07'
+    compile 'com.github.ytai.ioio:IOIOLibAndroidAccessory:5.07'
+    compile 'com.github.ytai.ioio:IOIOLibAndroidDevice:5.07'
+}
+
 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
-    compile 'com.google.android.support:wearable:1.3.0'
-    compile 'com.google.android.gms:play-services-wearable:8.4.0'
+    compile 'com.google.android.support:wearable:+'
+    compile 'com.google.android.gms:play-services-wearable:+'
 }