]> hydra-www.ietfng.org Git - acmetensortoys-ctfws-android/commitdiff
Overhaul internal architecutre
authorNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Mon, 13 Feb 2017 02:20:14 +0000 (21:20 -0500)
committerNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Mon, 13 Feb 2017 02:20:14 +0000 (21:20 -0500)
GameState objects now have Observers and are not passive objects; they
also encapsulate their own parsers.  The display logic is now an Observer
of the GameState and everything's just a little less janky.

The stage has been set for Wear integration, too, but that's not
happening any time soon.

lib/src/main/java/com/acmetensortoys/ctfwstimer/lib/CtFwSGameState.java
lib/src/main/java/com/acmetensortoys/ctfwstimer/lib/CtFwSMessageFilter.java [deleted file]
mobile/build.gradle
mobile/proguard-rules.pro
mobile/src/main/java/com/acmetensortoys/ctfwstimer/CtFwSCallbacksMQTT.java
mobile/src/main/java/com/acmetensortoys/ctfwstimer/CtFwSDisplayLocal.java [moved from mobile/src/main/java/com/acmetensortoys/ctfwstimer/CtFwSDisplay.java with 70% similarity]
mobile/src/main/java/com/acmetensortoys/ctfwstimer/MainActivity.java
mobile/src/main/java/com/acmetensortoys/ctfwstimer/MainActivityBuildHooks.java [new file with mode: 0644]
mobile/src/main/res/layout/activity_main.xml
mobile/src/noplay/java/com/acmetensortoys/ctfwstimer/MainActivityBuildHooksImpl.java [new file with mode: 0644]

index d86e9da31da55cf04950e88afceb9200c27910ba..2caf7b9bfa89669c5e77d663add0aa2ce59c3a28 100644 (file)
@@ -1,29 +1,25 @@
 package com.acmetensortoys.ctfwstimer.lib;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
 import java.util.NoSuchElementException;
 import java.util.Scanner;
+import java.util.Set;
 
 public class CtFwSGameState {
-    public boolean configured;
-    public long startT;     // NTP seconds for game start
-    public int  setupD;
-    public int  rounds;
-    public int  roundD;
-    public long endT = 0;   // NTP seconds for game end (if >= startT)
 
-    public int  flagsTotal;
-    public boolean flagsVisible = false;
-    public int  flagsRed = 0;
-    public int  flagsYel = 0;
+    // Game time
 
-    public void setFlags(boolean visible) {
-        flagsVisible = visible;
-    }
-    public void setFlags(int red, int yel) {
-        flagsRed = red; flagsYel = yel;
-    }
+    private boolean configured = false;
+    private long startT;     // NTP seconds for game start
+    private int  setupD;
+    private int  rounds;
+    private int  roundD;
+    private long endT = 0;   // NTP seconds for game end (if >= startT)
 
-    public void mqttConfigMessage(String st) {
+    public void fromMqttConfigMessage(String st) {
         String tm = st.trim();
         switch (tm) {
             case "none":
@@ -43,29 +39,33 @@ public class CtFwSGameState {
                 }
                 break;
         }
+        if (!isMessageTimeWithin(lastMsgTimestamp)) {
+            msgs.clear();
+            notifyMessages();
+        }
+        notifyConfig();
     }
-    public void mqttFlagsMessage(String st) {
-        String tm = st.trim();
-        switch(tm) {
-            case "?":
-                this.setFlags(false);
-                break;
-            default:
-                Scanner s = new Scanner(tm);
-                try {
-                    this.setFlags(true);
-                    this.setFlags(s.nextInt(),s.nextInt());
-                } catch (NumberFormatException e) {
-                    this.setFlags(false);
-                }
+    public String toMqttConfigMessage() {
+        if (!configured) {
+            return "none";
         }
+
+        return String.format(Locale.ROOT, "%d %d %d %d %d", startT, setupD, rounds, roundD, flagsTotal);
+    }
+    public void deconfigure() {
+        this.configured = false;
+        notifyConfig();
+    }
+    public void setEndT(long endT) {
+        this.endT = endT;
+        notifyConfig();
     }
 
     public class Now {
         public String rationale = null; // null if game is in play, otherwise other fields invalid
+        public boolean stop = false;
         public int round = 0;  // 0 for setup
         public long roundStart = 0, roundEnd = 0; // NTP seconds
-        public boolean stop = false;
     }
     public Now getNow(long now) {
         Now res = new Now();
@@ -77,6 +77,7 @@ public class CtFwSGameState {
             res.stop = true;
         } else if (now <= startT) {
             res.rationale = "Start time in the future!";
+            res.roundStart = startT;
         }
         if (res.rationale != null) {
             return res;
@@ -100,4 +101,123 @@ public class CtFwSGameState {
         res.round += 1;
         return res;
     }
+    public boolean isConfigured(){
+        return configured;
+    }
+    public long getStartT() { return startT; }
+    public long getFirstRoundStartT() { return startT + setupD; }
+    public int getRounds() { return rounds; }
+    public int getComputedGameDuration() { return rounds * roundD ; }
+
+    // Leaves off the natural endT comparison so that messages can be posted after the
+    // game ends and still count as part of this one (i.e. still be displayed).
+    private boolean isMessageTimeWithin(long time) {
+        return !configured  || time >= startT;
+    }
+
+    // Game score
+
+    public int  flagsTotal;
+    public boolean flagsVisible = false;
+    public int  flagsRed = 0;
+    public int  flagsYel = 0;
+
+    public void fromMqttFlagsMessage(String st) {
+        String tm = st.trim();
+        switch(tm) {
+            case "?":
+                flagsVisible = false;
+                break;
+            default:
+                Scanner s = new Scanner(tm);
+                try {
+                    flagsVisible = true;
+                    int red = s.nextInt();
+                    int yel = s.nextInt();
+                    flagsRed = red;
+                    flagsYel = yel;
+                } catch (NumberFormatException e) {
+                    flagsVisible = false;
+                }
+        }
+        notifyFlags();
+    }
+    public String toMqttFlagsMessage() {
+        if (!configured || !flagsVisible) {
+            return "?";
+        }
+
+        return String.format(Locale.ROOT, "%d %d", flagsRed, flagsYel);
+    }
+
+    // Informative messages handling
+
+    public class Msg {
+        public long when;
+        public String msg;
+
+        Msg(long when, String msg) {
+            this.when = when;
+            this.msg  = msg;
+        }
+    }
+    private List<Msg> msgs = new ArrayList<>();
+    private long lastMsgTimestamp;
+
+    public void onNewMessage(String str) {
+        Scanner s = new Scanner(str);
+        long t;
+
+        try {
+            t = s.nextLong();
+        } catch (NoSuchElementException nse) {
+            // Maybe they forgot a time stamp.  That's not ideal, but... fake it?
+            // XXX Back off a bit, for time sync reasons
+            lastMsgTimestamp = System.currentTimeMillis()/1000 - 30;
+            msgs.add(new Msg(lastMsgTimestamp, str));
+            notifyMessages();
+            return;
+        }
+
+        // If there is no configuration, assume the message is new enough
+        // If there *is* a configuration, check the time.
+        if (isMessageTimeWithin(t) && (lastMsgTimestamp <= t)) {
+            s.useDelimiter("\\z");
+            lastMsgTimestamp = t;
+            msgs.add(new Msg(lastMsgTimestamp, s.next().trim()));
+            notifyMessages();
+        }
+    }
+
+    // Observer interface
+
+    public interface Observer {
+        void onCtFwSConfigure(CtFwSGameState game);
+        void onCtFwSFlags(CtFwSGameState game);
+        void onCtFwSMessage(CtFwSGameState game, List<Msg> msgs);
+
+    }
+    final private Set<Observer> mObsvs = new HashSet<>();
+    private void notifyFlags() {
+        synchronized(this) {
+            for (Observer o : mObsvs) { o.onCtFwSFlags(this); }
+        }
+    }
+    private void notifyMessages() {
+        synchronized(this) {
+            for (Observer o : mObsvs) { o.onCtFwSMessage(this, msgs); }
+        }
+    }
+    private void notifyConfig() {
+        synchronized(this) {
+            for (Observer o : mObsvs) { o.onCtFwSConfigure(this); }
+        }
+    }
+    public void registerObserver(Observer d) {
+        synchronized(this) { mObsvs.add(d); }
+    }
+    public void unregisterObserver(Observer d) {
+        synchronized(this) { mObsvs.remove(d); }
+    }
+
 }
diff --git a/lib/src/main/java/com/acmetensortoys/ctfwstimer/lib/CtFwSMessageFilter.java b/lib/src/main/java/com/acmetensortoys/ctfwstimer/lib/CtFwSMessageFilter.java
deleted file mode 100644 (file)
index be919df..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.acmetensortoys.ctfwstimer.lib;
-
-import java.util.NoSuchElementException;
-import java.util.Scanner;
-
-public class CtFwSMessageFilter {
-    final private CtFwSGameState mCgs;
-    public CtFwSMessageFilter(CtFwSGameState cgs) {
-        mCgs = cgs;
-    }
-
-    public class Msg {
-        public long when;
-        public String msg;
-
-        public Msg(long when, String msg) {
-            this.when = when;
-            this.msg  = msg;
-        }
-    }
-
-    private long lastMsgTimestamp = 0;
-
-    public Msg filter(String str) {
-        Scanner s = new Scanner(str);
-        long t;
-        try {
-            t = s.nextLong();
-        } catch (NoSuchElementException nse) {
-            // Maybe they forgot a time stamp.  That's not ideal, but... fake it?
-            // XXX Back off a bit, for time sync reasons
-            lastMsgTimestamp = System.currentTimeMillis()/1000 - 30;
-            return new Msg(lastMsgTimestamp, str);
-        }
-
-        // If there is no configuration, assume the message is new enough
-        // If there *is* a configuration, check the time.
-        if ((!mCgs.configured  || t >= mCgs.startT) && (lastMsgTimestamp <= t)) {
-            s.useDelimiter("\\z");
-            lastMsgTimestamp = t;
-            return new Msg(lastMsgTimestamp, s.next().trim());
-        }
-
-        return null;
-    }
-}
index b4c4015e1122cb3cc5ed4b7846d641dc2ab9999f..7a763c9e0e00ca7d8aca5e28c56dff724a358087 100644 (file)
@@ -2,18 +2,18 @@ apply plugin: 'com.android.application'
 
 android {
     compileSdkVersion 25
-    buildToolsVersion "24.0.3"
+    buildToolsVersion "25.0.2"
     defaultConfig {
         applicationId "com.acmetensortoys.ctfwstimer"
         minSdkVersion 19
-        targetSdkVersion 24
+        targetSdkVersion 25
         versionCode 1
         versionName "1.0"
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
     buildTypes {
         release {
-            minifyEnabled false
+            minifyEnabled true
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
         debug {
@@ -21,11 +21,21 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
+
+    flavorDimensions "play"
+    productFlavors {
+        noplay {
+            dimension "play"
+        }
+        play {
+            dimension "play"
+        }
+    }
 }
 
 repositories {
     maven {
-        url "https://repo.eclipse.org/content/repositories/paho-releases/"
+        url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
     }
 }
 
@@ -38,11 +48,15 @@ dependencies {
 
     compile project(":lib")
 
-    compile('org.eclipse.paho:org.eclipse.paho.android.service:1.1.1') {
-        exclude module: 'support-v4'
-    }
     compile 'com.android.support:appcompat-v7:25.1.1'
-    compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
     compile 'com.android.support:support-v4:25.1.1'
+
+    playCompile 'com.google.android.gms:play-services:10.0.1'
+
+    compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.1-SNAPSHOT'
+    compile('org.eclipse.paho:org.eclipse.paho.android.service:1.1.2-SNAPSHOT') {
+        exclude module: 'support-v4'
+    }
+
     testCompile 'junit:junit:4.12'
 }
index 4e7b00d8d0cf9120fd54454ff71254573e2c0bcd..5538e6c51b26860da45ae070ccd4d794ef255ea8 100644 (file)
@@ -14,9 +14,4 @@
 # class:
 #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 #   public *;
-#}
-
-# Work around some Paho packaging warnings that manifest as errors.
-# See http://github.com/eclipse/paho.mqtt.android/issues/79
--keepattributes InnerClasses
--dontoptimize
\ No newline at end of file
+#}
\ No newline at end of file
index 30e43dfec3c27b75c35f470ac7044c9f0157d060..27d06e4b797fa784423f5c49dfdd2424a2d5700f 100644 (file)
@@ -3,23 +3,15 @@ package com.acmetensortoys.ctfwstimer;
 import android.util.Log;
 
 import com.acmetensortoys.ctfwstimer.lib.CtFwSGameState;
-import com.acmetensortoys.ctfwstimer.lib.CtFwSMessageFilter;
 
 import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
 import org.eclipse.paho.client.mqttv3.MqttMessage;
 
-import java.util.NoSuchElementException;
-import java.util.Scanner;
-
 class CtFwSCallbacksMQTT {
-    final private CtFwSDisplay mCdl;
     final private CtFwSGameState mCgs;
-    final private CtFwSMessageFilter mCmf;
 
-    CtFwSCallbacksMQTT(CtFwSDisplay cdl, CtFwSGameState cgs) {
-        mCdl = cdl;
+    CtFwSCallbacksMQTT(CtFwSGameState cgs) {
         mCgs = cgs;
-        mCmf = new CtFwSMessageFilter(mCgs);
     }
 
     IMqttMessageListener onConfig = new IMqttMessageListener() {
@@ -27,8 +19,7 @@ class CtFwSCallbacksMQTT {
         public void messageArrived(String topic, MqttMessage message) throws Exception {
             String tm = message.toString().trim();
             Log.d("CtFwS", "Message(Config): " + tm);
-            mCgs.mqttConfigMessage(tm);
-            mCdl.notifyGameState();
+            mCgs.fromMqttConfigMessage(tm);
         }
     };
 
@@ -36,12 +27,13 @@ class CtFwSCallbacksMQTT {
         @Override
         public void messageArrived(String topic, MqttMessage message) throws Exception {
             Log.d("CtFwS", "Message(End): " + message);
+            long endT;
             try {
-                mCgs.endT = Long.parseLong(message.toString());
+                endT = Long.parseLong(message.toString());
             } catch (NumberFormatException e) {
-                mCgs.endT = 0;
+                endT = 0;
             }
-            mCdl.notifyGameState();
+            mCgs.setEndT(endT);
         }
     };
 
@@ -50,24 +42,16 @@ class CtFwSCallbacksMQTT {
         public void messageArrived(String topic, MqttMessage message) throws Exception {
             String tm = message.toString().trim();
             Log.d("CtFwS", "Message(Flags): " + tm);
-            mCgs.mqttFlagsMessage(tm);
-            mCdl.notifyFlags();
+            mCgs.fromMqttFlagsMessage(tm);
         }
     };
 
-    private void onMessageCommon(String str) {
-        CtFwSMessageFilter.Msg m = mCmf.filter(str);
-        if (m != null) {
-            mCdl.notifyMessage(m.when, m.msg);
-        }
-    }
-
     IMqttMessageListener onMessage = new IMqttMessageListener() {
         @Override
         public void messageArrived(String topic, MqttMessage message) throws Exception {
             String str = message.toString();
             Log.d("CtFwS", "Message(Broadcast): " + str);
-            onMessageCommon(str);
+            mCgs.onNewMessage(str);
         }
     };
 
@@ -76,7 +60,7 @@ class CtFwSCallbacksMQTT {
         public void messageArrived(String topic, MqttMessage message) throws Exception {
             String str = message.toString();
             Log.d("CtFwS", "Message(Players): " + str);
-            onMessageCommon(str);
+            mCgs.onNewMessage(str);
         }
     };
 }
similarity index 70%
rename from mobile/src/main/java/com/acmetensortoys/ctfwstimer/CtFwSDisplay.java
rename to mobile/src/main/java/com/acmetensortoys/ctfwstimer/CtFwSDisplayLocal.java
index f4b9adcff6ae5f83ff5991679e07d26458eb945f..a89eabf26ea15185a86b637639b0ad4440e37c65 100644 (file)
@@ -5,40 +5,43 @@ import android.os.Handler;
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 import android.util.Log;
+import android.view.View;
 import android.widget.Chronometer;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import com.acmetensortoys.ctfwstimer.lib.CtFwSGameState;
 
-// TODO nwf is bad at UI design; someone who isn't him should improve this
+import java.util.List;
+
+import static android.view.View.INVISIBLE;
 
-class CtFwSDisplay {
+// TODO nwf is bad at UI design; someone who isn't him should improve this
+class CtFwSDisplayLocal implements CtFwSGameState.Observer {
     final private Activity mAct;
     final private Handler mHandler;
-    final private CtFwSGameState mCgs;
 
-    private long lastMsgTimeMS = 0;
-
-    CtFwSDisplay(Activity a, Handler h, CtFwSGameState cgs) {
+    CtFwSDisplayLocal(Activity a, Handler h) {
         mAct = a;
         mHandler = h;
-        mCgs = cgs;
     }
 
-    final private Runnable mProber = new Runnable() {
-        @Override
-        public void run() {
-            notifyGameState();
-        }
-    };
+    private Runnable mProber;
 
-    void notifyGameState() {
+    @Override
+    public void onCtFwSConfigure(final CtFwSGameState gs) {
         final long nowMS = System.currentTimeMillis();
         long nowET = SystemClock.elapsedRealtime(); // Chronometer timebase
         final long tbcf = nowMS - nowET; // time base correction factor ("when we booted"-ish)
 
-        final CtFwSGameState.Now now = mCgs.getNow(nowMS / 1000);
+        final CtFwSGameState.Now now = gs.getNow(nowMS / 1000);
+        final Runnable prober = new Runnable() {
+            @Override
+            public void run() {
+                onCtFwSConfigure(gs);
+            }
+        };
+
 
         Log.d("CtFwS", "Display game state; nowMS=" + nowMS + " r=" + now.round + " rs=" + now.roundStart + " re=" + now.roundEnd);
 
@@ -49,9 +52,12 @@ class CtFwSDisplay {
 
             doReset();
 
-            mHandler.removeCallbacks(mProber);
+            if (mProber != null) {
+                mHandler.removeCallbacks(mProber);
+            }
             if (!now.stop) {
-                mHandler.postDelayed(mProber, mCgs.startT*1000 - nowMS);
+                mProber = prober;
+                mHandler.postDelayed(mProber, now.roundStart*1000 - nowMS);
             }
 
             return;
@@ -59,12 +65,11 @@ class CtFwSDisplay {
 
         // Otherwise, it's game on!
 
-        // Clear the mesage log if it looks like it's a new game in play
-        if (lastMsgTimeMS < mCgs.startT * 1000) {
-            clearMsgs();
-        }
-
+        // Schedule a callback around the time of the next round; if we're early,
+        // that's fine, we'll schedule it again.  If we're late, it'll be glitchy,
+        // but that's fine.
         mHandler.removeCallbacks(mProber);
+        mProber = prober;
         mHandler.postDelayed(mProber, now.roundEnd * 1000 - nowMS);
 
         {
@@ -74,7 +79,7 @@ class CtFwSDisplay {
                 public void run() {
                     if (now.round == 0) {
                         tv_jb.setText(R.string.ctfws_gamestart);
-                    } else if (now.round == mCgs.rounds) {
+                    } else if (now.round == gs.getRounds()) {
                         tv_jb.setText(R.string.ctfws_gameend);
                     } else {
                         tv_jb.setText(
@@ -89,11 +94,7 @@ class CtFwSDisplay {
                 @Override
                 public void run() {
                     pb_jb.setIndeterminate(false);
-                    if (now.round == 0) {
-                        pb_jb.setMax(mCgs.setupD - 1);
-                    } else {
-                        pb_jb.setMax(mCgs.roundD - 1);
-                    }
+                    pb_jb.setMax((int)(now.roundEnd - now.roundStart));
                     pb_jb.setProgress(0);
                 }
             });
@@ -102,11 +103,11 @@ class CtFwSDisplay {
             ch_jb.post(new Runnable() {
                 @Override
                 public void run() {
-                    ch_jb.setBase(now.roundEnd * 1000 - tbcf);
+                    ch_jb.setBase((now.roundEnd + 1) * 1000 - tbcf);
                     ch_jb.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
                         @Override
                         public void onChronometerTick(Chronometer c) {
-                            pb_jb.setProgress((int)(now.roundEnd - System.currentTimeMillis()/1000) - 1);
+                            pb_jb.setProgress((int)(now.roundEnd - System.currentTimeMillis()/1000));
                         }
                     });
                     ch_jb.start();
@@ -119,7 +120,7 @@ class CtFwSDisplay {
                 @Override
                 public void run() {
                     pb_gp.setIndeterminate(false);
-                    pb_gp.setMax(mCgs.rounds * mCgs.roundD - 1);
+                    pb_gp.setMax(gs.getComputedGameDuration());
                     pb_gp.setProgress(0);
                 }
             });
@@ -128,14 +129,15 @@ class CtFwSDisplay {
             ch_gp.post(new Runnable() {
                 @Override
                 public void run() {
-                    ch_gp.setBase((mCgs.startT + mCgs.setupD) * 1000 - tbcf);
+                    ch_gp.setBase(gs.getFirstRoundStartT() * 1000 - tbcf);
                     ch_gp.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
                         @Override
                         public void onChronometerTick(Chronometer c) {
                             pb_gp.setProgress((int)(System.currentTimeMillis()/1000
-                                    - mCgs.startT - mCgs.setupD));
+                                    - gs.getFirstRoundStartT()));
                         }
                     });
+                    ch_gp.setVisibility(View.VISIBLE);
                     ch_gp.start();
                 }
             });
@@ -144,9 +146,7 @@ class CtFwSDisplay {
             pb_gp.post(new Runnable() {
                 @Override
                 public void run() {
-                    pb_gp.setIndeterminate(false);
-                    pb_gp.setMax(mCgs.rounds * mCgs.roundD - 1);
-                    pb_gp.setProgress(0);
+                    pb_gp.setIndeterminate(true);
                 }
             });
 
@@ -154,9 +154,8 @@ class CtFwSDisplay {
             ch_gp.post(new Runnable() {
                 @Override
                 public void run() {
-                    ch_gp.setBase(nowMS - tbcf);
-                    ch_gp.setOnChronometerTickListener(null);
                     ch_gp.stop();
+                    ch_gp.setVisibility(INVISIBLE);
                 }
             });
         }
@@ -166,8 +165,7 @@ class CtFwSDisplay {
                 @Override
                 public void run() {
                     tv_flags.setText(
-                            String.format(mAct.getResources().getString(R.string.ctfws_flags),
-                                    mCgs.flagsTotal));
+                            String.format(mAct.getResources().getString(R.string.ctfws_flags), gs.flagsTotal));
                 }
             });
         }
@@ -192,11 +190,9 @@ class CtFwSDisplay {
             ch.post(new Runnable() {
                 @Override
                 public void run() {
-                    ch.setOnChronometerTickListener(null);
-                    ch.setBase(SystemClock.elapsedRealtime());
                     ch.stop();
-                }
-            });
+                    ch.setVisibility(View.INVISIBLE);
+                }});
         }
         {
             final ProgressBar pb = (ProgressBar) (mAct.findViewById(R.id.pb_jailbreak));
@@ -218,16 +214,17 @@ class CtFwSDisplay {
         }
     }
 
-    void notifyFlags() {
+    @Override
+    public void onCtFwSFlags(CtFwSGameState gs) {
         // TODO: This stinks
 
         final StringBuffer sb = new StringBuffer();
-        if (mCgs.configured) {
-            if (mCgs.flagsVisible) {
+        if (gs.isConfigured()) {
+            if (gs.flagsVisible) {
                 sb.append("r=");
-                sb.append(mCgs.flagsRed);
+                sb.append(gs.flagsRed);
                 sb.append(" y=");
-                sb.append(mCgs.flagsYel);
+                sb.append(gs.flagsYel);
             } else {
                 sb.append("r=? y=?");
             }
@@ -242,30 +239,36 @@ class CtFwSDisplay {
         });
     }
 
-    void clearMsgs() {
-        final TextView msgs = (TextView) (mAct.findViewById(R.id.msgs));
-        msgs.post(new Runnable() {
-            @Override
-            public void run() {
-                msgs.setText("");
-            }
-        });
-    }
+    @Override
+    public void onCtFwSMessage(CtFwSGameState gs, List<CtFwSGameState.Msg> msgs) {
+        final TextView msgstv = (TextView)(mAct.findViewById(R.id.msgs));
+        int s = msgs.size();
 
-    void notifyMessage(long ts, String m) {
-        final StringBuffer sb = new StringBuffer();
-        long td = (ts == 0) ? 0 : (mCgs.configured) ? ts - mCgs.startT : 0;
-        sb.append(DateUtils.formatElapsedTime(td));
-        sb.append(": ");
-        sb.append(m);
-        sb.append("\n");
+        if (s == 0) {
+            msgstv.post(new Runnable() {
+                @Override
+                public void run() {
+                    msgstv.setText("");
+                }
+            });
+        } else {
 
-        final TextView msgs = (TextView)(mAct.findViewById(R.id.msgs));
-        msgs.post(new Runnable() {
-            @Override
-            public void run() {
-                msgs.append(sb);
-            }
-        });
+            CtFwSGameState.Msg m = msgs.get(s - 1);
+
+            long td = (m.when == 0) ? 0 : (gs.isConfigured()) ? m.when - gs.getStartT() : 0;
+
+            final StringBuffer sb = new StringBuffer();
+            sb.append(DateUtils.formatElapsedTime(td));
+            sb.append(": ");
+            sb.append(m.msg);
+            sb.append("\n");
+
+            msgstv.post(new Runnable() {
+                @Override
+                public void run() {
+                    msgstv.append(sb);
+                }
+            });
+        }
     }
 }
index 83599b998ab8cc021e1bd6a17de1774cb51f3567..277ce4212cb5d807cbab2a1620af84bdc22493a4 100644 (file)
@@ -17,13 +17,11 @@ import android.view.View;
 import android.widget.TextView;
 
 import org.eclipse.paho.android.service.MqttAndroidClient;
-import org.eclipse.paho.android.service.MqttTraceHandler;
 import org.eclipse.paho.client.mqttv3.IMqttActionListener;
 import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
 import org.eclipse.paho.client.mqttv3.IMqttToken;
 import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
 import org.eclipse.paho.client.mqttv3.MqttClient;
-import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
 import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
 import org.eclipse.paho.client.mqttv3.MqttException;
 import org.eclipse.paho.client.mqttv3.MqttMessage;
@@ -35,9 +33,10 @@ public class MainActivity extends AppCompatActivity {
     private MqttAndroidClient mMqc;
 
     private final CtFwSGameState mCgs = new CtFwSGameState();
-    private CtFwSDisplay mCdl; // set in onCreate
     private CtFwSCallbacksMQTT mCtfwscbs ; // set in onCreate
 
+    private MainActivityBuildHooks mabh = new MainActivityBuildHooksImpl();
+
     private TextView mTvSU; // set in onCreate
     private TextView mTvSS; // set in onCreate
     private void setServerStateText(@StringRes final int resid) {
@@ -120,14 +119,12 @@ public class MainActivity extends AppCompatActivity {
         public void onSuccess(IMqttToken asyncActionToken) {
             Log.d("CtFwS", "Conn OK 1");
             setServerStateText(R.string.mqtt_conn);
-            mCdl.clearMsgs();
         }
 
         @Override
         public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
             Log.e("CtFws", "Conn Fail", exception);
             setServerStateText(R.string.mqtt_disconn);
-            mCdl.clearMsgs();
         }
     };
 
@@ -145,8 +142,7 @@ public class MainActivity extends AppCompatActivity {
                 }
             }
             mMqc = null;
-            mCgs.configured = false;
-            mCdl.notifyGameState();
+            mCgs.deconfigure();
         }
 
         // If that's all we were told to do, we're done
@@ -206,7 +202,7 @@ public class MainActivity extends AppCompatActivity {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        String defserver = "tcp://nwf1.xen.prgmr.com:1883";
+        String defserver = "tcp://ctfws-mqtt.ietfng.org:1883";
 
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
@@ -214,8 +210,12 @@ public class MainActivity extends AppCompatActivity {
         mTvSU = (TextView) findViewById(R.id.tv_mqtt_server_uri);
         mTvSS = (TextView) findViewById(R.id.tv_mqtt_state);
 
-        mCdl = new CtFwSDisplay(this, new Handler(), mCgs);
-        mCtfwscbs = new CtFwSCallbacksMQTT(mCdl, mCgs);
+        CtFwSDisplayLocal mCdl = new CtFwSDisplayLocal(this, new Handler());
+        mCgs.registerObserver(mCdl);
+
+        mabh.onCreate(mCgs);
+
+        mCtfwscbs = new CtFwSCallbacksMQTT(mCgs);
 
         SharedPreferences sp = getPreferences(MODE_PRIVATE);
         if (sp.getString("server", null) == null) {
diff --git a/mobile/src/main/java/com/acmetensortoys/ctfwstimer/MainActivityBuildHooks.java b/mobile/src/main/java/com/acmetensortoys/ctfwstimer/MainActivityBuildHooks.java
new file mode 100644 (file)
index 0000000..63bea17
--- /dev/null
@@ -0,0 +1,10 @@
+package com.acmetensortoys.ctfwstimer;
+
+import com.acmetensortoys.ctfwstimer.lib.CtFwSGameState;
+
+// The MainActivity expects a "MainActivityBuildHooksImpl" class that ascribes to this interface
+// per build flavor.  This will be used when, for example, we kick on Google Play for Wear
+// interaction and want to push messages out to the wearable data network.
+interface MainActivityBuildHooks {
+    void onCreate(CtFwSGameState cgs);
+}
index 952f7a41bfe865783be7d580b3504b3a3d6f10c5..f6c88f21cb64f86fee691089388be4c37359c1dc 100644 (file)
                 <Chronometer
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:id="@+id/ch_gameProgress" />
+                    android:id="@+id/ch_gameProgress"
+                    android:visibility="invisible" />
 
             </TableRow>
 
diff --git a/mobile/src/noplay/java/com/acmetensortoys/ctfwstimer/MainActivityBuildHooksImpl.java b/mobile/src/noplay/java/com/acmetensortoys/ctfwstimer/MainActivityBuildHooksImpl.java
new file mode 100644 (file)
index 0000000..88b0d04
--- /dev/null
@@ -0,0 +1,10 @@
+package com.acmetensortoys.ctfwstimer;
+
+import com.acmetensortoys.ctfwstimer.lib.CtFwSGameState;
+
+public class MainActivityBuildHooksImpl implements MainActivityBuildHooks {
+    @Override
+    public void onCreate(CtFwSGameState cgs) {
+        // No-op
+    }
+}
\ No newline at end of file