classMap = new HashMap<>();
+
+ static {
+ classMap.put("ConfigurationItems", ConfigurationItems.class);
+
+ classMap.put("string", String.class);
+ classMap.put("bool", Boolean.class);
+ classMap.put("int", Integer.class);
+ classMap.put("float", Float.class);
+ classMap.put("byte", Byte.class);
+ classMap.put("long", Long.class);
+ classMap.put("short", Short.class);
+ classMap.put("object", Object.class);
+ }
+}
diff --git a/comp_configuration/src/main/java/com/xprize/comp_configuration/ConfigurationItems.java b/comp_configuration/src/main/java/com/xprize/comp_configuration/ConfigurationItems.java
new file mode 100644
index 0000000..5da9d88
--- /dev/null
+++ b/comp_configuration/src/main/java/com/xprize/comp_configuration/ConfigurationItems.java
@@ -0,0 +1,130 @@
+package com.xprize.comp_configuration;
+
+import android.util.Log;
+
+import org.json.JSONObject;
+
+import cmu.xprize.comp_logging.CErrorManager;
+import cmu.xprize.util.ILoadableObject;
+import cmu.xprize.util.IScope;
+import cmu.xprize.util.JSON_Helper;
+import cmu.xprize.util.TCONST;
+
+public class ConfigurationItems implements ILoadableObject {
+
+ private static final String TAG = "ConfigurationItems";
+
+ public static final String CONFIG_VERSION = "CONFIG_VERSION";
+ public static final String LANGUAGE_OVERRIDE = "LANGUAGE_OVERRIDE";
+ public static final String SHOW_TUTOR_VERSION = "SHOW_TUTOR_VERSION";
+ public static final String SHOW_DEBUG_LAUNCHER = "SHOW_DEBUG_LAUNCHER";
+ public static final String LANGUAGE_SWITCHER = "LANGUAGE_SWITCHER";
+ public static final String NO_ASR_APPS = "NO_ASR_APPS";
+ public static final String LANGUAGE_FEATURE_ID = "LANGUAGE_FEATURE_ID";
+ public static final String SHOW_DEMO_VIDS = "SHOW_DEMO_VIDS";
+ public static final String USE_PLACEMENT = "USE_PLACEMENT";
+ public static final String RECORD_AUDIO = "RECORD_AUDIO";
+ public static final String MENU_TYPE = "MENU_TYPE";
+ public static final String RECORDING = "RECORDING";
+ public static final String RECORDING_WITH_AUDIO_ENABLED = "RECORDING_WITH_AUDIO_ENABLED";
+ public static final String SHOW_HELPER_BUTTON = "SHOW_HELPER_BUTTON";
+ public static final String BASE_DIRECTORY = "BASE_DIRECTORY";
+ public static final String PINNING_MODE = "PINNING_MODE";
+
+ public String config_version;
+ public boolean language_override;
+ public boolean show_tutorversion;
+ public boolean show_debug_launcher;
+ public boolean language_switcher;
+ public boolean no_asr_apps;
+ public String language_feature_id;
+ public boolean show_demo_vids;
+ public boolean use_placement;
+ public boolean record_audio;
+ public String menu_type;
+ public boolean recording;
+ public boolean show_helper_button;
+ public String baseDirectory;
+ public boolean recording_with_audio_enabled;
+ public boolean pinning_mode;
+
+ public ConfigurationItems() {
+ String dataPath = TCONST.DOWNLOAD_PATH + "/config.json";
+ String jsonData = JSON_Helper.cacheDataByName(dataPath);
+ Log.e("jsonData", jsonData);
+
+ try {
+ loadJSON(new JSONObject(jsonData), null);
+ /*
+ The JSON object is logged here to make the app logs more identifiable
+ and searchable.
+ */
+ Log.i(TAG, new JSONObject(jsonData).toString(4));
+ this.setConfigVersion();
+ } catch (Exception e) {
+ Log.e(TAG, "Invalid Data Source for : " + dataPath, e);
+ setDefaults();
+ }
+ }
+
+ // used for QuickOptions
+ public ConfigurationItems(String config_version, boolean language_override,
+ boolean show_tutorversion, boolean show_debug_launcher,
+ boolean language_switcher, boolean no_asr_apps,
+ String language_feature_id, boolean show_demo_vids,
+ boolean use_placement, boolean record_audio,
+ String menu_type, boolean recording, boolean recording_with_audio_enabled,
+ boolean show_helper_button, String baseDirectory, boolean pinning_mode) {
+
+// this.config_version = config_version;
+ this.setConfigVersion();
+ this.language_override = language_override;
+ this.show_tutorversion = show_tutorversion;
+ this.show_debug_launcher = show_debug_launcher;
+ this.language_switcher = language_switcher;
+ this.no_asr_apps = no_asr_apps;
+ this.language_feature_id = language_feature_id;
+ this.show_demo_vids = show_demo_vids;
+ this.use_placement = use_placement;
+ this.record_audio = record_audio;
+ this.menu_type = menu_type;
+ this.recording = recording;
+ this.recording_with_audio_enabled = recording_with_audio_enabled;
+ this.show_helper_button = show_helper_button;
+ this.baseDirectory = baseDirectory;
+ this.pinning_mode = pinning_mode;
+ }
+
+ public void setDefaults() {
+ // use the swahili versions as default
+// config_version = "release_sw"; // shouldn't it be fttt...?
+ this.setConfigVersion();
+ language_override = true;
+ show_tutorversion = true;
+ show_debug_launcher = false;
+ language_switcher = false;
+ no_asr_apps = false;
+ language_feature_id = "LANG_SW";
+ show_demo_vids = true;
+ use_placement = true;
+ record_audio = false;
+ menu_type = "CD1";
+ show_helper_button = false;
+ baseDirectory = "roboscreen";
+ recording = true;
+ recording_with_audio_enabled = false;
+ pinning_mode = false;
+ }
+
+ private void setConfigVersion() {
+ String dataPath = TCONST.DOWNLOAD_PATH + "/config.json";
+ String jsonData = JSON_Helper.cacheDataByName(dataPath);
+ String configAcronym = JSON_Helper.createValueAcronym(jsonData);
+ this.config_version = configAcronym;
+ }
+
+ @Override
+ public void loadJSON(JSONObject jsonObj, IScope scope) {
+ JSON_Helper.parseSelf(jsonObj, this, ConfigurationClassMap.classMap, scope);
+ }
+}
diff --git a/comp_configuration/src/main/java/com/xprize/comp_configuration/ConfigurationQuickOptions.java b/comp_configuration/src/main/java/com/xprize/comp_configuration/ConfigurationQuickOptions.java
new file mode 100644
index 0000000..fc85856
--- /dev/null
+++ b/comp_configuration/src/main/java/com/xprize/comp_configuration/ConfigurationQuickOptions.java
@@ -0,0 +1,52 @@
+package com.xprize.comp_configuration;
+
+/**
+ * RoboTutor
+ * Similar to QuickDebugTutor, this class offers the developer a way to store multiple pre-set configuration
+ * option sets without changing the config file.
+ * Created by kevindeland on 5/9/19.
+ */
+
+public class ConfigurationQuickOptions {
+
+
+ // Both SW and EN versions, and they both have the debugger menu.
+ public static ConfigurationItems DEBUG_SW_EN = new ConfigurationItems(
+ "debug_sw_en",
+ false,
+ true,
+ true,
+ true,
+ false,
+ "LANG_NULL",
+ false,
+ false,
+ false,
+ "CD1",
+ true,
+ false,
+ false,
+ "roboscreen",
+ false
+ );
+
+ // EN version, and they both have the debugger menu.
+ public static ConfigurationItems DEBUG_EN = new ConfigurationItems(
+ "debug_en",
+ true,
+ true,
+ true,
+ false,
+ false,
+ "LANG_EN",
+ false,
+ false,
+ false,
+ "CD1",
+ true,
+ false,
+ false,
+ "roboscreen",
+ false
+ );
+}
diff --git a/comp_configuration/src/test/java/com/xprize/comp_configuration/ExampleUnitTest.java b/comp_configuration/src/test/java/com/xprize/comp_configuration/ExampleUnitTest.java
new file mode 100644
index 0000000..ff270d8
--- /dev/null
+++ b/comp_configuration/src/test/java/com/xprize/comp_configuration/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.xprize.comp_configuration;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/comp_logging/.gitignore b/comp_logging/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/comp_logging/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/comp_logging/build.gradle b/comp_logging/build.gradle
new file mode 100644
index 0000000..350d762
--- /dev/null
+++ b/comp_logging/build.gradle
@@ -0,0 +1,89 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion rootProject.ext.rtCompileSdkVersion
+ buildToolsVersion rootProject.ext.rtBuildToolsVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.rtMinSdkVersion
+ targetSdkVersion rootProject.ext.rtTargetSdkVersion
+ versionCode rootProject.ext.rtVersionCode
+ versionName rootProject.ext.rtVersionName
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+ def keystorePropertiesFile = rootProject.file("keystore.properties")
+ def keystoreProperties = new Properties()
+ keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+
+ signingConfigs {
+ android {
+ keyAlias keystoreProperties['keyAlias']
+ keyPassword keystoreProperties['keyPassword']
+ storeFile file(keystoreProperties['storeFile'])
+ storePassword keystoreProperties['storePassword']
+ }
+ }
+
+ buildTypes {
+ def BOOLEAN = "boolean"
+ def STRING = "String"
+ def TRUE = "true"
+ def FALSE = "false"
+
+ def LANGUAGE_FEATURE_ID = "LANGUAGE_FEATURE_ID"
+ def LANGUAGE_ENGLISH = "\"LANG_EN\""
+ def LANGUAGE_SWAHILI = "\"LANG_SW\""
+
+ debug {
+ buildConfigField "String", "WIFI_CONFIG", "\"DEBUG\""
+ debuggable true
+ }
+
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+
+ xprize {
+ buildConfigField "String", "WIFI_CONFIG", "\"XPRIZE\""
+
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+
+ local {
+ buildConfigField "String", "WIFI_CONFIG", "\"LOCAL\""
+
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+
+ vmc {
+ buildConfigField "String", "WIFI_CONFIG", "\"VMC\""
+
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+ }
+}
+
+dependencies {
+ api fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ implementation 'com.android.support:appcompat-v7:25.2.0'
+ implementation 'com.google.code.gson:gson:2.8.0'
+ api 'junit:junit:4.12'
+}
diff --git a/comp_logging/proguard-rules.pro b/comp_logging/proguard-rules.pro
new file mode 100644
index 0000000..fac3064
--- /dev/null
+++ b/comp_logging/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Dev\Android\AndroidSDK/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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/comp_logging/src/androidTest/java/cmu/xprize/comp_logging/ExampleInstrumentedTest.java b/comp_logging/src/androidTest/java/cmu/xprize/comp_logging/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..084e88c
--- /dev/null
+++ b/comp_logging/src/androidTest/java/cmu/xprize/comp_logging/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package cmu.xprize.comp_logging;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("cmu.xprize.comp_logging.test", appContext.getPackageName());
+ }
+}
diff --git a/comp_logging/src/main/AndroidManifest.xml b/comp_logging/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6837b8b
--- /dev/null
+++ b/comp_logging/src/main/AndroidManifest.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/util/src/main/java/cmu/xprize/util/CErrorManager.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/CErrorManager.java
similarity index 91%
rename from util/src/main/java/cmu/xprize/util/CErrorManager.java
rename to comp_logging/src/main/java/cmu/xprize/comp_logging/CErrorManager.java
index 8383eae..62f7584 100644
--- a/util/src/main/java/cmu/xprize/util/CErrorManager.java
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/CErrorManager.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,10 +16,12 @@
//
//*********************************************************************************
-package cmu.xprize.util;
+package cmu.xprize.comp_logging;
import android.util.Log;
+import cmu.xprize.comp_logging.ILogManager;
+
/**
* This was added with Android Studio 2.1 and PIXEL C -- simple logEvent could cause the logcat
* to miss the error message
@@ -28,7 +29,7 @@
*/
public class CErrorManager {
- static ILogManager mLogManager;
+ static ILogManager mLogManager;
static public void setLogManager(ILogManager manager) {
mLogManager = manager;
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/CInterventionLogManager.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/CInterventionLogManager.java
new file mode 100644
index 0000000..7a1867e
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/CInterventionLogManager.java
@@ -0,0 +1,26 @@
+package cmu.xprize.comp_logging;
+
+/**
+ * RoboTutor
+ *
+ * Created by kevindeland on 2019-10-25.
+ */
+public class CInterventionLogManager extends CLogManagerBase {
+
+ private static String TAG = "CInterventionLogManager";
+
+ private CInterventionLogManager() {
+ super.TAG = TAG;
+ }
+
+ // Singleton
+ private static CInterventionLogManager ourInstance = new CInterventionLogManager();
+
+ public static CInterventionLogManager getInstance() {
+ return ourInstance;
+ }
+
+ public void postInterventionLog(InterventionLogItem event) {
+ postEvent_I(TLOG_CONST.PERFORMANCE_TAG, event.toString());
+ }
+}
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/CLogManager.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/CLogManager.java
new file mode 100644
index 0000000..5421f5b
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/CLogManager.java
@@ -0,0 +1,40 @@
+//*********************************************************************************
+//
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//*********************************************************************************
+
+package cmu.xprize.comp_logging;
+
+
+public class CLogManager extends CLogManagerBase implements ILogManager {
+ private static String TAG = "CLogManager";
+
+
+ // Singleton
+ private static CLogManager ourInstance = new CLogManager();
+
+ public static CLogManager getInstance() {
+ return ourInstance;
+ }
+
+ private CLogManager() {
+ super.TAG = TAG;
+ }
+
+
+
+
+}
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/CLogManagerBase.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/CLogManagerBase.java
new file mode 100644
index 0000000..f832f49
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/CLogManagerBase.java
@@ -0,0 +1,654 @@
+//*********************************************************************************
+//
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//*********************************************************************************
+
+package cmu.xprize.comp_logging;
+
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.HashMap;
+
+
+public class CLogManagerBase implements ILogManager {
+
+ private static final int OBJ_PART = 0;
+ private static final int VAL_PART = 1;
+
+ //private static final String LOG_VERSION = "1.0.0"; // initial release
+ private static final String LOG_VERSION = "1.0.1"; // Updated LTKPlus to use LTKPLUS tag
+
+ private static String currenttutor = "";
+ private String TERMINATING_PACKET = "{\"end\":\"end\"}]}";
+ private byte[] TERMINATE_BYTES = TERMINATING_PACKET.getBytes();
+
+ private LogThread logThread; // background thread handling log data
+ private String log_Path;
+ private String log_Filename;
+ private boolean isLogging = false;
+
+ private Handler logHandler;
+ private HashMap queueMap = new HashMap();
+ private boolean mDisabled = false;
+
+ private File logFile;
+ private FileOutputStream logStream;
+ private java.nio.channels.FileLock logLock;
+
+ private FileWriter logWriter;
+ private RandomAccessFile seekableLogWriter;
+ private boolean seekable = true;
+
+ private boolean logWriterValid = false;
+
+ // Datashop specific
+
+ private boolean loggingDS = false;
+ private File logDSFile;
+ private FileOutputStream logDSStream;
+ private java.nio.channels.FileLock logDSLock;
+ private FileWriter logDSWriter;
+ private boolean logDSWriterValid = false;
+
+
+ protected String TAG = "CLogManagerBase";
+
+
+ protected CLogManagerBase() {
+ }
+
+ public void startLogging(String logPath, String logFilename) {
+
+ log_Path = logPath;
+ log_Filename = logFilename;
+
+ // Restart the log if necessary
+ //
+ stopLogging();
+
+ isLogging = true;
+ mDisabled = false;
+
+ logThread = new LogThread(TAG);
+ logThread.start();
+
+ try {
+ logHandler = new Handler(logThread.getLooper());
+ }
+ catch(Exception e) {
+ Log.e(TAG, "Handler Create Failed:" + e);
+ }
+
+ lockLog();
+ }
+
+
+ /**
+ * Stop accepting new packets -
+ * Causes the thread to flush the input queue and then exit
+ *
+ */
+ public void stopLogging() {
+
+ if(isLogging) {
+ Log.i(TAG, "Shutdown begun");
+
+ isLogging = false;
+ mDisabled = true;
+
+ // Terminate the log thread - flush the queue prior to exit
+ //
+ try {
+
+ logThread.getLooper().quitSafely();
+
+ logThread.join(); // waits until it finishes
+ Log.i(TAG, "Shutdown complete");
+
+ } catch (InterruptedException e) {
+ }
+
+ releaseLog();
+ }
+ }
+
+ public void transferHotLogs(String hotPath, String readyPath) {
+
+ File hotDir = new File(hotPath);
+
+ // our first time...
+ if (!hotDir.exists()) {
+ return;
+ }
+
+ File readyDir = new File(readyPath);
+
+ try {
+
+ // make dir if necessary
+ if (!readyDir.exists()) {
+ readyDir.mkdir();
+ }
+
+ for (File f : hotDir.listFiles()) {
+ Log.w("LOG_DEBUG", "Moving file " + f.getName() + " between folders.");
+
+ if (f.isDirectory()) {
+ // do nothing... there should not be any directories
+ } else {
+ File readyLog = new File(readyPath, f.getName());
+ InputStream in = new FileInputStream(f);
+ OutputStream out = new FileOutputStream(readyLog);
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+
+ in.close();
+ out.close();
+
+ f.delete();
+ }
+ }
+ } catch (IOException e) {
+ CErrorManager.logEvent(TAG, "Moving file Error:", e, false);
+ }
+
+ }
+
+
+ public static void setTutor(String tutorid) {
+ currenttutor = tutorid;
+ }
+
+
+ /**
+ * This is a background thread on which to process all log data requests
+ *
+ */
+ private final class LogThread extends HandlerThread {
+
+ public LogThread(String name) {
+ super(name);
+ }
+
+ public LogThread(String name, int priority) {
+ super(name, priority);
+ }
+ }
+
+
+ /**
+ * This is the central processsing point of the data log - this runs on an independent thread
+ * from the UI.
+ */
+ public class Queue implements Runnable {
+
+ protected String dataPacket;
+ protected String unEncodedPacket;
+ protected String statePacket;
+
+ public Queue(String packet) {
+
+ dataPacket = packet;
+ unEncodedPacket = null;
+ }
+
+ public Queue(String _packet, String _target, String _state) {
+ dataPacket = _packet;
+ unEncodedPacket = _target;
+ statePacket = _state;
+ }
+
+ // we can accept data with various object/value encodings (i.e. different delimiters)
+ //
+ private String parseData(String dataPacket, String delimiter) {
+
+ String encodedPacket = "{";
+
+ String[] objvalPairs = dataPacket.split(",");
+
+ for(int pair = 0 ; pair < objvalPairs.length ; pair++) {
+
+ String[] objval = objvalPairs[pair].split(delimiter);
+
+ if(objval.length > 1) {
+ encodedPacket = encodedPacket + "\"" + objval[OBJ_PART] + "\":\"" + objval[VAL_PART] + "\"";
+ }
+ else {
+ encodedPacket = encodedPacket + "\"" + objval[OBJ_PART] + "\":\"" + "" + "\"";
+ }
+
+ if(pair < objvalPairs.length -1) {
+ encodedPacket = encodedPacket + ",";
+ }
+ }
+ encodedPacket = encodedPacket + "}";
+
+ return encodedPacket;
+ }
+
+ @Override
+ public void run() {
+
+ try {
+ queueMap.remove(this);
+
+ // Don't do this JSON encoding on the UI Thread -
+ // if unEncodedPacket is not null then the packet is incomplete and unEncodedPacket
+ // contains a String containing a comma delimited set of obj:value pairs
+ //
+ // For statePackets
+ // e.g. "myobj1|itsvalue,myobj2|itsvalue"
+ //
+ // For unEncodedPacket
+ // e.g. "myobj1:itsvalue,myobj2:itsvalue"
+ //
+ // These need to be encoded into a JSON data subobject.
+ //
+ if(statePacket != null) {
+
+ String encodedJSONPacket = parseData(statePacket, "#");
+
+ dataPacket = dataPacket + "\"data\":" + encodedJSONPacket + "},\n";
+ }
+ else if(unEncodedPacket != null) {
+
+ String encodedJSONPacket = parseData(unEncodedPacket, ":");
+
+ dataPacket = dataPacket + "\"data\":" + encodedJSONPacket + "},\n";
+ }
+
+ writePacketToLog(dataPacket);
+
+ } catch (Exception e) {
+ CErrorManager.logEvent(TAG, "Write Error:", e, false);
+ }
+ }
+ }
+
+
+ /**
+ * We use file locks to keep the logs around until we are finished. The RoboTutor XPrize initiative
+ * used a Google Drive-Sync utility App that required locking the files so they weren't deleted while in
+ * use. So this is not a requirement otherwise.
+ *
+ */
+ private void lockLog() {
+
+ // Release previous log file if still locked
+ //
+ if(logWriterValid) {
+ releaseLog();
+ }
+
+
+ // String oldPath = CPreferenceCache.getPrefID(TLOG_CONST.ENGINE_INSTANCE) + TLOG_CONST.JSONLOG;
+ String newPath = log_Filename + TLOG_CONST.JSONLOG;
+ // String oldDsPath = CPreferenceCache.getPrefID(TLOG_CONST.ENGINE_INSTANCE) + TLOG_CONST.DATASHOP + TLOG_CONST.JSONLOG;
+ String newDsPath = log_Filename + TLOG_CONST.DATASHOP + TLOG_CONST.JSONLOG;
+
+ String state = Environment.getExternalStorageState();
+
+ if (Environment.MEDIA_MOUNTED.equals(state)) {
+
+ String outPath;
+ String outDSPath;
+
+ // Validate output folder
+ outPath = log_Path;
+ outDSPath = log_Path;
+ File outputFile = new File(outPath);
+
+ if (!outputFile.exists())
+ outputFile.mkdir();
+
+ // Generate a tutor instance-unique id for the log name
+ //
+ outPath += newPath;
+
+ logFile = new File(outPath);
+
+ try {
+ logStream = new FileOutputStream(logFile);
+ logLock = logStream.getChannel().lock();
+
+ if(seekable) {
+ seekableLogWriter = new RandomAccessFile(outPath, "rwd");
+ }
+ else {
+ logWriter = new FileWriter(outPath, TLOG_CONST.APPEND);
+ }
+
+ logWriterValid = true;
+
+ // Begin the root JSON element
+ postPacket("{\"RT_log_version\":\"" + LOG_VERSION + "\",\"RT_log_data\":[");
+
+ } catch (Exception e) {
+ Log.e(TAG, "lockLog Failed: " + e);
+ }
+
+
+ //**** DATASHOP
+
+ if(loggingDS) {
+
+ // Generate a tutor instance-unique id for DataShop
+ //
+ outDSPath += newDsPath;
+
+ logDSFile = new File(outDSPath);
+
+ try {
+ logDSStream = new FileOutputStream(logDSFile);
+ logDSLock = logDSStream.getChannel().lock();
+ logDSWriter = new FileWriter(outDSPath, TLOG_CONST.APPEND);
+
+ logDSWriterValid = true;
+
+ } catch (Exception e) {
+ Log.e(TAG, "DataShop lockLog Failed: " + e);
+ }
+ }
+ }
+ }
+
+
+ private void releaseLog() {
+
+ try {
+ if(logWriterValid) {
+
+ if(seekable) {
+ logWriterValid = false;
+
+ seekableLogWriter.close();
+ }
+ else {
+ // Terminate the root JSON element
+ //
+ writePacketToLog(TERMINATING_PACKET);
+
+ logWriterValid = false;
+
+ logWriter.flush();
+ logWriter.close();
+ }
+
+ logLock.release();
+ logStream.close();
+ }
+ }
+ catch(Exception e) {
+ Log.e(TAG, "releaseLog Failed: " + e);
+ }
+
+ //**** DATASHOP
+
+ if(loggingDS) {
+ try {
+ if (logDSWriterValid) {
+
+ logDSWriterValid = false;
+
+ logDSWriter.flush();
+ logDSWriter.close();
+
+ logDSLock.release();
+ logDSStream.close();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "releaseLog Failed: " + e);
+ }
+ }
+ }
+
+
+ /**
+ * Note that this is currently XPrize log specific.
+ * TODO: make general Purpose
+ */
+ private void writePacketToLog(String jsonPacket) {
+
+ // Append Glyph Data to file
+ try {
+ // Throws if there is a JSON serializatin error
+ //
+ if(logWriterValid) {
+
+ if(seekable) {
+
+ if(seekableLogWriter.length() > TERMINATE_BYTES.length) {
+ seekableLogWriter.seek(seekableLogWriter.length() - TERMINATE_BYTES.length);
+ }
+
+ seekableLogWriter.writeBytes(jsonPacket);
+
+ seekableLogWriter.writeBytes(TERMINATING_PACKET);
+ }
+ else {
+ logWriter.write(jsonPacket);
+ logWriter.flush();
+ }
+ }
+ }
+ catch(Exception e) {
+ Log.e(TAG, "Serialization Error: " + e);
+ }
+ }
+
+
+ /**
+ * Keep a mapping of pending messages so we can flush the queue if we want to terminate
+ * the tutor before it finishes naturally.
+ *
+ * @param qCommand
+ */
+ private void enQueue(Queue qCommand) {
+
+ if (!mDisabled) {
+ queueMap.put(qCommand, qCommand);
+
+ logHandler.post(qCommand);
+ }
+ }
+
+
+ /**
+ * Post a command to this scenegraph queue
+ *
+ * @param command
+ */
+ public void post(String command) {
+
+ enQueue(new Queue(command));
+ }
+
+
+ /**
+ * Post a command to this scenegraph queue
+ *
+ * @param command
+ */
+ private void postUnencoded(String command, String target, String state) {
+
+ enQueue(new Queue(command, target, state));
+ }
+
+
+ @Override
+ public void postTutorState(String Tag, String Msg) {
+ Log.i(Tag, postEvent_BASE("TUTORSTATE", Tag, Msg));
+ }
+
+ @Override
+ public void postEvent_V(String Tag, String Msg) {
+ Log.v(Tag, postEvent_BASE("VERBOSE", Tag, Msg));
+ }
+ @Override
+ public void postEvent_D(String Tag, String Msg) {
+ Log.d(Tag, postEvent_BASE("DEBUG", Tag, Msg));
+ }
+ @Override
+ public void postEvent_I(String Tag, String Msg) {
+ Log.i(Tag, postEvent_BASE("INFO", Tag, Msg));
+ }
+ @Override
+ public void postEvent_W(String Tag, String Msg) {
+ Log.w(Tag, postEvent_BASE("WARN", Tag, Msg));
+ }
+ @Override
+ public void postEvent_E(String Tag, String Msg) {
+ Log.e(Tag, postEvent_BASE("ERROR", Tag, Msg));
+ }
+ @Override
+ public void postEvent_A(String Tag, String Msg) {
+ Log.wtf(Tag, postEvent_BASE("ASSERT", Tag, Msg));
+ }
+
+ // Note that we leave the Msg JSON encoding to the Log thread where it can be processed off the
+ // UI thread.
+ //
+ private String postEvent_BASE(String classification, String Tag, String Msg) {
+
+ String packet;
+
+ packet = "{" +
+ "\"type\":\"LOG_DATA\"," +
+ "\"tutor\":\"" + currenttutor + "\"," +
+ "\"class\":\"" + classification + "\"," +
+ "\"tag\":\"" + Tag + "\"," +
+ "\"time\":\"" + System.currentTimeMillis() + "\",";
+
+ // Note that tutor state is encoded with | object|value delimiters while
+ // Regular messages are delimited with : object:value delimiters.
+ //
+ switch(classification) {
+ case "TUTORSTATE":
+ postUnencoded(packet, null, Msg);
+ break;
+ case "VERBOSE":
+ case "DEBUG":
+ break;
+ default:
+ postUnencoded(packet, Msg, null);
+ break;
+ }
+
+ return Msg;
+ }
+
+
+ @Override
+ public void postDateTimeStamp(String Tag, String Msg) {
+
+ String packet;
+
+ // #Mod 331 add calendar time to timestamp
+ //
+ DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+ String formattedDate = df.format(Calendar.getInstance().getTime());
+
+ packet = "{" +
+ "\"class\":\"VERBOSE\"," +
+ "\"tag\":\"" + Tag + "\"," +
+ "\"type\":\"TimeStamp\"," +
+ "\"datetime\":\"" + formattedDate + "\"," +
+ "\"time\":\"" + System.currentTimeMillis() + "\",";
+
+ postUnencoded(packet, Msg, null);
+
+ // Emit to logcat as info class message
+ //
+ Log.i(Tag, packet + Msg);
+ }
+
+
+ @Override
+ public void postError(String Tag, String Msg) {
+
+ String packet;
+
+ packet = "{" +
+ "\"class\":\"ERROR\"," +
+ "\"tag\":\"" + Tag + "\"," +
+ "\"type\":\"Error\"," +
+ "\"time\":\"" + System.currentTimeMillis() + "\"," +
+ "\"msg\":\"" + Msg + "\"" +
+ "},\n";
+
+ post(packet);
+ }
+
+
+ @Override
+ public void postError(String Tag, String Msg, Exception e) {
+
+ String packet;
+
+ packet = "{" +
+ "\"class\":\"ERROR\"," +
+ "\"tag\":\"" + Tag + "\"," +
+ "\"type\":\"Exception\"," +
+ "\"time\":\"" + System.currentTimeMillis() + "\"," +
+ "\"msg\":\"" + Msg + "\"," +
+ "\"exception\":\"" + e.toString() + "\"" +
+ "},\n";
+
+ post(packet);
+ }
+
+ @Override
+ public void postBattery(String Tag, String percent, String chargeType) {
+
+ String packet;
+
+ packet = "{" +
+ "\"class\":\"BATTERY\"," +
+ "\"tag\":\"" + Tag + "\"," +
+ "\"time\":\"" + System.currentTimeMillis() + "\"," +
+ "\"percent\":\"" + percent + "\"," +
+ "\"chargeType\":\"" + chargeType + "\"" +
+ "},\n";
+
+ post(packet);
+
+ Log.i(Tag, packet);
+ }
+
+
+ @Override
+ public void postPacket(String packet) {
+
+ post(packet);
+ }
+}
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/CPerfLogManager.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/CPerfLogManager.java
new file mode 100644
index 0000000..ba65565
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/CPerfLogManager.java
@@ -0,0 +1,93 @@
+//*********************************************************************************
+//
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//*********************************************************************************
+
+package cmu.xprize.comp_logging;
+
+import android.util.Log;
+
+public class CPerfLogManager extends CLogManagerBase implements IPerfLogManager {
+ private static String TAG = "CLogManager";
+
+ // Singleton
+ private static CPerfLogManager ourInstance = new CPerfLogManager();
+
+ public static CPerfLogManager getInstance() {
+ return ourInstance;
+ }
+
+ private CPerfLogManager() {
+ super.TAG = TAG;
+ }
+
+ private PerformanceLogItem lastEvent = new PerformanceLogItem();
+
+ @Override
+ public void postPerformanceLog(PerformanceLogItem event) {
+ lastEvent = event;
+ postEvent_I(TLOG_CONST.PERFORMANCE_TAG, event.toString());
+ }
+
+ // TODO: Super hacky. Need refactoring.
+ @Override
+ public void postPerformanceLogWithoutContext(PerformanceLogItem event) {
+
+ if (event.getGameId() == null || event.getGameId().isEmpty()) {
+ event.setGameId(lastEvent.getGameId());
+ }
+
+ if (event.getTutorName() == null || event.getTutorName().isEmpty()) {
+ event.setTutorName(lastEvent.getTutorName());
+ }
+
+ if (event.getTutorId() == null || event.getTutorId().isEmpty()) {
+ event.setTutorId(lastEvent.getTutorId());
+ }
+
+ if (event.getPromotionMode() == null || event.getPromotionMode().isEmpty()) {
+ event.setPromotionMode(lastEvent.getPromotionMode());
+ }
+
+ if (event.getTaskName() == null || event.getTaskName().isEmpty()) {
+ event.setTaskName(lastEvent.getTaskName());
+ }
+
+ if (event.getLevelName() == null || event.getLevelName().isEmpty()) {
+ event.setLevelName(lastEvent.getLevelName());
+ }
+
+ if (event.getProblemName() == null || event.getProblemName().isEmpty()) {
+ event.setProblemName(lastEvent.getProblemName());
+ }
+
+ if (event.getProblemNumber() == 0) {
+ event.setProblemNumber(lastEvent.getProblemNumber());
+ }
+
+ if (event.getSubstepNumber() == 0) {
+ event.setSubstepNumber(lastEvent.getSubstepNumber());
+ event.setSubstepNumber(-1); // 1=ones, 2=tens, 3=hundreds
+ }
+
+ if (event.getAttemptNumber() == 0) {
+ event.setAttemptNumber(lastEvent.getAttemptNumber());
+ }
+
+ postEvent_I(TLOG_CONST.PERFORMANCE_TAG, event.toString());
+ }
+}
+
diff --git a/util/src/main/java/cmu/xprize/util/CPreferenceCache.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/CPreferenceCache.java
similarity index 92%
rename from util/src/main/java/cmu/xprize/util/CPreferenceCache.java
rename to comp_logging/src/main/java/cmu/xprize/comp_logging/CPreferenceCache.java
index d8d2a54..0b0efeb 100644
--- a/util/src/main/java/cmu/xprize/util/CPreferenceCache.java
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/CPreferenceCache.java
@@ -1,4 +1,4 @@
-package cmu.xprize.util;
+package cmu.xprize.comp_logging;
import android.app.Activity;
import android.content.Context;
@@ -22,7 +22,7 @@ static public String initLogPreference(Activity app) {
context = app;
idMap = new HashMap<>();
- return updateTutorGUID(TCONST.ENGINE_INSTANCE);
+ return updateTutorGUID(TLOG_CONST.ENGINE_INSTANCE);
}
@@ -46,7 +46,7 @@ static public int updateTutorInstance(String key) {
// "tutor" may then be used to anonymously access the current tutors key
//
idMap.put(key, value);
- idMap.put(TCONST.CURRENT_TUTOR, value);
+ idMap.put(TLOG_CONST.CURRENT_TUTOR, value);
editor.putInt(key, ordinal + 1);
editor.apply();
@@ -75,7 +75,7 @@ static public String updateTutorGUID(String key) {
// "tutor" may then be used to anonymously access the current tutors key
//
idMap.put(key, value);
- idMap.put(TCONST.CURRENT_TUTOR, value);
+ idMap.put(TLOG_CONST.CURRENT_TUTOR, value);
editor.putString(key, GUID);
editor.apply();
@@ -102,7 +102,7 @@ static private String getGUID() {
// Find a unique GUID that doesn't exist in the preferences cache
//
do {
- for (int i1 = 0; i1 < TCONST.GUID_LEN; i1++) {
+ for (int i1 = 0; i1 < TLOG_CONST.GUID_LEN; i1++) {
try {
guid += guidMap.charAt((int) (Math.random() * guidMap.length()));
}
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/ILogManager.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/ILogManager.java
new file mode 100644
index 0000000..d5e4e73
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/ILogManager.java
@@ -0,0 +1,41 @@
+package cmu.xprize.comp_logging;
+
+public interface ILogManager {
+
+ /**
+ * Transfer logs from one path to another. Separation of hot logs from ready logs prevents
+ * RoboTransfer from transferring a log while it is being written.
+ *
+ * @param hotPath
+ * @param readyPath
+ */
+ public void transferHotLogs(String hotPath, String readyPath);
+
+ public void startLogging(String logPath, String logFileName);
+ public void stopLogging();
+
+ public void postTutorState(String Tag, String Msg);
+
+ public void postEvent_V(String Tag, String Msg);
+
+ public void postEvent_D(String Tag, String Msg);
+
+ public void postEvent_I(String Tag, String Msg);
+
+ public void postEvent_W(String Tag, String Msg);
+
+ public void postEvent_E(String Tag, String Msg);
+
+ public void postEvent_A(String Tag, String Msg);
+
+ public void postDateTimeStamp(String Tag, String Msg);
+
+ public void post(String command);
+
+ public void postError(String Tag, String Msg);
+ public void postError(String Tag, String Msg, Exception e);
+
+ public void postBattery(String Tag, String percent, String chargeType);
+
+ public void postPacket(String packet);
+}
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/IPerfLogManager.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/IPerfLogManager.java
new file mode 100644
index 0000000..e055540
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/IPerfLogManager.java
@@ -0,0 +1,8 @@
+package cmu.xprize.comp_logging;
+
+public interface IPerfLogManager extends ILogManager {
+
+ public void postPerformanceLog(PerformanceLogItem event);
+
+ public void postPerformanceLogWithoutContext(PerformanceLogItem event);
+}
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/ITutorLogger.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/ITutorLogger.java
new file mode 100644
index 0000000..1b5d87d
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/ITutorLogger.java
@@ -0,0 +1,7 @@
+package cmu.xprize.comp_logging;
+
+public interface ITutorLogger {
+
+ public void logState(String logData);
+
+}
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/InterventionLogItem.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/InterventionLogItem.java
new file mode 100644
index 0000000..01fc690
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/InterventionLogItem.java
@@ -0,0 +1,41 @@
+package cmu.xprize.comp_logging;
+
+import com.google.gson.Gson;
+
+import org.json.JSONObject;
+
+/**
+ * RoboTutor
+ *
+ * Created by kevindeland on 2019-10-25.
+ */
+public class InterventionLogItem {
+
+ long timestamp;
+ String student;
+ String group;
+ String tutor;
+ String recommendStudent;
+ String errorType;
+ Boolean helpTaken;
+
+ public InterventionLogItem(
+ long timestamp, String student, String group,
+ String tutor, String recommendStudent,
+ String errorType, Boolean helpTaken) {
+ this.timestamp = timestamp;
+ this.student = student;
+ this.group = group;
+ this.tutor = tutor;
+ this.recommendStudent = recommendStudent;
+ this.errorType = errorType;
+ this.helpTaken = helpTaken;
+ }
+
+ @Override
+ public String toString() {
+ Gson gson = new Gson();
+
+ return gson.toJson(this);
+ }
+}
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/PerformanceLogItem.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/PerformanceLogItem.java
new file mode 100644
index 0000000..2bda026
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/PerformanceLogItem.java
@@ -0,0 +1,489 @@
+package cmu.xprize.comp_logging;
+
+import java.lang.reflect.Field;
+
+import static cmu.xprize.comp_logging.PerformanceLogItem.MATRIX_TYPE.LITERACY_MATRIX;
+import static cmu.xprize.comp_logging.PerformanceLogItem.MATRIX_TYPE.MATH_MATRIX;
+import static cmu.xprize.comp_logging.PerformanceLogItem.MATRIX_TYPE.SONGS_MATRIX;
+import static cmu.xprize.comp_logging.PerformanceLogItem.MATRIX_TYPE.STORIES_MATRIX;
+import static cmu.xprize.comp_logging.PerformanceLogItem.MATRIX_TYPE.UNKNOWN_MATRIX;
+
+/**
+ * Created by kevindeland on 9/13/17.
+ */
+
+public class PerformanceLogItem {
+
+ private long timestamp;
+ private String userId;
+ private String sessionId;
+ private String gameId;
+ private String language;
+ private String tutorName;
+ private String tutorId;
+ private String problemName;
+ private int problemNumber;
+ private String levelName;
+ private int totalProblemsCount;
+
+ private String matrixName; // could be literacy or math or whatever
+
+ public final static class MATRIX_TYPE {
+ public final static String MATH_MATRIX = "math";
+ public final static String LITERACY_MATRIX = "literacy";
+ public final static String STORIES_MATRIX = "stories";
+ public final static String UNKNOWN_MATRIX = "unknown";
+ public final static String SONGS_MATRIX = "songs";
+ }
+
+
+ private int totalSubsteps;
+ private int substepNumber;
+ private int substepProblem;
+ private int attemptNumber;
+ private String taskName;
+
+ private String expectedAnswer;
+ private String userResponse;
+ private String correctness;
+
+ private String distractors;
+ private String scaffolding;
+ private String promptType;
+ private String feedbackType;
+
+ private String promotionMode; // placement or promotion
+
+ private final static String DEBUG_TAG = "DEBUG_PERFORMANCE_LOG";
+
+ //
+ // iterative way to print fields in the desired order
+ private static final String[] orderedFieldsToPrint = {"timestamp", "userId", "sessionId", "gameId", "language", "tutorName", "tutorId", "matrixName", "levelName", "taskName",
+ "problemName", "problemNumber", "substepNumber", "substepProblem", "attemptNumber", "expectedAnswer", "userResponse", "correctness", "feedbackType", "totalProblemsCount", "promotionMode", "scaffolding"};
+
+ public PerformanceLogItem() {
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder msg = new StringBuilder();
+
+ for (String fieldName : orderedFieldsToPrint) {
+ try {
+ Field field = this.getClass().getDeclaredField(fieldName);
+ if(field != null) {
+ msg.append(fieldName);
+ msg.append(": ");
+ msg.append(field.get(this));
+ msg.append(", ");
+ }
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ String result = msg.toString();
+ return result.substring(0, result.length() - 2); // don't forget to slice off that last comma
+ }
+
+
+ /**
+ * sets matrix based on which tutor....
+ * TODO move into new class
+ * TODO even better, just pass the "skills" variable
+ * @param tutorId
+ */
+ private void setMatrixNameByTutorId(String tutorId) {
+
+ if (isAlwaysMathTutor(tutorId)) {
+ matrixName = MATH_MATRIX;
+ } else if (isAkiraTutor(tutorId)) {
+ setMatrixAkira(tutorId);
+ } else if (isBpopTutor(tutorId)) {
+ setMatrixBpop(tutorId);
+ } else if (isWriteTutor(tutorId)) {
+ setMatrixWrite(tutorId);
+ } else if (isStoryTutor(tutorId)) {
+ setMatrixStory(tutorId);
+ } else if (isAlwaysLitMatrix(tutorId)) {
+ matrixName = LITERACY_MATRIX;
+ } else {
+ matrixName = UNKNOWN_MATRIX;
+ }
+
+ }
+
+ /**
+ * set matrix name if it's a Story tutor
+ * @param tutorName
+ */
+ private void setMatrixStory(String tutorName) {
+ if (tutorName.contains("A..Z")
+ || tutorName.contains("wrd")
+ || tutorName.contains("syl")
+ || tutorName.contains("vow")
+ || tutorName.contains("abcdefg_EnglishTune_LowerCase")
+ || tutorName.contains("ABCDEFG_EnglishTune_UpperCase")
+ || tutorName.contains("LC_Vowel_Song_1")
+ || tutorName.contains("LC_Vowel_Song_2")
+ || tutorName.contains("UC_Vowel_Song_1")
+ || tutorName.contains("UC_Vowel_Song_2")
+ || tutorName.contains("letters_alphabet_song")
+ || tutorName.contains("comm")
+ || tutorName.contains("ltr")
+ || tutorName.contains("syl")
+ || tutorName.contains("nafasi")
+ || tutorName.contains("herufi_kubwa")
+ || tutorName.contains("kituo")){
+ matrixName = LITERACY_MATRIX;
+
+ } else if (tutorName.contains("1..4")
+ || tutorName.contains("0..10")
+ || tutorName.contains("0..20")
+ || tutorName.contains("0..50")
+ || tutorName.contains("0..100")
+ || tutorName.contains("10..100")
+ || tutorName.contains("50..100")
+ || tutorName.contains("100..900")
+ || tutorName.contains("100..1000")
+ || tutorName.contains("Counting_Fingers_Toes")
+ || tutorName.contains("Number_Song_2")
+ || tutorName.contains("numbers_counting_song")
+ || tutorName.contains("word_problem")) {
+ matrixName = MATH_MATRIX;
+
+ } else if (tutorName.contains("Garden_Song")
+ || tutorName.contains("Safari_Song")
+ || tutorName.contains("School_Welcome_Song")
+ || tutorName.contains("Kusoma_Welcome_Song")) {
+ matrixName = SONGS_MATRIX;
+
+ } else if (tutorName.contains("story_")) {
+
+ if (tutorName.startsWith("story.echo")
+ || tutorName.startsWith("story.parrot")
+ || tutorName.startsWith("story.read")) {
+ matrixName = LITERACY_MATRIX;
+
+ } else if (tutorName.startsWith("story.hear")
+ || tutorName.startsWith("story.clo.hear")
+ || tutorName.startsWith("story.pic.hear")
+ || tutorName.startsWith("story.gen.hear")) {
+ matrixName = STORIES_MATRIX;
+
+ } else {
+ matrixName = UNKNOWN_MATRIX;
+ }
+
+ } else {
+ matrixName = UNKNOWN_MATRIX;
+ }
+ }
+
+ /**
+ * set matrix name if it's a Write tutor
+ * @param tutorName
+ */
+ private void setMatrixWrite(String tutorName) {
+
+ if (tutorName.contains("ltr")
+ || tutorName.contains("missingLtr")
+ || tutorName.contains("wrd")) {
+ matrixName = LITERACY_MATRIX;
+ } else if (tutorName.contains("arith")
+ || tutorName.contains("num")){
+ matrixName = MATH_MATRIX;
+ } else {
+ matrixName = UNKNOWN_MATRIX;
+ }
+ }
+
+ /**
+ * set matrix name if it's a BubblePop tutor
+ * @param tutorName
+ */
+ private void setMatrixBpop(String tutorName) {
+
+ if (tutorName.contains("ltr")
+ || tutorName.contains("wrd")
+ || tutorName.contains("syl")) {
+ matrixName = LITERACY_MATRIX;
+ }
+
+ else if (tutorName.contains("mn")
+ || tutorName.contains("gl")
+ || tutorName.contains("addsub")
+ || tutorName.contains("num")) {
+ matrixName = MATH_MATRIX;
+ } else {
+ matrixName = UNKNOWN_MATRIX;
+ }
+
+ }
+
+ /**
+ * set matrix name if it's an Akira tutor
+ * @param tutorName
+ */
+ private void setMatrixAkira(String tutorName) {
+ if (tutorName.contains("ltr")
+ || tutorName.contains("wrd")
+ || tutorName.contains("syl")) {
+ matrixName = LITERACY_MATRIX;
+ } else {
+ matrixName = MATH_MATRIX;
+ }
+ }
+
+
+ //
+ // for checking tutor type based on name
+ //
+ private boolean isStoryTutor(String tutorName) {
+ return tutorName.startsWith("story");
+ }
+
+ private boolean isWriteTutor(String tutorName) {
+ return tutorName.startsWith("write");
+ }
+
+ private boolean isBpopTutor(String tutorName) {
+ return tutorName.startsWith("bpop");
+ }
+
+ private boolean isAlwaysLitMatrix(String tutorName) {
+ return tutorName.startsWith("spelling")
+ || tutorName.startsWith("picmatch");
+ }
+
+ private boolean isAkiraTutor(String tutorName) {
+ return tutorName.startsWith("akira");
+ }
+
+ private boolean isAlwaysMathTutor(String tutorName) {
+ return tutorName.startsWith("num.scale")
+ || tutorName.startsWith("math")
+ || tutorName.startsWith("countingx")
+ || tutorName.startsWith("place.value")
+ || tutorName.startsWith("placevalue")
+ || tutorName.startsWith("bigmath")
+ || tutorName.startsWith("numcompare");
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ public void setSessionId(String sessionId) {
+ this.sessionId = sessionId;
+ }
+
+ public String getGameId() {
+ return gameId;
+ }
+
+ public void setGameId(String gameId) {
+ this.gameId = gameId;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public String getTutorName() {
+ return tutorName;
+ }
+
+ public void setTutorName(String tutorName) {
+
+ this.tutorName = tutorName;
+ }
+
+ public String getTutorId() {
+ return tutorId;
+ }
+
+ public void setTutorId(String tutorId) {
+ if(tutorId == null) {
+ return;
+ }
+
+ this.tutorId = tutorId.replaceAll(":", "_");
+
+
+ if(tutorId != null) {
+ setMatrixNameByTutorId(tutorId);
+ }
+ }
+
+ public void setMatrixNameBySkillId(String skill) {
+ if (skill == null) {
+ return;
+ }
+ switch (skill) {
+
+ case "letters":
+ matrixName = LITERACY_MATRIX;
+ break;
+
+ case "numbers":
+ matrixName = MATH_MATRIX;
+ break;
+
+ case "stories":
+ matrixName = STORIES_MATRIX;
+ break;
+ }
+ }
+
+ public String getPromotionMode() {
+ return promotionMode;
+ }
+
+ public void setPromotionMode(String promotionMode) {
+ this.promotionMode = promotionMode;
+ }
+
+ public String getMatrixName() {
+ return matrixName;
+ }
+
+ public String getProblemName() {
+ return problemName;
+ }
+
+ public void setProblemName(String problemName) {
+ this.problemName = problemName;
+ }
+
+ public int getProblemNumber() {
+ return problemNumber;
+ }
+
+ public void setProblemNumber(int problemNumber) {
+ this.problemNumber = problemNumber;
+ }
+
+ public int getTotalProblemsCount() { return totalProblemsCount; }
+
+ public void setTotalProblemsCount(int totalProblemsCount) { this.totalProblemsCount = totalProblemsCount; }
+
+ public int getTotalSubsteps() { return totalSubsteps; }
+
+ public void setTotalSubsteps(int totalSubsteps) {
+ this.totalSubsteps = totalSubsteps;
+ }
+
+ public int getSubstepNumber() {
+ return substepNumber;
+ }
+
+ public void setSubstepNumber(int substepNumber) {
+ this.substepNumber = substepNumber;
+ }
+
+ public int getSubstepProblem() {
+ return substepProblem;
+ }
+
+ public void setSubstepProblem(int substepProblem) {
+ this.substepProblem = substepProblem;
+ }
+
+ public int getAttemptNumber() {
+ return attemptNumber;
+ }
+
+ public void setAttemptNumber(int attemptNumber) {
+ this.attemptNumber = attemptNumber;
+ }
+
+ public String getExpectedAnswer() {
+ return expectedAnswer;
+ }
+
+ public void setExpectedAnswer(String expectedAnswer) {
+ this.expectedAnswer = expectedAnswer;
+ }
+
+ public String getUserResponse() {
+ return userResponse;
+ }
+
+ public void setUserResponse(String userResponse) {
+ this.userResponse = userResponse;
+ }
+
+ public String getCorrectness() {
+ return correctness;
+ }
+
+ public void setCorrectness(String correctness) {
+ this.correctness = correctness;
+ }
+
+ public String getScaffolding() {
+ return scaffolding;
+ }
+
+ public void setScaffolding(String scaffolding) {
+ this.scaffolding = scaffolding;
+ }
+
+ public String getPromptType() {
+ return promptType;
+ }
+
+ public void setPromptType(String promptType) {
+ this.promptType = promptType;
+ }
+
+ public String getFeedbackType() {
+ return feedbackType;
+ }
+
+ public void setFeedbackType(String feedbackType) {
+ this.feedbackType = feedbackType;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getDistractors() {
+ return distractors;
+ }
+
+ public void setDistractors(String distractors) {
+ this.distractors = distractors;
+ }
+
+ public void setLevelName(String levelName) {
+ this.levelName = levelName;
+ }
+
+ public String getLevelName() { return levelName; }
+
+ public void setTaskName(String taskName) {
+ this.taskName = taskName;
+ }
+
+ public String getTaskName() { return taskName; }
+}
diff --git a/comp_logging/src/main/java/cmu/xprize/comp_logging/TLOG_CONST.java b/comp_logging/src/main/java/cmu/xprize/comp_logging/TLOG_CONST.java
new file mode 100644
index 0000000..817217f
--- /dev/null
+++ b/comp_logging/src/main/java/cmu/xprize/comp_logging/TLOG_CONST.java
@@ -0,0 +1,21 @@
+package cmu.xprize.comp_logging;
+
+
+public class TLOG_CONST {
+
+ // Preference keys
+ public static final String ENGINE_INSTANCE = "RoboTutor";
+ public static final String CURRENT_TUTOR = "tutor";
+
+ public static final String GLYPHLOG = "glyphlog_";
+ public static final String DATASHOP = "-DS";
+ public static final String JSONLOG = ".json";
+
+ public static final boolean APPEND = true;
+ public static final boolean REPLACE = false;
+
+ public static final int GUID_LEN = 5;
+ public static final String GUID_UPDATE = "GUIDUPDATE";
+
+ public static final String PERFORMANCE_TAG = "PERFORMANCE_TAG";
+}
diff --git a/comp_logging/src/main/res/values/strings.xml b/comp_logging/src/main/res/values/strings.xml
new file mode 100644
index 0000000..82292cd
--- /dev/null
+++ b/comp_logging/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ comp_logging
+
diff --git a/comp_logging/src/test/java/cmu/xprize/comp_logging/ExampleUnitTest.java b/comp_logging/src/test/java/cmu/xprize/comp_logging/ExampleUnitTest.java
new file mode 100644
index 0000000..71a9ad8
--- /dev/null
+++ b/comp_logging/src/test/java/cmu/xprize/comp_logging/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package cmu.xprize.comp_logging;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/comp_pointtap/build.gradle b/comp_pointtap/build.gradle
index b546b2b..5441133 100644
--- a/comp_pointtap/build.gradle
+++ b/comp_pointtap/build.gradle
@@ -11,10 +11,58 @@ android {
versionName rootProject.ext.rtVersionName
}
+ def keystorePropertiesFile = rootProject.file("keystore.properties")
+ def keystoreProperties = new Properties()
+ keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+
+ signingConfigs {
+ android {
+ keyAlias keystoreProperties['keyAlias']
+ keyPassword keystoreProperties['keyPassword']
+ storeFile file(keystoreProperties['storeFile'])
+ storePassword keystoreProperties['storePassword']
+ }
+ }
+
buildTypes {
+
+ debug {
+ buildConfigField "String", "WIFI_CONFIG", "\"DEBUG\""
+ debuggable true
+ }
+
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+
+ xprize {
+ buildConfigField "String", "WIFI_CONFIG", "\"XPRIZE\""
+
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+
+ local {
+ buildConfigField "String", "WIFI_CONFIG", "\"LOCAL\""
+
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+
+ vmc {
+ buildConfigField "String", "WIFI_CONFIG", "\"VMC\""
+
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
}
}
diff --git a/comp_pointtap/src/main/java/cmu/xprize/comp_pointtap/CHandAnimation.java b/comp_pointtap/src/main/java/cmu/xprize/comp_pointtap/CHandAnimation.java
index 9a0a1ac..a33e50d 100644
--- a/comp_pointtap/src/main/java/cmu/xprize/comp_pointtap/CHandAnimation.java
+++ b/comp_pointtap/src/main/java/cmu/xprize/comp_pointtap/CHandAnimation.java
@@ -44,7 +44,7 @@
import java.util.Map;
import cmu.xprize.util.CAnimatorUtil;
-import cmu.xprize.util.CErrorManager;
+import cmu.xprize.comp_logging.CErrorManager;
import cmu.xprize.util.TCONST;
public class CHandAnimation extends PercentRelativeLayout implements Animator.AnimatorListener {
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index fc9b829..766ee8c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Dec 28 10:00:20 PST 2015
+#Wed May 12 03:26:56 IST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/settings.gradle b/settings.gradle
index 8b0c146..5e48e6b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':util', ':comp_pointtap'
+include ':app', ':util', ':comp_pointtap', ':comp_configuration', ':comp_logging'
diff --git a/util/build.gradle b/util/build.gradle
index 1ff83b6..658eeb1 100644
--- a/util/build.gradle
+++ b/util/build.gradle
@@ -11,10 +11,66 @@ android {
versionName rootProject.ext.rtVersionName
}
+ def keystorePropertiesFile = rootProject.file("keystore.properties")
+ def keystoreProperties = new Properties()
+ keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+
+ signingConfigs {
+ android {
+ keyAlias keystoreProperties['keyAlias']
+ keyPassword keystoreProperties['keyPassword']
+ storeFile file(keystoreProperties['storeFile'])
+ storePassword keystoreProperties['storePassword']
+ }
+ }
+
buildTypes {
+ def BOOLEAN = "boolean"
+ def STRING = "String"
+ def TRUE = "true"
+ def FALSE = "false"
+
+ def LANGUAGE_FEATURE_ID = "LANGUAGE_FEATURE_ID"
+ def LANGUAGE_ENGLISH = "\"LANG_EN\""
+ def LANGUAGE_SWAHILI = "\"LANG_SW\""
+
+ debug {
+ buildConfigField "String", "WIFI_CONFIG", "\"DEBUG\""
+ debuggable true
+ }
+
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+
+ xprize {
+ buildConfigField "String", "WIFI_CONFIG", "\"XPRIZE\""
+
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+
+ local {
+ buildConfigField "String", "WIFI_CONFIG", "\"LOCAL\""
+
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
+ }
+
+ vmc {
+ buildConfigField "String", "WIFI_CONFIG", "\"VMC\""
+
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ signingConfig signingConfigs.android
}
}
@@ -26,13 +82,18 @@ android {
// if true, only report errors
ignoreWarnings true
}
- buildToolsVersion '25.0.0'
+
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- testCompile 'junit:junit:4.12'
- compile 'com.android.support:appcompat-v7:25.2.0'
- compile 'com.android.support:percent:25.2.0'
+ api fileTree(include: ['*.jar'], dir: 'libs')
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'org.json:json:20140107'
+ implementation 'com.android.support:appcompat-v7:25.2.0'
+ api 'com.android.support:percent:25.2.0'
compile files('libs/datashop-logging.jar')
+ api project(':comp_logging')
}
diff --git a/util/src/main/java/cmu/xprize/util/CAnimatorUtil.java b/util/src/main/java/cmu/xprize/util/CAnimatorUtil.java
index a8b6587..02eb808 100644
--- a/util/src/main/java/cmu/xprize/util/CAnimatorUtil.java
+++ b/util/src/main/java/cmu/xprize/util/CAnimatorUtil.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -31,6 +30,8 @@
import android.view.animation.BounceInterpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
+import android.util.Log;
+import java.util.Arrays;
import java.util.ArrayList;
@@ -49,9 +50,7 @@ static private Animator createFloatAnimator(View _tarView, String prop, long dur
static private Animator createFloatAnimator(View _tarView, String prop, long duration, int repeat, int mode, TimeInterpolator interpolator, long delay, float... endPts) {
ValueAnimator vAnimator = null;
-
vAnimator = ObjectAnimator.ofFloat(_tarView, prop, endPts).setDuration(duration);
-
vAnimator.setInterpolator(interpolator);
vAnimator.setRepeatCount(repeat);
@@ -236,7 +235,6 @@ static public AnimatorSet configStretch(View _tarView, String direction, long du
static public AnimatorSet configZoomIn(View _tarView, long duration, long delay, TimeInterpolator interpolator, float... absScales) {
ArrayList zoomAnimators = new ArrayList();
-
AnimatorSet animation = new AnimatorSet();
zoomAnimators.add(createFloatAnimator(_tarView, "scaleX", duration, 0, 0, interpolator, delay, absScales));
@@ -260,7 +258,6 @@ static public AnimatorSet configTranslate(View _tarView, long duration, long del
}
AnimatorSet animation = new AnimatorSet();
-
moveAnimators.add(createFloatAnimator(_tarView, "x", duration, 0, 0, new LinearInterpolator(), delay, wayPointsX));
moveAnimators.add(createFloatAnimator(_tarView, "y", duration, 0, 0, new LinearInterpolator(), delay, wayPointsY));
diff --git a/util/src/main/java/cmu/xprize/util/CAt_Data.java b/util/src/main/java/cmu/xprize/util/CAt_Data.java
new file mode 100644
index 0000000..60cf457
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/CAt_Data.java
@@ -0,0 +1,59 @@
+//*********************************************************************************
+//
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//*********************************************************************************
+
+package cmu.xprize.util;
+
+import org.json.JSONObject;
+
+import cmu.xprize.util.CClassMap;
+import cmu.xprize.util.ILoadableObject;
+import cmu.xprize.util.IScope;
+import cmu.xprize.util.JSON_Helper;
+
+public class CAt_Data implements ILoadableObject {
+
+ public int gridIndex;
+ public int row;
+ public int col;
+
+ // json loadable
+ public String skill;
+ public String tutor_id;
+ public String tutor_desc;
+ public String tutor_data;
+ public String cell_row;
+ public String cell_column;
+ public String same;
+ public String next;
+ public String harder;
+ public String easier;
+
+
+ //************ Serialization
+
+
+ @Override
+ public void loadJSON(JSONObject jsonObj, IScope scope) {
+
+ JSON_Helper.parseSelf(jsonObj, this, CClassMap.classMap, scope);
+
+ row = Integer.parseInt(cell_row);
+ col = Integer.parseInt(cell_column);
+ }
+
+}
diff --git a/util/src/main/java/cmu/xprize/util/CDisplayMetrics.java b/util/src/main/java/cmu/xprize/util/CDisplayMetrics.java
new file mode 100644
index 0000000..dc94372
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/CDisplayMetrics.java
@@ -0,0 +1,50 @@
+/**
+ Copyright(c) 2015-2017 Kevin Willows
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+package cmu.xprize.util;
+
+import android.app.Activity;
+import android.util.DisplayMetrics;
+
+public class CDisplayMetrics {
+
+ private static final CDisplayMetrics ourInstance = new CDisplayMetrics();
+
+ static public float designDensity = 2.0f;
+
+ static public float instanceHeight;
+ static public float instanceWidth;
+ static public float instanceDensity;
+ static public float densityRescale;
+
+ public static CDisplayMetrics getInstance(Activity _activity) {
+
+ // get the multiplier used for drawables at the current screen density and calc the
+ // correction rescale factor for design scale
+ //
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ _activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+
+ instanceHeight = displayMetrics.heightPixels;
+ instanceWidth = displayMetrics.widthPixels;
+ instanceDensity = displayMetrics.density;
+ densityRescale = designDensity / instanceDensity;
+
+ return ourInstance;
+ }
+
+ public static CDisplayMetrics getInstance() {
+ return ourInstance;
+ }
+
+}
diff --git a/util/src/main/java/cmu/xprize/util/CErrorDialog.java b/util/src/main/java/cmu/xprize/util/CErrorDialog.java
index 1429643..dd95737 100644
--- a/util/src/main/java/cmu/xprize/util/CErrorDialog.java
+++ b/util/src/main/java/cmu/xprize/util/CErrorDialog.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,12 +22,15 @@
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
+import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import cmu.xprize.common.R;
+import static cmu.xprize.util.TCONST.QGRAPH_MSG;
+
public class CErrorDialog implements View.OnClickListener {
private final Dialog dialog;
@@ -62,6 +64,8 @@ public Boolean isShowing() {
@Override
public void onClick(View v) {
+ Log.v(QGRAPH_MSG, "event.click: " + " CErrorDialog:exit");
+
System.exit(1);
}
}
diff --git a/util/src/main/java/cmu/xprize/util/CEvent.java b/util/src/main/java/cmu/xprize/util/CEvent.java
index f932e88..1c7edcc 100644
--- a/util/src/main/java/cmu/xprize/util/CEvent.java
+++ b/util/src/main/java/cmu/xprize/util/CEvent.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/util/src/main/java/cmu/xprize/util/CEventMap.java b/util/src/main/java/cmu/xprize/util/CEventMap.java
deleted file mode 100644
index 23f6b0c..0000000
--- a/util/src/main/java/cmu/xprize/util/CEventMap.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package cmu.xprize.util;
-
-import java.util.HashMap;
-
-import cmu.xprize.util.TCONST;
-
-public class CEventMap {
-
- static public HashMap eventMap = new HashMap();
-
- //
- // This is used to map "type" (class names) used in json HashMap specs to real classes
-
- static {
- eventMap.put("SILENCE_EVENT", TCONST.SILENCE_EVENT);
- eventMap.put("SOUND_EVENT", TCONST.SOUND_EVENT);
- eventMap.put("WORD_EVENT", TCONST.WORD_EVENT);
- eventMap.put("SILENCE_TIMEOUT", TCONST.TIMEDSILENCE_EVENT);
- eventMap.put("SOUND_TIMEOUT", TCONST.TIMEDSOUND_EVENT);
- eventMap.put("WORD_TIMEOUT", TCONST.TIMEDWORD_EVENT);
- eventMap.put("START_TIMEOUT", TCONST.TIMEDSTART_EVENT);
- eventMap.put("ALL_TIMED_EVENTS",TCONST.ALLTIMED_EVENTS);
- eventMap.put("ALL_STATIC_EVENTS",TCONST.ALL_EVENTS);
- eventMap.put("ALL_EVENTS",TCONST.ALL_EVENTS);
- }
-}
diff --git a/util/src/main/java/cmu/xprize/util/CFileNameHasher.java b/util/src/main/java/cmu/xprize/util/CFileNameHasher.java
index 36d777a..be2f2cc 100644
--- a/util/src/main/java/cmu/xprize/util/CFileNameHasher.java
+++ b/util/src/main/java/cmu/xprize/util/CFileNameHasher.java
@@ -1,9 +1,14 @@
package cmu.xprize.util;
+import android.util.Log;
+
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import static cmu.xprize.util.TCONST.GRAPH_MSG;
+import static cmu.xprize.util.TCONST.QGRAPH_MSG;
+
/**
* Created by kevin on 11/7/2016.
*/
@@ -30,13 +35,11 @@ private CFileNameHasher() {
public String generateHash(String filename) {
String hashName;
+ String prunedName;
- System.out.println("filename :" + filename);
-
- hashName = filename.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
- System.out.println("prunedname :" + hashName);
+ prunedName = filename.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
- byte[] nameBytes = hashName.getBytes( Charset.forName("UTF-8" ));
+ byte[] nameBytes = prunedName.getBytes( Charset.forName("UTF-8" ));
nameBytes = md.digest(nameBytes);
@@ -45,7 +48,7 @@ public String generateHash(String filename) {
sb.append(Integer.toString((nameBytes[i] & 0xff) + 0x100, 16).substring(1));
}
- System.out.println("hashname :" + sb.toString());
+ Log.v(GRAPH_MSG, "target:CFileNameHasher,action:generatehash,filename:" + filename + " prunedname:" + prunedName + " hashname:" + sb.toString());
return sb.toString();
}
diff --git a/util/src/main/java/cmu/xprize/util/CLoaderView.java b/util/src/main/java/cmu/xprize/util/CLoaderView.java
index 092235a..4aa9132 100644
--- a/util/src/main/java/cmu/xprize/util/CLoaderView.java
+++ b/util/src/main/java/cmu/xprize/util/CLoaderView.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,12 +18,17 @@
package cmu.xprize.util;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.v4.content.LocalBroadcastManager;
import android.util.AttributeSet;
-import android.view.View;
+import android.util.Log;
import android.view.ViewGroup;
-import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
import cmu.xprize.common.R;
@@ -33,6 +37,17 @@ public class CLoaderView extends LinearLayout {
private ViewGroup viewParent = null;
+ private LocalBroadcastManager bManager;
+ private LoadReceiver bReceiver;
+
+ private TextView SprogressTitle;
+ private TextView SprogressMsg1;
+ private TextView SprogressMsg2;
+
+ private ProgressBar SprogressBarI;
+ private ProgressBar SprogressBarD;
+
+
public CLoaderView(Context context, ViewGroup parent) {
super(context);
viewParent = parent;
@@ -56,7 +71,105 @@ public CLoaderView(Context context, AttributeSet attrs, int defStyleAttr) {
public void init(Context context, AttributeSet attrs) {
+
+ // Capture the local broadcast manager
+ bManager = LocalBroadcastManager.getInstance(getContext());
+
+ IntentFilter filter = new IntentFilter(TCONST.START_PROGRESSIVE_UPDATE);
+ filter.addAction(TCONST.UPDATE_PROGRESS);
+ filter.addAction(TCONST.PROGRESS_TITLE);
+ filter.addAction(TCONST.PROGRESS_MSG1);
+ filter.addAction(TCONST.PROGRESS_MSG2);
+
+ bReceiver = new LoadReceiver();
+
+ bManager.registerReceiver(bReceiver, filter);
+ }
+
+
+ @Override
+ protected void onFinishInflate() {
+
+ super.onFinishInflate();
+
+ SprogressTitle = (TextView) findViewById(R.id.SprogressTitle);
+ SprogressMsg1 = (TextView) findViewById(R.id.SprogressMsg1);
+ SprogressMsg2 = (TextView) findViewById(R.id.SprogressMsg2);
+
+ SprogressBarI = (ProgressBar) findViewById(R.id.SprogressBarI);
+ SprogressBarD = (ProgressBar) findViewById(R.id.SprogressBarD);
+ }
+
+
+ /**
+ * Release resources and disconnect from broadcast Manager
+ */
+ public void onDestroy() {
+
+ try {
+ setOnClickListener(null);
+ bManager.unregisterReceiver(bReceiver);
+ }
+ catch(Exception e) {
+ }
}
+ class LoadReceiver extends BroadcastReceiver {
+
+ public void onReceive (Context context, Intent intent) {
+
+ Log.d("Loader", "Broadcast received: ");
+
+ switch(intent.getAction()) {
+
+ case TCONST.START_INDETERMINATE_UPDATE:
+ SprogressBarI.setVisibility(VISIBLE);
+ SprogressBarD.setVisibility(GONE);
+ requestLayout();
+ break;
+
+ case TCONST.START_PROGRESSIVE_UPDATE:
+ SprogressBarD.setVisibility(VISIBLE);
+ SprogressBarI.setVisibility(GONE);
+
+ String maxVal = intent.getStringExtra(TCONST.TEXT_FIELD);
+
+ SprogressBarD.setProgress(0);
+ SprogressBarD.setMax(Integer.parseInt(maxVal));
+ requestLayout();
+ break;
+
+ case TCONST.UPDATE_PROGRESS:
+ String curVal = intent.getStringExtra(TCONST.TEXT_FIELD);
+
+ SprogressBarD.setProgress(Integer.parseInt(curVal));
+ SprogressBarD.postInvalidate();
+ break;
+
+ case TCONST.PROGRESS_TITLE:
+ String titleText = intent.getStringExtra(TCONST.TEXT_FIELD);
+
+ SprogressTitle.setText(titleText);
+ SprogressTitle.setVisibility(VISIBLE);
+ break;
+
+ case TCONST.PROGRESS_MSG1:
+ String msgText1 = intent.getStringExtra(TCONST.TEXT_FIELD);
+
+ SprogressMsg1.setText(msgText1);
+ SprogressMsg1.setVisibility(VISIBLE);
+ break;
+
+ case TCONST.PROGRESS_MSG2:
+ String msgText2 = intent.getStringExtra(TCONST.TEXT_FIELD);
+
+ SprogressMsg2.setText(msgText2);
+ SprogressMsg2.setVisibility(VISIBLE);
+ break;
+
+ }
+ }
+ }
+
}
diff --git a/util/src/main/java/cmu/xprize/util/CLogManager.java b/util/src/main/java/cmu/xprize/util/CLogManager.java
deleted file mode 100644
index c9fa7a7..0000000
--- a/util/src/main/java/cmu/xprize/util/CLogManager.java
+++ /dev/null
@@ -1,462 +0,0 @@
-//*********************************************************************************
-//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//*********************************************************************************
-
-package cmu.xprize.util;
-
-import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.util.HashMap;
-
-public class CLogManager implements ILogManager {
-
- private LogThread logThread; // background thread handling log data
- private String log_Path;
- private boolean isLogging = false;
-
- private Handler logHandler;
- private HashMap queueMap = new HashMap();
- private boolean mDisabled = false;
-
- private File logFile;
- private FileOutputStream logStream;
- private java.nio.channels.FileLock logLock;
- private FileWriter logWriter;
- private boolean logWriterValid = false;
-
- // Datashop specific
-
- private File logDSFile;
- private FileOutputStream logDSStream;
- private java.nio.channels.FileLock logDSLock;
- private FileWriter logDSWriter;
- private boolean logDSWriterValid = false;
-
-
- final private String TAG = "CLogManager";
-
-
- // Singleton
- private static CLogManager ourInstance = new CLogManager();
-
- public static CLogManager getInstance() {
- return ourInstance;
- }
-
- private CLogManager() {
- }
-
-
- public void startLogging(String logPath) {
-
- log_Path = logPath;
-
- // Restart the log if necessary
- //
- stopLogging();
-
- isLogging = true;
- mDisabled = false;
- Log.d(TAG, "Startup");
-
- logThread = new LogThread(TAG);
- logThread.start();
-
- try {
- logHandler = new Handler(logThread.getLooper());
- }
- catch(Exception e) {
- Log.d(TAG, "Handler Create Failed:" + e);
- }
-
- lockLog();
- }
-
-
- /**
- * Stop accepting new packets -
- * Causes the thread to flush the input queue and then exit
- *
- */
- public void stopLogging() {
-
- if(isLogging) {
- Log.d(TAG, "Shutdown begun");
-
- isLogging = false;
- mDisabled = true;
-
- // Terminate the log thread - flush the queue prior to exit
- //
- try {
-
- logThread.getLooper().quitSafely();
-
- logThread.join(); // waits until it finishes
- Log.d(TAG, "Shutdown complete");
-
- } catch (InterruptedException e) {
- }
-
- releaseLog();
- }
- }
-
-
- /**
- * This is a background thread on which to process all log data requests
- *
- */
- private final class LogThread extends HandlerThread {
-
- public LogThread(String name) {
- super(name);
- }
-
- public LogThread(String name, int priority) {
- super(name, priority);
- }
- }
-
-
- /**
- * This is the central processsing point of the data log - this runs on an independent thread
- * from the UI.
- */
- public class Queue implements Runnable {
-
- protected String dataPacket;
- protected String target = CPreferenceCache.getPrefID(TCONST.ENGINE_INSTANCE) + TCONST.JSONLOG;
-
- public Queue(String packet) {
- dataPacket = packet;
- }
-
- public Queue(String _packet, String _target) {
- dataPacket = _packet;
- target = _target;
- }
-
- @Override
- public void run() {
-
- try {
- queueMap.remove(this);
-
- writePacketToLog(dataPacket, target);
-
- } catch (Exception e) {
- CErrorManager.logEvent(TAG, "Write Error:", e, false);
- }
- }
- }
-
-
- /**
- * We use file locks to keep the logs around until we are finished. The XPrize initiative used a
- * Google Drive Sync utility that required locking the files so they weren't deleted while in
- * use. So this is not a requirement otherwise.
- *
- */
- public void lockLog() {
-
- // Release previous log file if still locked
- //
- if(logWriterValid) {
- releaseLog();
- }
-
- String path = CPreferenceCache.getPrefID(TCONST.ENGINE_INSTANCE) + TCONST.JSONLOG;
- String dsPath = CPreferenceCache.getPrefID(TCONST.ENGINE_INSTANCE) + TCONST.DATASHOP + TCONST.JSONLOG;
-
- String state = Environment.getExternalStorageState();
-
- if (Environment.MEDIA_MOUNTED.equals(state)) {
-
- String outPath;
- String outDSPath;
-
- // Validate output folder
- outPath = log_Path;
- outDSPath = log_Path;
- File outputFile = new File(outPath);
-
- if (!outputFile.exists())
- outputFile.mkdir();
-
- // Generate a tutor instance-unique id for the log name
- //
- outPath += path;
-
- logFile = new File(outPath);
-
- try {
- logStream = new FileOutputStream(logFile);
- logLock = logStream.getChannel().lock();
- logWriter = new FileWriter(outPath, TCONST.APPEND);
-
- logWriterValid = true;
-
- // Begin the root JSON element
- postPacket("{");
-
- } catch (Exception e) {
- Log.d(TAG, "lockLog Failed: " + e);
- }
-
-
- //**** DATASHOP
-
- // Generate a tutor instance-unique id for DataShop
- //
- outDSPath += dsPath;
-
- logDSFile = new File(outDSPath);
-
- try {
- logDSStream = new FileOutputStream(logDSFile);
- logDSLock = logDSStream.getChannel().lock();
- logDSWriter = new FileWriter(outDSPath, TCONST.APPEND);
-
- logDSWriterValid = true;
-
- } catch (Exception e) {
- Log.d(TAG, "DataShop lockLog Failed: " + e);
- }
-
- }
- }
-
-
- public void releaseLog() {
-
- try {
- if(logWriterValid) {
-
- // Terminate the root JSON element
- postPacket("}");
-
- logWriterValid = false;
-
- logWriter.flush();
- logWriter.close();
-
- logLock.release();
- logStream.close();
- }
- }
- catch(Exception e) {
- Log.d(TAG, "releaseLog Failed: " + e);
- }
-
- //**** DATASHOP
-
- try {
- if(logDSWriterValid) {
-
- logDSWriterValid = false;
-
- logDSWriter.flush();
- logDSWriter.close();
-
- logDSLock.release();
- logDSStream.close();
- }
- }
- catch(Exception e) {
- Log.d(TAG, "releaseLog Failed: " + e);
- }
-
- }
-
-
-
- /**
- * Note that this is currently XPrize log specific.
- * TODO: make general Purpose
- */
- public void writePacketToLog(String jsonPacket, String path) {
-
- // Append Glyph Data to file
- try {
- // Throws if there is a JSON serializatin error
- //
- if(logWriterValid) {
- logWriter.write(jsonPacket);
- logWriter.flush();
- }
- }
- catch(Exception e) {
- Log.e(TAG, "Serialization Error: " + e);
- }
- }
-
-
- /**
- * Keep a mapping of pending messages so we can flush the queue if we want to terminate
- * the tutor before it finishes naturally.
- *
- * @param qCommand
- */
- private void enQueue(Queue qCommand) {
-
- if (!mDisabled) {
- queueMap.put(qCommand, qCommand);
-
- logHandler.post(qCommand);
- }
- }
-
- /**
- * Post a command to this scenegraph queue
- *
- * @param command
- */
- public void post(String command) {
-
- enQueue(new Queue(command));
- }
-
-
- /**
- * Post a command to this scenegraph queue
- *
- * @param command
- */
- public void postTo(String command, String target) {
-
- enQueue(new Queue(command, target));
- }
-
-
- @Override
- public void postSystemEvent(String Tag, String Msg) {
-
- String packet;
-
- // Emit to LogCat
- //
- Log.i(TAG, Msg);
-
- packet = "{" +
- "\"type\":\"Event\"," +
- "\"time\":\"" + System.currentTimeMillis() + "\"," +
- "\"tag\":\"" + Tag + "\"," +
- "\"msg\":\"" + Msg + "\"," +
- "},\n";
-
- postTo(packet, TCONST.ENGINE_INSTANCE + TCONST.JSONLOG);
- }
-
-
- @Override
- public void postSystemTimeStamp(String Tag) {
-
- String packet;
-
- packet = "{" +
- "\"type\":\"TimeStamp\"," +
- "\"time\":\"" + System.currentTimeMillis() + "\"," +
- "\"tag\":\"" + Tag + "\"," +
- "},\n";
-
- postTo(packet, TCONST.ENGINE_INSTANCE + TCONST.JSONLOG);
- }
-
-
- @Override
- public void postEvent(String Tag, String Msg) {
-
- String packet;
-
- // Emit to LogCat
- //
- Log.i(TAG, Msg);
-
- packet = "{" +
- "\"type\":\"Event\"," +
- "\"time\":\"" + System.currentTimeMillis() + "\"," +
- "\"tag\":\"" + Tag + "\"," +
- "\"msg\":\"" + Msg + "\"," +
- "},\n";
-
- post(packet);
- }
-
-
- @Override
- public void postTimeStamp(String Tag) {
-
- String packet;
-
- packet = "{" +
- "\"type\":\"TimeStamp\"," +
- "\"time\":\"" + System.currentTimeMillis() + "\"," +
- "\"tag\":\"" + Tag + "\"," +
- "},\n";
-
- post(packet);
- }
-
-
- @Override
- public void postError(String Tag, String Msg) {
-
- String packet;
-
- packet = "{" +
- "\"type\":\"Error\"," +
- "\"time\":\"" + System.currentTimeMillis() + "\"," +
- "\"tag\":\"" + Tag + "\"," +
- "\"msg\":\"" + Msg + "\"," +
- "},\n";
-
- post(packet);
- }
-
-
- @Override
- public void postError(String Tag, String Msg, Exception e) {
-
- String packet;
-
- packet = "{" +
- "\"type\":\"Error\"," +
- "\"time\":\"" + System.currentTimeMillis() + "\"," +
- "\"tag\":\"" + Tag + "\"," +
- "\"msg\":\"" + Msg + "\"," +
- "\"exception\":\"" + e.toString() + "\"," +
- "},\n";
-
- post(packet);
- }
-
-
- @Override
- public void postPacket(String packet) {
-
- post(packet);
- }
-
-}
diff --git a/util/src/main/java/cmu/xprize/util/CMessageQueueFactory.java b/util/src/main/java/cmu/xprize/util/CMessageQueueFactory.java
new file mode 100644
index 0000000..a841252
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/CMessageQueueFactory.java
@@ -0,0 +1,237 @@
+package cmu.xprize.util;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import cmu.xprize.comp_logging.CErrorManager;
+
+/**
+ * CMessageQueue
+ *
+ * Why does every single CComponent have a different Queue? That's dumb.
+ * Let's just put them all in the same class.
+ * Created by kevindeland on 8/27/19.
+ */
+
+public class CMessageQueueFactory {
+
+ private IMessageQueueRunner runner;
+ private String TAG;
+
+ public CMessageQueueFactory(IMessageQueueRunner runner, String TAG) {
+ this.runner = runner;
+ this.TAG = TAG;
+ }
+
+
+ //************************************************************************
+ //************************************************************************
+ // Component Message Queue -- Start
+
+ private final Handler mainHandler = new Handler(Looper.getMainLooper());
+ private HashMap queueMap = new HashMap();
+ private HashMap nameMap = new HashMap();
+ private boolean _qDisabled = false;
+
+ public class Queue implements Runnable {
+
+ protected String _command;
+ String _name; // used to find a command and cancel it.
+ Object _targetObject;
+ String _targetString;
+
+ public Queue(String command) {
+ _command = command;
+ }
+
+ // needed for cancelling
+ public Queue (String name, String command) {
+ this._name = name;
+ this._command = command;
+
+ if (name != null) {
+ nameMap.put(name, this);
+ }
+ }
+
+ public Queue(String command, Object target) {
+ _command = command;
+ _targetObject = target;
+ }
+
+ public Queue(String name, String command, String target) {
+ _name = name;
+ _command = command;
+ _targetString = target;
+ }
+
+ public String getCommand() {
+ return _command;
+ }
+
+
+ @Override
+ public void run() {
+
+ try {
+
+ if (_name != null) {
+ nameMap.remove(_name);
+ }
+
+ queueMap.remove(this);
+ Log.d("QUEUE_FACTORY",
+ String.format(
+ "CMessageQueue.run _name=%s;_command=%s;_targetObject=%s;_targetString=%s",
+ _name, _command, _targetObject, _targetString));
+
+ // ugh this is somewhat different for each file (see QueueConstructorVars.txt)
+ // could refactor
+ if(_targetObject != null) {
+ Log.wtf("QUEUE_FACTORY", "runCommand(object)");
+ runner.runCommand(_command, _targetObject);
+ } else if(_targetString != null) {
+ runner.runCommand(_command, _targetString);
+ Log.wtf("QUEUE_FACTORY", "runCommand(string)");
+ } else {
+ runner.runCommand(_command);
+ Log.wtf("QUEUE_FACTORY", String.format("runCommand(%s)", _command));
+ }
+
+ }
+ catch(Exception e) {
+ CErrorManager.logEvent(TAG, "Run Error: cmd:" + _command + " tar: " + _targetObject + " >", e, false);
+ }
+ }
+ }
+
+
+ /**
+ * Disable the input queues permenantly in prep for destruction
+ * walks the queue chain to diaable scene queue
+ *
+ */
+ public void terminateQueue() {
+
+ // disable the input queue permenantly in prep for destruction
+ //
+ _qDisabled = true;
+ flushQueue();
+ }
+
+
+ /**
+ * Remove any pending scenegraph commands.
+ *
+ */
+ private void flushQueue() {
+
+ Iterator> tObjects = queueMap.entrySet().iterator();
+
+ while(tObjects.hasNext() ) {
+ Map.Entry entry = (Map.Entry) tObjects.next();
+
+ Log.d(TAG, "Post Cancelled on Flush: " + ((Queue)entry.getValue()).getCommand());
+
+ mainHandler.removeCallbacks((Queue)(entry.getValue()));
+ }
+ }
+
+ /**
+ * Remove named posts
+ *
+ */
+ public void cancelPost(String name) {
+
+ Log.d(TAG, "Cancel Post Requested: " + name);
+
+ while(nameMap.containsKey(name)) {
+
+ Log.d(TAG, "Post Cancelled: " + name);
+
+ mainHandler.removeCallbacks((Queue) (nameMap.get(name)));
+ nameMap.remove(name); // JUDITH replicate
+ }
+ }
+
+ public void postEvent(String event) {
+ postEvent(event, 0);
+ }
+
+ public void postEvent(String event, Integer delay) {
+
+ post(event, (long) delay);
+ }
+
+ public void postNamed(String name, String command, String target, Long delay) {
+ enQueue(new Queue(name, command, target), delay);
+ }
+
+ /**
+ * Keep a mapping of pending messages so we can flush the queue if we want to terminate
+ * the tutor before it finishes naturally.
+ *
+ * @param qCommand
+ */
+ private void enQueue(Queue qCommand) {
+ enQueue(qCommand, 0);
+ }
+ private void enQueue(Queue qCommand, long delay) {
+
+ if(!_qDisabled) {
+ queueMap.put(qCommand, qCommand);
+
+ if(delay > 0) {
+ mainHandler.postDelayed(qCommand, delay);
+ }
+ else {
+ mainHandler.post(qCommand);
+ }
+ }
+ }
+
+ /**
+ * Post a command to the tutorgraph queue
+ *
+ * @param command
+ */
+ public void post(String command) {
+ post(command, 0);
+ }
+ public void post(String command, long delay) {
+
+ enQueue(new Queue(command), delay);
+ }
+
+
+ public void postNamed(String name, String command, Long delay) {
+ enQueue(new Queue(name, command), delay);
+ }
+
+ /**
+ * Post a command and target to this scenegraph queue
+ *
+ * @param command
+ */
+ public void post(String command, Object target) {
+ post(command, target, 0);
+ }
+ public void post(String command, Object target, long delay) {
+
+ enQueue(new Queue(command, target), delay);
+ }
+
+
+ // Component Message Queue -- End
+ //************************************************************************
+ //************************************************************************
+
+}
+
+
+
diff --git a/util/src/main/java/cmu/xprize/util/CPlacementTest_Tutor.java b/util/src/main/java/cmu/xprize/util/CPlacementTest_Tutor.java
new file mode 100644
index 0000000..bda761d
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/CPlacementTest_Tutor.java
@@ -0,0 +1,35 @@
+package cmu.xprize.util;
+
+import org.json.JSONObject;
+
+/**
+ * RoboTutor
+ *
+ * Created by kevindeland on 4/12/18.
+ */
+
+public class CPlacementTest_Tutor implements ILoadableObject {
+
+
+ public int l;
+
+ // json loadable
+ public String tutor;
+ public String level;
+ public String fail;
+ public String pass;
+
+
+ //************ Serialization
+
+
+ @Override
+ public void loadJSON(JSONObject jsonObj, IScope scope) {
+
+ JSON_Helper.parseSelf(jsonObj, this, CClassMap.classMap, scope);
+
+ l = Integer.parseInt(level);
+
+ }
+
+}
diff --git a/util/src/main/java/cmu/xprize/util/CTutorData_Metadata.java b/util/src/main/java/cmu/xprize/util/CTutorData_Metadata.java
new file mode 100644
index 0000000..94ebb8e
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/CTutorData_Metadata.java
@@ -0,0 +1,1111 @@
+package cmu.xprize.util;
+
+import android.content.Context;
+import android.text.style.LocaleSpan;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+
+import cmu.xprize.common.R;
+
+/**
+ * RoboTutor
+ *
+ * Created by kevindeland on 5/18/18.
+ */
+
+public class CTutorData_Metadata {
+
+ /**
+ * NEW_THUMBS why can't this return a string instead?
+ * @param tutor
+ * @return
+ */
+ public static TCONST.Thumb getThumbImage(CAt_Data tutor) {
+
+
+ Log.d("CHUNT", "tutor_desc = " + tutor.tutor_desc);
+ // tutortype is first token... e.g. "story.hear" --> "story"
+ String[] tutorDesc = tutor.tutor_desc.split("\\.");
+ if (tutorDesc.length == 0) {
+ return TCONST.Thumb.NOTHING;
+ }
+ Log.d("CHUNT", "tutorDesc = " + tutorDesc + ", " + tutorDesc.length);
+
+
+
+ String tutorType = tutorDesc[0];
+ Log.d("CHUNT", "tutorType = " + tutorType);
+
+ switch (tutorType) {
+ case "akira":
+ return TCONST.Thumb.AKIRA;
+
+ case "bpop":
+ switch (tutor.tutor_desc) {
+ case "bpop.num":
+ case "bpop.addsub":
+ return TCONST.Thumb.BPOP_NUM;
+
+ case "bpop.gl":
+ return TCONST.Thumb.GL;
+
+ case "bpop.mn":
+ return TCONST.Thumb.MN;
+
+ // bpop.ltr
+ default:
+ return TCONST.Thumb.BPOP_LTR;
+ }
+
+ case "countingx":
+
+ String startingNumber = tutor.tutor_id.split("[:_]")[1];
+
+ switch (startingNumber) {
+ case "1":
+ return TCONST.Thumb.CX_1;
+
+ case "10":
+ return TCONST.Thumb.CX_10;
+
+ case "100":
+ return TCONST.Thumb.CX_100;
+
+ default:
+ return TCONST.Thumb.CX_1;
+ }
+
+
+ case "math":
+ // only one
+ return TCONST.Thumb.MATH;
+
+ case "numberscale":
+ // only one
+ return TCONST.Thumb.NUMSCALE;
+
+
+ case "story":
+
+ String storyPrefix = tutor.tutor_id.split(":")[2];
+ // story or song
+ if(storyPrefix.toLowerCase().contains("song") || storyPrefix.toLowerCase().contains("tune")) {
+ return TCONST.Thumb.SONG;
+ }
+ else if(storyPrefix.startsWith("story")){
+ String storyNum = storyPrefix.split("_")[1];
+ int storyNumInt = Integer.parseInt(storyNum);
+
+ switch (storyNumInt % 5) {
+ case 0:
+ return TCONST.Thumb.STORY_5;
+
+ case 1:
+ return TCONST.Thumb.STORY_1;
+
+ case 2:
+ return TCONST.Thumb.STORY_2;
+
+ case 3:
+ return TCONST.Thumb.STORY_3;
+
+ case 4:
+ return TCONST.Thumb.STORY_4;
+ }
+
+ } else {
+ return TCONST.Thumb.STORY_NONSTORY;
+ }
+
+
+ case "write":
+ // only one... for now
+ return TCONST.Thumb.WRITE;
+
+
+ // Added Tutors Code Drop 2
+ case "picmatch":
+ return TCONST.Thumb.PICMATCH;
+
+ case "placevalue":
+ case "place":
+ return TCONST.Thumb.PLACEVALUE;
+
+ case "numcompare":
+ return TCONST.Thumb.NUMCOMPARE;
+
+ case "spelling":
+ return TCONST.Thumb.SPELLING;
+
+ case "bigmath":
+ return TCONST.Thumb.BIGMATH;
+
+
+
+ default:
+ return TCONST.Thumb.NOTHING;
+
+ }
+
+ }
+
+ /**
+ * Return filename to a thumb image.
+ *
+ * @param tutor
+ * @return
+ */
+ public static String getThumbName(CAt_Data tutor) {
+ // NEW_THUMBS (if returns null... then use the "getThumbImage" method")
+
+ // check story
+ if (tutor.tutor_desc.startsWith("story")) {
+
+ StringBuilder builder = new StringBuilder("thumb_");
+ String suffix = null;
+
+ switch(tutor.tutor_desc) {
+
+ case "story.hear":
+ suffix = "_hear";
+ break;
+
+ case "story.echo":
+ case "story.read":
+ case "story.hide":
+ case "story.reveal":
+ case "story.parrot":
+ suffix = "_read";
+ break;
+
+ case "story.gen.hide":
+ case "story.clo.hear":
+ case "story.pic.hear":
+ case "story.gen.hear":
+ suffix = "_comp";
+ break;
+ }
+
+ String storyName = tutor.tutor_id.split("::")[1];
+ builder.append(storyName)
+ .append(suffix)
+ .append(".png");
+
+ return builder.toString();
+ }
+
+
+ switch(tutor.tutor_desc) {
+ case "numcompare":
+ return "thumb_number_discrimination.png";
+
+ case "picmatch":
+ return "thumb_picture_matching.png";
+
+ case "place.value":
+ case "placevalue":
+ return "thumb_place_value.png";
+
+ case "spelling":
+ return "thumb_spelling_tutor.png";
+
+ case "write.sen.corr.ltr":
+ case "write.sen.corr.wrd":
+ case "write.sen.corr.sen":
+ case "write.sen.copy.ltr":
+ case "write.sen.copy.wrd":
+ case "write.sen.copy.sen":
+ return "thumb_sentence_writing.png";
+
+ case "bigmath":
+ return "thumb_bigmath_1d.png";
+
+ }
+
+ return null;
+ }
+
+ /**
+ * Given a tutor, return a list of Strings that describes the tutor.
+ * @param tutor
+ * @return
+ */
+ public static ArrayList parseNameIntoLabels(CAt_Data tutor) {
+
+ // OPEN_SOURCE FIXME
+ // write.ltr.uc.trc:A..D_asc √√√
+ // bpop.ltr.lc:A..D.asc.show.mc --> "null A to Z"
+ // bpop.wrd:m2M.noShow.rise --> nothing
+ // bpop.wrd:dolch_preprimer.noShow.mc --> nothing
+ //
+ // write.wrd:phon.m2M √√√
+
+ Log.d("CHUNT", "tutor_desc = " + tutor.tutor_desc);
+ // tutortype is first token... e.g. "story.hear" --> "story"
+ String[] tutorDesc = tutor.tutor_desc.split("\\.");
+ if (tutorDesc.length == 0) {
+ return null;
+ }
+ Log.d("CHUNT", "tutorDesc = " + tutorDesc + ", " + tutorDesc.length);
+
+ int[] tutor_CONST;
+
+
+ String tutorType = tutorDesc[0];
+ Log.d("CHUNT", "tutorType = " + tutorType);
+
+
+ ArrayList result = new ArrayList<>();
+
+ try {
+
+
+ switch (tutorType) {
+ case "akira":
+ result.add("Akira");
+
+ result = processAkiraTutorId(tutor, result);
+ break;
+
+ case "bpop":
+ result = processBubblePopTutorId(tutor, result);
+
+ break;
+
+ case "countingx":
+ result.add("Tap to Count");
+ String[] countingSplit = tutor.tutor_id.split("[:_]");
+ result.add(String.format("Tap to count from %s to %s.", countingSplit[1], countingSplit[2]));
+ break;
+
+
+ case "math":
+ result.add("Math");
+ String[] splitMe = tutor.tutor_id.split(":");
+ String[] secondPart = splitMe[1].split("\\.");
+
+ String[] thirdPart = secondPart[3].split("-");
+
+ StringBuilder descriptor = new StringBuilder();
+ descriptor.append(thirdPart[0].equals("SUB") ? "Subtract " : "Add ");
+ descriptor.append(String.format("values between %s and %s", secondPart[0], secondPart[2]));
+ result.add(descriptor.toString());
+ break;
+
+ case "bigmath":
+ result.add("Math");
+ break;
+
+ case "place":
+ case "place.value":
+ result.add("Place Value");
+ break;
+
+ case "numcompare":
+ result.add("Number Comparison");
+ break;
+
+ case "picmatch":
+ result.add("Picture Match");
+ break;
+
+ case "spelling":
+ result.add("Spelling");
+ break;
+
+ case "numberscale":
+ result.add("Number Scale");
+ String numscaleSuffix = tutor.tutor_id.split(":")[1];
+ String[] numscaleDetails = numscaleSuffix.split("\\.");
+ String numscaleOffset = numscaleDetails[3].substring("off".length());
+ result.add(String.format("Explore numbers %s to %s, counting by %s.", numscaleDetails[0], numscaleDetails[2], numscaleOffset));
+ break;
+
+
+ case "story":
+ result.add("Story");
+ result = processStoryTutorId(tutor, result);
+ break;
+
+
+ case "write":
+ result.add("Write");
+ result = processWriteTutorId(tutor, result);
+ break;
+
+
+ default:
+ break;
+ }
+ }catch (Exception e) {
+ // result.add("Error generating name");
+ // commenting out (for now) so that it doesn't show the error
+ }
+
+ // give it some empty lines
+ for (int i = result.size(); i < 4; i++) {
+ result.add(""); // add blanks til we get to the end
+ }
+ result.add("" + tutor.tutor_id + ""); // add ID for each one
+
+ return result;
+ }
+
+
+ private static ArrayList processStoryTutorId(CAt_Data tutor, ArrayList result) {
+
+ // story.hear::1_1 --> "Error generating name"
+ // story.[hear|echo|read]::\d_\d --> "Error generating name"
+
+ String[] splitStory = tutor.tutor_id.split(":");
+ String storySuffix = splitStory[2];
+
+ if (storySuffix.split("[0-9]")[0].equals("")) { // if begins with number
+ // it's a number story
+ String[] storyNumberSuffix = storySuffix.split("[._]");
+
+ //System.out.println(Arrays.toString(splitStory));
+ String[] storyMode = splitStory[0].split("\\.");
+ String storyModeCap = storyMode[1].substring(0, 1).toUpperCase() + storyMode[1].substring(1);
+ //System.out.println(Arrays.toString(storyMode));
+
+ result.add(String.format(Locale.US, "%s numbers from %d to %d",
+ storyModeCap, Integer.parseInt(storyNumberSuffix[0]), Integer.parseInt(storyNumberSuffix[2])));
+
+ } else {
+
+ String[] storySuffixSplit = storySuffix.split("[_\\.\\-]");
+ String syl1, syl2, letter, wordType;
+ StringBuilder descriptionBuilder = new StringBuilder();
+
+ boolean isLitStory = true;
+
+ String storyType = tutor.tutor_desc.split("\\.")[1];
+ descriptionBuilder.append(storyType.substring(0,1).toUpperCase() + storyType.substring(1) + " "); // capitalize first letter
+
+ //System.out.println(Arrays.toString(storySuffixSplit));
+ switch(storySuffixSplit[0]) {
+
+ case "ltr":
+ letter = storySuffixSplit[1]; // "ltr-A.rand"
+ descriptionBuilder.append(String.format("the letter %s", letter));
+ break;
+
+ case "vow":
+ descriptionBuilder.append(String.format("all vowels"));
+ break;
+
+ case "all":
+ descriptionBuilder.append(String.format("all letters"));
+ break;
+
+ case "syl":
+ syl1 = storySuffixSplit[1];
+ syl2 = storySuffixSplit[3];
+ descriptionBuilder.append(String.format("the syllables %s through %s", syl1, syl2));
+ break;
+
+ case "begin":
+ syl1 = storySuffixSplit[2];
+ descriptionBuilder.append(String.format("words beginning with %s", syl1));
+ break;
+
+ case "end":
+ syl1 = storySuffixSplit[2];
+ descriptionBuilder.append(String.format("words ending with %s", syl1));
+ break;
+
+ case "comm":
+ String wordCategory = storySuffixSplit[3];
+ if (wordCategory.equals("body")) wordCategory = "the " + wordCategory;
+ descriptionBuilder.append(String.format("common words about %s", wordCategory));
+ break;
+
+ case "HF":
+ descriptionBuilder.append(String.format(Locale.US, "common %d-letter words", Integer.parseInt(storySuffixSplit[3])));
+ break;
+
+ case "nonwrd":
+ descriptionBuilder.append(String.format(Locale.US, "%d-letter nonsense words", Integer.parseInt(storySuffixSplit[2])));
+ break;
+
+ default:
+
+ isLitStory = false;
+ // it's a real story...
+ String storyName = storySuffix.split("__")[0]; // this removes the iteration at the end
+ result.add(storyName.replace("_", " ")); // "Kusoma_Song" --> "Kusoma Song"
+ break;
+ }
+
+ if(isLitStory) {
+ //System.out.println(descriptionBuilder.toString());
+ result.add(descriptionBuilder.toString());
+ }
+
+
+ }
+
+ return result;
+ }
+
+ private static ArrayList processWriteTutorId(CAt_Data tutor, ArrayList result) {
+ // write.wrd.dic:phon.r2R √√√
+ // write.wrd.trc:phon.m2M √√√
+ // write.wrd:dolch_preprimer √√√
+ // write.wrd:dolch_1st_grade √√√
+
+ // write.ltr.uc.trc:A..D_asc √√√
+
+ String splitDesc[] = tutor.tutor_desc.split("\\.");
+ String modeOfEntry = "Write";
+ // check for trace
+ if(splitDesc.length > 2) {
+ switch(splitDesc[2]) {
+
+ case "trc":
+ modeOfEntry = "Trace";
+ break;
+
+ case "dic":
+ default:
+ // do nothing
+ break;
+ }
+ }
+
+ String splitSuffix[] = tutor.tutor_id.split(":")[1].split("[\\-\\.]");
+ String str;
+ switch(splitDesc[1]) {
+
+ // letters
+ // write.ltr.uc.trc:vow.asc.A..Z.1
+ // write.ltr.lc:all.asc.A..Z.11
+ case "ltr":
+ String caps;
+ switch(splitDesc[2]) {
+ case "uc":
+ caps = "uppercase";
+ break;
+ case "lc":
+ caps = "lowercase";
+ break;
+ default:
+ caps = null;
+ }
+
+ result.add(String.format("%s %s letters", modeOfEntry, caps));
+
+ String vowels = null, order;
+ switch(splitSuffix[0]) {
+ case "vow":
+ vowels = "Vowels";
+ break;
+
+ case "all":
+ vowels = "All Letters";
+ break;
+
+ default:
+ break;
+
+ }
+
+ // THIS is a mess... fix
+ if (vowels == null) {
+ // write.ltr.uc:A..D_asc
+ order = splitSuffix[2].substring(2);
+ if (order.equals("asc")) order = "ascending";
+
+ result.add(String.format("Letters %s through %s, %s", splitSuffix[0], splitSuffix[2].substring(0, 1), order));
+ } else {
+ switch(splitSuffix[1]) {
+ case "asc":
+ order = "Ascending";
+ break;
+
+ case "rand":
+ default:
+ order = "Random";
+
+ }
+ result.add(String.format("%s, %s", vowels, order));
+ }
+
+ break;
+
+ // words
+ // write.wrd.trc:syl.2ch..2ch..1
+ // write.wrd:syl.2ch..2ch..2
+ // write.wrd:lc.wrd.cat-food.1
+ // write.wrd:lc.wrd.cat-body.4
+ // write.wrd:lc.wrd.len-4.1
+ // write.wrd:lc.wrd.len-10
+ // write.wrd:lc.nonwrd.len-4.lev1
+ case "wrd":
+
+ String wordToWrite = null;
+
+ //System.out.println(Arrays.toString(splitSuffix));
+ if(splitSuffix[0].equals("syl")) {
+ String syl = splitSuffix[1];
+ wordToWrite = String.format("the syllable %s", syl);
+ } else if (splitSuffix[0].equals("phon")) {
+ String[] g2p = splitSuffix[1].split("2");
+ wordToWrite = " words with g2p: " + g2p[0] + " to " + g2p[1] + "";
+
+ } else if (splitSuffix[1].startsWith("dolch")) {
+ wordToWrite = splitSuffix[1].substring(6) + " grade Dolch words";
+ } else if (splitSuffix[1].equals("wrd")) {
+
+ if(splitSuffix[2].equals("cat")) {
+ wordToWrite = String.format("common words about %s", splitSuffix[3]);
+ } else if (splitSuffix[2].equals("len")) {
+ wordToWrite = String.format(Locale.US, "common %d-letter words", Integer.parseInt(splitSuffix[3]));
+ }
+
+ } else if (splitSuffix[1].equals("nonwrd")) {
+
+ wordToWrite = String.format(Locale.US, "%d-letter nonsense words", Integer.parseInt(splitSuffix[3]));
+ }
+
+ result.add(String.format("%s %s", modeOfEntry, wordToWrite));
+ break;
+
+ // missing letter
+ // write.missingLtr:lc.begin.ha.1
+ // write.missingLtr:lc.end.ko.9
+ // "write.missingLtr:0.1.5.fin.s"
+ case "missingLtr":
+
+ modeOfEntry = "Complete";
+ String wordToComplete = null;;
+ System.out.println(Arrays.toString(splitSuffix));
+ if(splitSuffix[1].equals("begin")) {
+ wordToComplete = String.format(" the word beginning with %s", splitSuffix[2]);
+ } else if (splitSuffix[1].equals("end")) {
+ wordToComplete = String.format(" the word ending with %s", splitSuffix[2]);
+ } else {
+ wordToComplete = String.format(" the word containing %s", splitSuffix[3]);
+ }
+ result.add(String.format("%s %s", modeOfEntry, wordToComplete));
+ break;
+
+ // numbers
+ case "num":
+
+ String numberToWrite = "the number";
+ result.add(String.format("%s %s", modeOfEntry, numberToWrite));
+ break;
+
+ // arith
+ case "arith":
+
+ String answerToWrite = "the equation";
+ result.add(String.format("%s %s", modeOfEntry, answerToWrite));
+ break;
+
+
+ case "dolch":
+
+ str = "Write " + splitSuffix[2] + " grade Dolch words";
+ result.add(str);
+ break;
+
+
+ case "phon":
+
+
+
+
+
+ default:
+ }
+
+
+ // MATH
+ // write.num:WR-100s.8
+ // write.num:WR-3D.9
+ // write.num:WR-2D.6
+ // write.num.trc:WR-1..4.1"
+ // write.num.trc:WR-1..10.2
+ // write.num.dic:WR-1..20.13
+ // write.num.dic:WR-2D.15
+
+ // write.arith:ADD-2D-H.3 -- write.arith.add
+ // write.arith:SUB-2D-H.4 -- write.arith.sub
+ // write.arith:ADD-3D-H
+
+
+
+ return result;
+ }
+
+
+ private static ArrayList processAkiraTutorId(CAt_Data tutor, ArrayList result) {
+
+ //
+ // LIT
+ // "akira:vow.ltr.lc:I..I.vow.10.rand.say.8"
+ // "akira:all.ltr.uc:CH..CH.all.10.rand.say.17"
+ // akira:syl.lc.say.nye..nye.noShow..19
+ // akira:syl.lc.say.ku..ku.noShow..9
+ // akira:begin.wrd.wa.show.8
+ // akira:end.wrd.wa.show.15
+ // akira:comm.wrd.body.noShow.4
+ // akira:HF.wrd.7.noShow.4
+ // akira:HF.wrd.4.noShow.1
+ // akira:nonwrd.8.noShow.3
+ // akira:comm.wrd.people.noShow.3
+
+ // MATH
+ // "akira:0..9.by.1.asc
+ // "akira:0..9.by.1.des"
+ // akira:10..100.by.10.asc
+ // akira:100..1000.by.100.asc
+ // "akira:100..1000.by.within.asc
+
+ String suffix = tutor.tutor_id.substring("akira:".length());
+ String[] splits = suffix.split("[:\\.\\-_]");
+
+
+ StringBuilder identifyString = new StringBuilder("Identify ");
+ String str = null;
+ //System.out.println(Arrays.toString(splits));
+ switch(tutor.skill) {
+ case "numbers":
+
+ String num1 = splits[0];
+ String num2 = splits[2];
+ str = String.format(Locale.US, "numbers %d through %d", Integer.parseInt(num1), Integer.parseInt(num2));
+ break;
+
+
+ case "letters":
+
+ switch(splits[0]) {
+ case "vow":
+ if(splits[3].equals(splits[5])) {
+ str = String.format("the vowel %s", splits[3]);
+ } else {
+ str = String.format("all vowels");
+ }
+ break;
+
+ case "all":
+ if (splits[3].equals(splits[5])) {
+ str = String.format("the letter %s", splits[3]);
+ } else {
+ str = String.format("letters from A to Z");
+ }
+
+ break;
+
+ case "syl":
+ str = String.format("the syllable %s", splits[3]);
+ break;
+
+
+ case "begin":
+ str = String.format("words beginning with %s", splits[2]);
+ break;
+
+ case "end":
+ str = String.format("words ending with %s", splits[2]);
+ break;
+
+ case "comm":
+ String wordCategory = splits[2];
+ if (wordCategory.equals("body")) wordCategory = "the " + wordCategory;
+ str = String.format("common words about %s", wordCategory);
+ break;
+
+ case "HF":
+ str = String.format(Locale.US, "common %d-letter words", Integer.parseInt(splits[2]));
+ break;
+
+
+ case "nonwrd":
+ str = String.format(Locale.US, "%d-letter nonsense words", Integer.parseInt(splits[1]));
+ break;
+
+ // OPEN_SOURCE new English options...
+ // akira:wrd.a2AE √√√
+ // akira:wrd.th2TH √√√
+ // akira:wrd.dolch_preprimer √√√
+ // akira:wrd.dolch_2nd_grade √√√
+ case "wrd":
+ if (splits[1].startsWith("dolch")) {
+ str = splits[2] + " grade Dolch words";
+ } else if (splits[1].contains("2")) {
+ String[] g2p = splits[1].split("2");
+ str = " g2p mapping: " + g2p[0] + " to " + g2p[1] + "";
+ }
+
+ break;
+
+ // akira:ltr.lc_A..D_rand
+ // akira:ltr.lc_E..G_rand --> "Identify null"
+ case "ltr":
+ //str = String.format()
+ switch(splits[1]) {
+ case "lc":
+ str = "lowercase";
+ break;
+ case "uc":
+ str = "uppercase";
+ }
+ str += " letters " + splits[2] + " to " + splits[4];
+ break;
+ }
+ break;
+ }
+
+ identifyString.append(str);
+ result.add(identifyString.toString());
+
+ return result;
+ }
+
+ /**
+ * Specifically for processing Bubble Pop.
+ * @param tutor
+ * @param result
+ * @return
+ */
+ private static ArrayList processBubblePopTutorId(CAt_Data tutor, ArrayList result) {
+
+ // new cases for English Version:
+
+ // OPEN_SOURCE Bpop metadata errors
+ // letters
+
+ // change to bpop.ltr.lc:A..D.[all|vow].[asc|rand].... [show|noShow] should be in index 7
+
+ // phon words
+ // bpop.wrd:u2AH.show.mc --> Fail on first row
+ // bpop.wrd:oo2UH.show.mc
+ // change to bpop.wrd:phon.oo2UH.show.mc
+
+ // common words
+ // bpop.wrd:dolch_preprimer.show.mc
+ // bpop.wrd:dolch_preprimer.noShow.mc
+ // bpop.wrd:dolch_1st_grade.show.rise
+ result.add("Bubble Pop");
+
+
+ String suffix = tutor.tutor_id.split(":")[1];
+ String[] suffixSplit = suffix.split("[_//.//-]");
+
+
+ // global
+ String showWord;
+ // lit vars
+ String itemType, caps, wrdType, ltrOrder;
+ boolean lc = false;
+ // math vars
+ String startRange, endRange, numOrder, addSub, digits, av, orientation, mc, translate;
+ switch(tutor.tutor_desc) {
+
+ case "bpop.ltr.lc":
+ lc = true;
+ case "bpop.ltr.uc":
+ if (!lc) {
+ caps = "uppercase";
+ } else {
+ caps = "lowercase";
+ }
+
+ String ltrName;
+ if (suffixSplit[0].equals(suffixSplit[2])) {
+ ltrName = suffixSplit[0];
+ if (lc) ltrName = ltrName.toLowerCase();
+ } else {
+ ltrName = "letters"; // all letters
+ }
+ result.add(String.format("Identify the %s %s", caps, ltrName));
+
+ ltrOrder = suffixSplit[4];
+ String ltrOrderText;
+ switch(ltrOrder) {
+ case "asc":
+ ltrOrderText = "Ascending";
+ break;
+
+ case "rand":
+ ltrOrderText = "Random";
+ break;
+
+ default:
+ ltrOrderText = null;
+ }
+
+ String ltrType = suffixSplit[3];
+ String ltrTypeText;
+ switch (ltrType) {
+ case "vow":
+ ltrTypeText = "vowels";
+ break;
+
+ case "all":
+ default:
+ ltrTypeText = "A to Z";
+ break;
+ }
+
+ if (ltrOrderText != null) {
+ result.add(String.format("%s %s", ltrOrderText, ltrTypeText));
+ showWord = suffixSplit[7]; // wrong index?
+ } else {
+ // bpop.ltr.uc:A..D.asc.noShow.rise --> "null A to Z"
+ // bpop.ltr.lc:A..D.asc.show.rise --> "null A to Z"
+ result.add(String.format("Letters %s to %s", suffixSplit[0], suffixSplit[2]));
+
+ showWord = suffixSplit[4];
+ }
+
+
+ switch(showWord) {
+ case "show":
+ result.add("Audio plus visual stimuli");
+ break;
+
+ case "noShow":
+ result.add("Audio stimulus only");
+ default:
+ break;
+ }
+
+
+
+ break;
+
+ case "bpop.wrd":
+
+ // "bpop.wrd:2ch..2ch.syl.rand.all.stat.show.47__it_1",
+ // "bpop.wrd:cha..cha.syl.rand.sim.stat.show.52"
+ // "bpop.wrd:wrd.4.lc.rand.stat.show.1"
+ // "bpop.wrd:wrd.4.lc.rand.stat.noShow.2"
+ // "bpop.wrd:nonwrd.4.lc.rand.stat.show.1"
+ // "bpop.wrd:nonwrd.4.lc.rand.stat.noShow.1"
+ // "bpop.wrd:begin.wrd.ha.noShow.2"
+ // "bpop.wrd:begin.wrd.ki.noShow.2"
+ // "bpop.wrd:wrd.food.lc.rand.stat.show.1"
+ // "bpop.wrd:wrd.animals.lc.rand.stat.noShow.4"
+ // "bpop.wrd:wrd.people.lc.rand.stat.noShow.6"
+
+ showWord = null;
+
+ boolean syl = suffixSplit[3].equals("syl"); // OPEN_SOURCE e2EH.show.rise not enough
+ // check if syllable
+ if(syl) {
+ if (suffixSplit[0].substring(1).equals("ch")) {
+ String numCharsInSyllable = suffixSplit[0].substring(0, 1); // 3ch --> 3
+ result.add(String.format("Identify syllables with %s characters", numCharsInSyllable));
+ } else {
+ String syllable = suffixSplit[0];
+ result.add(String.format("Practice identifying the syllable %s", syllable));
+ }
+ showWord = suffixSplit[7];
+
+ } else {
+ switch(suffixSplit[0]) {
+ case "wrd":
+ try {
+ // can be either a number, or a category
+ int numChars = Integer.parseInt(suffixSplit[1]);
+ result.add(String.format("Identify words with %s letters", numChars));
+ } catch (NumberFormatException e) {
+ result.add(String.format("Identify words about %s", suffixSplit[1]));
+ }
+ showWord = suffixSplit[5];
+ break;
+
+ case "begin":
+ case "end":
+ String suffixOrPrefix = suffixSplit[2];
+ result.add(String.format("Identify words that %s with %s", suffixSplit[0], suffixOrPrefix));
+ showWord = suffixSplit[3];
+ break;
+
+ case "nonwrd":
+ result.add(String.format("Identify nonsense words with %s letters", suffixSplit[1]));
+ showWord = suffixSplit[5];
+ break;
+
+ case "phon":
+
+ result.add(String.format("Identify words that map grapheme %s", "GGG"));
+ result.add(String.format("to phoneme %s", "PPP"));
+ break;
+ }
+
+ //System.out.println(Arrays.toString(suffixSplit));
+
+ }
+
+
+ switch(showWord) {
+ case "show":
+ result.add("Audio plus visual stimuli");
+ break;
+
+ case "noShow":
+ result.add("Audio stimulus only");
+ default:
+ break;
+ }
+
+
+ break;
+
+ case "bpop.num":
+
+ // bpop.num:1..4.by.1.asc.q2q.AV.mc.1
+
+ startRange = suffixSplit[0];
+ endRange = suffixSplit[2];
+ String offset = suffixSplit[4];
+ numOrder = suffixSplit[5];
+ translate = suffixSplit[6];
+ av = suffixSplit[7];
+ mc = suffixSplit[8];
+
+ String translateText = null;
+ switch (translate) {
+ case "q2q":
+ translateText = "quantities to quantities";
+ break;
+
+ case "s2s":
+ translateText = "numerals to numerals";
+ break;
+
+ case "q2s":
+ translateText = "quantities to numerals";
+ break;
+
+ case "s2q":
+ translateText = "numerals to quantities";
+ break;
+
+ case "x2s":
+ translateText = "audio to numerals";
+ break;
+ }
+ result.add(String.format("Identify numbers: %s", translateText));
+
+ result.add(String.format("Range: %s to %s", startRange, endRange));
+
+ String mcText;
+ switch(mc) {
+
+ case "rise":
+ mcText = "Rising";
+ break;
+
+ case "mc":
+ default:
+ mcText = "Static";
+ break;
+ }
+ result.add(String.format("%s bubbles", mcText));
+
+ break;
+
+ case "bpop.addsub":
+
+ // bpop.addsub:0..10.
+ startRange = suffixSplit[0];
+ // empty string in [1]
+ endRange = suffixSplit[2];
+ numOrder = suffixSplit[3]; // incr, decr, rand
+ String orderTxt;
+
+ addSub = suffixSplit[4].equals("SUB") ? "subtraction" : "addition";
+ digits = suffixSplit[5]; // [123]D or number
+
+ orientation = suffixSplit[6].equals("V") ? "Vertical" : "Horizontal";
+
+ result.add(String.format("%s %s", orientation, addSub));
+
+ boolean incr = false;
+ switch(numOrder) {
+
+ case "incr":
+ incr = true;
+ case "decr":
+ orderTxt = numOrder + "ementing";
+ String start = incr ? startRange : endRange;
+ String end = incr ? endRange : startRange;
+ result.add(String.format("Numbers %s from %s to %s, by %s",
+ orderTxt, start, end, digits));
+ break;
+
+ case "rand":
+ orderTxt = "random";
+ digits = digits.substring(0, digits.indexOf("D"));
+ result.add(String.format("Random %s-digit numbers between %s and %s",
+ digits, startRange, endRange));
+ break;
+ }
+
+
+ break;
+
+ case "bpop.gl":
+
+ // bpop.gl:num.10..99.GL_DD_OFFw10_L
+ translate = suffixSplit[0];
+ String representationText = null;
+ switch(translate) {
+ case "num":
+ representationText = "number";
+ break;
+
+ case "dot":
+ representationText = "quantity";
+ break;
+ }
+
+ startRange = suffixSplit[1];
+ // empty string in [1]
+ endRange = suffixSplit[3];
+
+ String GL = suffixSplit[7];
+ String GLtext = null;
+ switch(GL) {
+ case "M":
+ GLtext = "greater";
+ break;
+
+ case "L":
+ GLtext = "lesser";
+ break;
+ }
+
+ // System.out.println(Arrays.toString(suffixSplit));
+ result.add(String.format("Which %s is %s?", representationText, GLtext));
+ result.add(String.format("Numbers between %s and %s", startRange, endRange));
+
+ break;
+ case "bpop.mn":
+
+ // bpop.mn:10..99.MN-DD-UP-OFF1-BL1
+ startRange = suffixSplit[0];
+ // empty string in [1]
+ endRange = suffixSplit[2];
+
+ // System.out.println(Arrays.toString(suffixSplit));
+ result.add("Select the missing number");
+ result.add(String.format("Numbers between %s and %s", startRange, endRange));
+
+ break;
+ }
+
+ return result;
+ }
+}
diff --git a/util/src/main/java/cmu/xprize/util/FailureInterventionHelper.java b/util/src/main/java/cmu/xprize/util/FailureInterventionHelper.java
new file mode 100644
index 0000000..2659f01
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/FailureInterventionHelper.java
@@ -0,0 +1,77 @@
+package cmu.xprize.util;
+
+import android.content.Intent;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+
+import static cmu.xprize.util.consts.INTERVENTION_CONST.BROADCAST_FAILURE_UPDATE;
+import static cmu.xprize.util.consts.INTERVENTION_CONST.FAILS_HAPPENED;
+import static cmu.xprize.util.consts.INTERVENTION_CONST.FAILS_NEEDED;
+
+/**
+ * FailureInterventionHelper
+ *
+ * How to add FailureInterventionHelper to a tutor
+ * 1. declare a `protected` Helper in a CComponent for a Tutor
+ * 2. construct the Helper in the TComponent class, after `dataSource` has been loaded via loadJSON (usually setDataSource)
+ * 3. look in `trackAndLogPerformance`, and call `shouldTriggerIntervention` when the student gets a question incorrect
+ *
+ * Created by kevindeland on 9/20/19.
+ */
+
+public class FailureInterventionHelper {
+
+ public enum Tutor {BPOP, AKIRA, SPELL, WRITE, PICMATCH, NUMCOMPARE}
+
+ private Tutor _tutorType;
+ private int _dataSize;
+
+ public FailureInterventionHelper(Tutor _tutorType, int _dataSize) {
+ this._tutorType = _tutorType;
+ this._dataSize = _dataSize;
+ }
+
+ /**
+ * Returns whether a failure intervention should be triggered, based on the number of wrong
+ * attempts that have been committed.
+ * FAILSON Note that this should be changed to be dependent on the passing logic for each tutor,
+ * FAILSON and the total number of problems
+ *
+ * @param anyWrongAttempts how many the student has gotten wrong in total
+ * @return TRUE if an intervention should be triggered
+ */
+ public boolean shouldTriggerIntervention(int anyWrongAttempts) {
+
+ switch(_tutorType) {
+ case BPOP:
+ return anyWrongAttempts == 9;
+
+ case AKIRA:
+ return anyWrongAttempts == TCONST.FAILURE_COUNT_AKIRA;
+
+ case SPELL:
+ return anyWrongAttempts == 9;
+
+ case WRITE:
+ return anyWrongAttempts == 9;
+
+ case PICMATCH:
+ return anyWrongAttempts == 9;
+
+ case NUMCOMPARE:
+ return anyWrongAttempts == 9;
+
+ default:
+ Log.wtf("FailureInterventionHelper", "_tutorType not found: " + _tutorType);
+ return false;
+ }
+ }
+
+ public void sendBroadcastUpdate(LocalBroadcastManager manager, int numWrong) {
+ Intent failureIntent = new Intent(BROADCAST_FAILURE_UPDATE);
+ failureIntent.putExtra(FAILS_HAPPENED, numWrong);
+ failureIntent.putExtra(FAILS_NEEDED, 9);
+ manager.sendBroadcast(failureIntent);
+
+ }
+}
diff --git a/util/src/main/java/cmu/xprize/util/GlobalStaticsEngine.java b/util/src/main/java/cmu/xprize/util/GlobalStaticsEngine.java
new file mode 100644
index 0000000..10dad67
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/GlobalStaticsEngine.java
@@ -0,0 +1,51 @@
+package cmu.xprize.util;
+
+/**
+ * GlobalStaticsEngine
+ * CTutorEngine and RoboTutor hold some important variables that are impossible for
+ * classes to access if they are lower on the dependency tree hierarchy. This class is for holding
+ * variables that need to be accessed by those classes.
+ * Created by kevindeland on 2019-10-27.
+ */
+public class GlobalStaticsEngine {
+
+ private static String currentTutorId;
+
+ private static String currentDomain;
+
+ private static int currentLevel;
+
+ private static String currentTutorType;
+
+ public static String getCurrentTutorId() {
+ return currentTutorId;
+ }
+
+ public static void setCurrentTutorId(String currentTutorId) {
+ GlobalStaticsEngine.currentTutorId = currentTutorId;
+ }
+
+ public static String getCurrentDomain() {
+ return currentDomain;
+ }
+
+ public static void setCurrentDomain(String currentDomain) {
+ GlobalStaticsEngine.currentDomain = currentDomain;
+ }
+
+ public static int getCurrentLevel() {
+ return currentLevel;
+ }
+
+ public static void setCurrentLevel(int currentLevel) {
+ GlobalStaticsEngine.currentLevel = currentLevel;
+ }
+
+ public static String getCurrentTutorType() {
+ return currentTutorType;
+ }
+
+ public static void setCurrentTutorType(String currentTutorType) {
+ GlobalStaticsEngine.currentTutorType = currentTutorType;
+ }
+}
diff --git a/util/src/main/java/cmu/xprize/util/IButtonController.java b/util/src/main/java/cmu/xprize/util/IButtonController.java
new file mode 100644
index 0000000..2ab6883
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/IButtonController.java
@@ -0,0 +1,12 @@
+package cmu.xprize.util;
+
+public interface IButtonController {
+
+ public void doDebugLaunchAction(String debugTutor);
+ void doDebugTagLaunchAction(String tag);
+
+ public void doButtonBehavior(String buttonid);
+ public void doAskButtonAction(String actionid);
+
+ public void doLaunch(String intent, String intentData, String dataSource, String tutorId, String matrix); // WARRIOR_MAN
+}
diff --git a/util/src/main/java/cmu/xprize/util/IEvent.java b/util/src/main/java/cmu/xprize/util/IEvent.java
index 96468ea..0ed5488 100644
--- a/util/src/main/java/cmu/xprize/util/IEvent.java
+++ b/util/src/main/java/cmu/xprize/util/IEvent.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,6 +19,7 @@
package cmu.xprize.util;
public interface IEvent {
+
public String getType();
public Object getString(String key);
}
diff --git a/util/src/main/java/cmu/xprize/util/IEventDispatcher.java b/util/src/main/java/cmu/xprize/util/IEventDispatcher.java
index 22d2593..7c1f8ff 100644
--- a/util/src/main/java/cmu/xprize/util/IEventDispatcher.java
+++ b/util/src/main/java/cmu/xprize/util/IEventDispatcher.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -24,9 +23,11 @@
public interface IEventDispatcher {
- public List mListeners = new ArrayList();
+ public boolean isGraphEventSource();
+
+ public void addEventListener(String listener);
+ public void addEventListener(IEventListener listener);
- public void addEventListener(String linkedView);
public void dispatchEvent(IEvent event);
}
diff --git a/util/src/main/java/cmu/xprize/util/IEventListener.java b/util/src/main/java/cmu/xprize/util/IEventListener.java
index f8f8d06..0c4318c 100644
--- a/util/src/main/java/cmu/xprize/util/IEventListener.java
+++ b/util/src/main/java/cmu/xprize/util/IEventListener.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/util/src/main/java/cmu/xprize/util/IInterventionSource.java b/util/src/main/java/cmu/xprize/util/IInterventionSource.java
new file mode 100644
index 0000000..580bd6d
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/IInterventionSource.java
@@ -0,0 +1,12 @@
+package cmu.xprize.util;
+
+/**
+ * RoboTutor
+ *
+ * Created by kevindeland on 6/19/19.
+ */
+
+public interface IInterventionSource {
+
+ void triggerIntervention(String type);
+}
diff --git a/util/src/main/java/cmu/xprize/util/ILogManager.java b/util/src/main/java/cmu/xprize/util/ILogManager.java
deleted file mode 100644
index 5801516..0000000
--- a/util/src/main/java/cmu/xprize/util/ILogManager.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package cmu.xprize.util;
-
-public interface ILogManager {
-
- public void startLogging(String logPath);
- public void stopLogging();
-
- public void postSystemEvent(String Tag, String Msg);
- public void postSystemTimeStamp(String Tag);
-
- public void postEvent(String Tag, String Msg);
- public void postTimeStamp(String Tag);
-
- public void post(String command);
-
- public void postError(String Tag, String Msg);
- public void postError(String Tag, String Msg, Exception e);
-
- public void postPacket(String packet);
-}
diff --git a/util/src/main/java/cmu/xprize/util/IMessageQueueRunner.java b/util/src/main/java/cmu/xprize/util/IMessageQueueRunner.java
new file mode 100644
index 0000000..b0700ab
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/IMessageQueueRunner.java
@@ -0,0 +1,16 @@
+package cmu.xprize.util;
+
+/**
+ * RoboTutor
+ *
+ * Created by kevindeland on 8/27/19.
+ */
+
+public interface IMessageQueueRunner {
+
+ void runCommand(String command);
+
+ void runCommand(String command, Object target);
+
+ void runCommand(String command, String target);
+}
diff --git a/util/src/main/java/cmu/xprize/util/IPerformanceTracker.java b/util/src/main/java/cmu/xprize/util/IPerformanceTracker.java
new file mode 100644
index 0000000..b695d23
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/IPerformanceTracker.java
@@ -0,0 +1,14 @@
+package cmu.xprize.util;
+
+/**
+ * IPerformanceTracker
+ *
+ *
currently only implemented by BigMath. Would ideally work for others as well.
+ *
+ * Created by kevindeland on 10/7/18.
+ */
+
+public interface IPerformanceTracker {
+
+ void trackAndLogPerformance(boolean correct, Object expected, Object actual);
+}
diff --git a/util/src/main/java/cmu/xprize/util/IPublisher.java b/util/src/main/java/cmu/xprize/util/IPublisher.java
index 0acbbdd..d99c92c 100644
--- a/util/src/main/java/cmu/xprize/util/IPublisher.java
+++ b/util/src/main/java/cmu/xprize/util/IPublisher.java
@@ -1,5 +1,7 @@
package cmu.xprize.util;
+import java.util.HashMap;
+
/**
* Created by kevin on 10/27/2016.
*/
@@ -12,8 +14,16 @@ public interface IPublisher {
public void publishValue(String varName, int value);
+ public void publishFeatureSet(String featureset);
+
+ public void retractFeatureSet(String featureset);
+
public void publishFeature(String feature);
public void retractFeature(String feature);
+ public void publishFeatureMap(HashMap featureMap);
+
+ public void retractFeatureMap(HashMap featureMap);
+
}
diff --git a/util/src/main/java/cmu/xprize/util/IScriptable.java b/util/src/main/java/cmu/xprize/util/IScriptable.java
index 72e1105..ba1c171 100644
--- a/util/src/main/java/cmu/xprize/util/IScriptable.java
+++ b/util/src/main/java/cmu/xprize/util/IScriptable.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -34,6 +33,7 @@ public interface IScriptable {
public Object evaluate(boolean neg);
public void preEnter();
+ public boolean testFeatures();
public String applyNode();
}
diff --git a/util/src/main/java/cmu/xprize/util/ImageLoader.java b/util/src/main/java/cmu/xprize/util/ImageLoader.java
new file mode 100644
index 0000000..f9a93aa
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/ImageLoader.java
@@ -0,0 +1,81 @@
+package cmu.xprize.util;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.widget.ImageView;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+
+public class ImageLoader {
+
+ public static RequestLoader with(Context context) {
+ return new RequestLoader(context);
+ }
+
+ static class RequestLoader {
+ private Context context;
+ private int imageResource;
+
+ RequestLoader(@NonNull Context context) {
+ this.context = context;
+ }
+
+ /**
+ * Load a Drawable into the RequestLoader
+ *
+ * @param imagePath
+ * @return
+ */
+ public RequestLoader loadDrawable(String imagePath) {
+ this.imageResource = context.getResources().getIdentifier(imagePath, "drawable", context.getPackageName());
+ return this;
+ }
+
+
+
+ public void into(ImageView imageView) {
+
+ Drawable image = ContextCompat.getDrawable(context, imageResource);
+ imageView.setImageDrawable(image);
+
+ }
+ }
+
+ public static BitmapLoader makeBitmapLoader(String path) { return new BitmapLoader(path);}
+
+ // NEW_THUMBS (3) copy this BitmapLoader. Use it to load images.
+ public static class BitmapLoader {
+
+ private Bitmap bitmapResource;
+ private String _path;
+
+ BitmapLoader(String path) {
+ this._path = path;
+ }
+
+ /**
+ * Load a Bitmap
+ * @param imageName
+ * @return
+ */
+ public BitmapLoader loadBitmap(String imageName) throws FileNotFoundException {
+ String fullPath = _path + imageName;
+
+ InputStream in = new FileInputStream(_path + imageName);
+ bitmapResource = BitmapFactory.decodeStream(in);
+
+ return this;
+ }
+
+ public void into(ImageView imageView) {
+ imageView.setImageBitmap(this.bitmapResource);
+ }
+ }
+}
diff --git a/util/src/main/java/cmu/xprize/util/JSON_Helper.java b/util/src/main/java/cmu/xprize/util/JSON_Helper.java
index ff1f413..0c0bea2 100644
--- a/util/src/main/java/cmu/xprize/util/JSON_Helper.java
+++ b/util/src/main/java/cmu/xprize/util/JSON_Helper.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2021 RoboTutorLLC All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -38,6 +37,8 @@
import java.util.HashMap;
import java.util.Iterator;
+import cmu.xprize.comp_logging.CErrorManager;
+
/**
* Global helper to cache spec files from the tutor assets folder
@@ -77,6 +78,74 @@ static public String cacheDataByName(String fileName) {
return cacheData(fileName, TCONST.DEFINED);
}
+ static public String createValueAcronym(String jsonData) {
+ /*
+ @params: jsonData - A serialised jsonString, e.g.
+ {
+ "config_version": "ftttfNULLfCD2", → compute instead
+ "language_override": false,
+ "show_tutorversion": true,
+ "show_debug_launcher": true,
+ "language_switcher": true,
+ "no_asr_apps": false,
+ "language_feature_id": "LANG_NULL", → NULL | EN | SW
+ "show_demo_vids": false,
+ "menu_type": "CD2" → 1 or 2 in acronym
+
+ }
+
+ The config_version will be replaced with a custom acronym, which is generated below.
+
+ */
+ String outputAcronym = new String();
+ try {
+ JSONObject jsonObject = new JSONObject(jsonData);
+ JSONArray keys = jsonObject.names();
+
+ for (int i=0; i elemClass = fieldClass.getComponentType();
-
- Object field_Array = Array.newInstance(elemClass, nArr.length());
+ try {
+ nArr = jsonObj.getJSONArray(fieldName);
+ Class> elemClass = fieldClass.getComponentType();
+ Object field_Array = Array.newInstance(elemClass, nArr.length());
+ field.set(self, parseArray(jsonObj, self, classMap, scope, nArr, elemClass, field_Array));
+ } catch(Exception e){
+ JSONArray emptyArray = new JSONArray();
+ Object field_Array = Array.newInstance(String.class, 0);
+ field.set(self, parseArray(jsonObj, self, classMap, scope, emptyArray, String.class, field_Array));
+ }
- field.set(self, parseArray(jsonObj, self, classMap, scope, nArr, elemClass, field_Array));
}
// otherwise assume it is a discrete object of ILoadable type
@@ -455,7 +537,9 @@ else if (fieldClass.equals(HashMap.class)) {
}
} catch (Exception e) {
+
CErrorManager.logEvent(TAG, "ERROR: parseSelf:", e, true);
+
}
}
}
@@ -541,7 +625,7 @@ static Object parseArray(JSONObject jsonObj, Object self, HashMap
}
}
catch(Exception e) {
- CErrorManager.logEvent(TAG, "Json Array Format Error: ", e, false);
+ CErrorManager.logEvent(TAG, "Json Array Format Error: ", e, true);
}
return field_Array;
diff --git a/util/src/main/java/cmu/xprize/util/MathUtil.java b/util/src/main/java/cmu/xprize/util/MathUtil.java
new file mode 100644
index 0000000..da9a6f2
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/MathUtil.java
@@ -0,0 +1,42 @@
+package cmu.xprize.util;
+
+/**
+ * RoboTutor
+ *
+ * Created by kevindeland on 7/17/18.
+ */
+
+public class MathUtil {
+
+
+ /**
+ * Get hundreds digit of a 3-digit number.
+ *
+ * @param numberValue must be 3 digits or less.
+ * @return hundreds digit
+ */
+ public static int getHunsDigit(int numberValue) {
+ return numberValue / 100;
+ }
+
+ /**
+ * Get tens digit of a number.
+ *
+ * @param numberValue must be positive.
+ * @return tens digit
+ */
+ public static int getTensDigit(int numberValue) {
+ return (numberValue / 10) % 10;
+ }
+
+ /**
+ * Get ones digit of a number.
+ *
+ * @param numberValue must be positive.
+ * @return ones digit
+ */
+ public static int getOnesDigit(int numberValue) {
+ return numberValue % 10;
+ }
+
+}
diff --git a/util/src/main/java/cmu/xprize/util/QueueConstructorVars.txt b/util/src/main/java/cmu/xprize/util/QueueConstructorVars.txt
new file mode 100644
index 0000000..fe43094
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/QueueConstructorVars.txt
@@ -0,0 +1,21 @@
+CMessageQueueFactory,CBP_Component,CNumberScale_Component,CCountX_Component,
+CAk_Component,CNd_Component,CBigMathComponent
+ String:command
+ String:name,String:command
+ String:command,Object:target***
+
+/* Anything can be added to this */
+CPicMatch_Component,CSpellingComponent
+ null
+
+CWritingComponent**
+ String:name,String:command
+ String:name,String:command,String:target
+
+
+/* Might just keep these separate */
+CQn_Component,CRt_Component****
+ String:command
+ String:command,Object:target
+
+****These do NOT have the (name, command) constructor, don't have nameMap
diff --git a/util/src/main/java/cmu/xprize/util/TCJSGF.java b/util/src/main/java/cmu/xprize/util/TCJSGF.java
index 2c89687..07bfa0a 100644
--- a/util/src/main/java/cmu/xprize/util/TCJSGF.java
+++ b/util/src/main/java/cmu/xprize/util/TCJSGF.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/util/src/main/java/cmu/xprize/util/TCONST.java b/util/src/main/java/cmu/xprize/util/TCONST.java
index 0f71d78..efaa884 100644
--- a/util/src/main/java/cmu/xprize/util/TCONST.java
+++ b/util/src/main/java/cmu/xprize/util/TCONST.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -21,27 +20,36 @@
// global tutor constants
-import android.content.Context;
+import android.os.Environment;
-import java.io.File;
import java.util.HashMap;
public class TCONST {
-// sdcard/robotutor_assets/assets/audio/en/cmu/xprize/activity_selector/d39950ec96e6a5361508996ce7ae6444.mp3
-
// These features are based on the current tutor selection model
// When no tutor has been selected it should run the tutor select
// and when it finishes it should run the difficulty select until
// the user wants to select another tutor.
//
- public static final String FTR_TUTOR_SELECT = "FTR_TUTOR_SELECT";
- public static final String FTR_DIFFICULTY_SELECT = "FTR_DIFFICULTY_SELECT";
+ public static final String FTR_TUTOR_SELECT = "FTR_TUTOR_SELECT"; // these are never read anymore, but are still used as features
+ public static final String FTR_DEBUG_SELECT = "FTR_DEBUG_SELECT";
+ public static final String FTR_DEBUG_LAUNCH = "FTR_DEBUG_LAUNCH";
+
+ public static final String FTR_GOODBYE = "GOODBYE";
+ public static final int NUM_GOODBYE_SOUND_CLIPS = 9; // make sure this matches the number of sound clips in
+ // the "EXIT_BUTTON_BEHAVIOR" object in activity_selector/animator_graph.json
+
+ public static final String SKILL_WRITING = "letters";
+ public static final String SKILL_STORIES = "stories";
+ public static final String SKILL_MATH = "numbers";
- // RoboTutor Version spec index meaning 0.1.2.3
- // Given 4.23.2.3
- // Major release 4 | Feature release 23 | Fix release 2 | compatible Asset Version 3
+ public static final String FINISH = "FINISH";
+
+
+ // RoboTutor Version spec Index meaning 0.1.2.3
+ // Given 4.23.9.8
+ // Major release 4 | Feature release 23 | Fix release 9 | compatible Asset Version 8
//
public static final int MAJOR_VERSION = 0;
public static final int FEATURE_RELEASE = 1;
@@ -62,18 +70,23 @@ public class TCONST {
// They will arrive in files named - RoboTutor_AssetA.0.1.0.zip
//
- public static final String ROBOTUTOR_ASSET_PATTERN = "RTAsset_";
+ public static final String ROBOTUTOR_ASSET_PATTERN = "rtasset_";
+ public static final String CODE_DROP_1_ASSET_PATTERN = "codedrop1_";
+ public static final String CODE_DROP_2_ASSET_PATTERN = "codedrop2_";
+ public static final String PROTOTYPE_ASSET_PATTERN = "protoassets_";
+ public static final String QA_ASSET_PATTERN = "qa_assets";
+ public static final String ENGLISH_ASSET_PATTERN = "English_";
+ public static final String SWAHILI_ASSET_PATTERN = "Swahili_";
public static final String COMMAND = "COMMAND";
public static final String MODULE = "MODULE";
+ public static final String QUEUE = "QUEUE";
public static final String NODE = "NODE";
public static final String CONDITION = "CONDITION";
public static final String NUMDATA_HEADER = "{\n" + "\"dataSource\": ";
public static final boolean ADD_FEATURE = true;
public static final boolean DEL_FEATURE = false;
- public static final int GUID_LEN = 5;
- public static final String GUID_UPDATE = "GUIDUPDATE";
public static final String FTR_PLACE_ = "FTR_PLACE_";
public static final String _USED = "_USED";
@@ -85,7 +98,7 @@ public class TCONST {
public static final int MAX_DIGITS = 4;
public static final String NO_DATASOURCE = "";
public static final String DATA_PREFIX = "DATA_";
- public static final String DATA_PATH = "data";
+ public static final String DATA_PATH = "data";
public static final String FW_PREPLISTENER = "FW_PREPLISTENER";
public static final String FW_TTS = "FW_TTS";
@@ -95,12 +108,34 @@ public class TCONST {
public static final String SAY_STIMULUS = "FTR_SAY";
public static final String SHOW_STIMULUS = "FTR_SHOW";
+
+ public static final String ASM_DIGIT_OR_OVERHEAD_CORRECT = "ASM_DIGIT_OR_OVERHEAD_CORRECT";
+ public static final String ASM_DIGIT_OR_OVERHEAD_WRONG = "ASM_DIGIT_OR_OVERHEAD_WRONG";
+ public static final String ASM_CLICK_ON_DOT = "ASM_CLICK_ON_DOT";
+ public static final String ASM_ALL_DOTS_DOWN = "ASM_ALL_DOTS_DOWN";
+
+ public static final String ASM_ADD = "ASM_ADD";
+ public static final String ASM_ADD_PROMPT = "ASM_ADD_PROMPT";
+ public static final String ASM_ADD_PROMPT_COUNT_FROM = "ASM_ADD_PROMPT_COUNT_FROM";
+
+ public static final String ASM_SUB = "ASM_SUB";
+ public static final String ASM_SUB_PROMPT = "ASM_SUB_PROMPT";
+
+ public static final String ASM_MULTI = "ASM_MULTI";
+ public static final String ASM_MULTI_PROMPT = "ASM_MULTI_PROMPT";
+ public static final String ASM_RA_START = "ASM_RA_START";
+ public static final String ASM_NEXT_NUMBER = "ASM_NEXT_NUMBER";
+ public static final String ASM_NEXT_RESULT = "ASM_NEXT_RESULT";
+ public static final String ASM_RESULT_FIRST_TWO = "ASM_RESULT_FIRST_TWO";
+ public static final String ASM_RESULT_NEXT_OR_LAST = "ASM_RESULT_NEXT_OR_LAST";
+ public static final String ASM_REPEATED_ADD_DOWN = "ASM_REPEATED_ADD_DOWN";
+
public static final String TYPE_CTUTOR = "CTutor";
public static final String TYPE_CSCENEGRAPH = "CSceneGraph";
public static final String TYPE_CTUTORGRAPH = "CTutorGraph";
public static final String EVENT_SCENEQUEUE = "Scene Queue Event";
public static final String EVENT_TUTORGRAPH = "Tutor Graph Event";
- public static final String AUDIO_REF = "audio_ref";
+// public static final String AUDIO_REF = "audio_ref";
public static final String SET_BANNER_COLOR = "SET_BANNER_COLOR";
public static final String LAST_ATTEMPT = "FTR_LASTATTEMPT";
@@ -146,8 +181,6 @@ public class TCONST {
public static final String INVISIBLE = "INVISIBLE";
public static final String GONE = "GONE";
- public static final String ENGINEMESSAGE = "EngineMessage: ";
-
public static final String ASK_SELECTION = "ASK_SELECTION";
public static final String ASK_BUTTON_ID = "ASK_BUTTON_ID";
public static final String CANCEL_POINTAT = "CANCEL_POINTAT";
@@ -157,13 +190,114 @@ public class TCONST {
public static final String RIGHTLANE = "RIGHT";
public static final String STORY_INTENT = "story_reading";
+ public static final String QUESTIONS_INTENT = "story_questions";
public static final String ON_CLICK = "ON_CLICK";
-
- public static final String SKILL_WRITING = "letters";
- public static final String SKILL_READING = "stories";
- public static final String SKILL_MATH = "numbers";
- public static final String SKILL_SHAPES = "shapes";
- public static final String SKILL_UNSET = "SKILL_UNSET";
+ public static final String ENCODED_FOLDER = "[encfolder]";
+ public static final String SHARED_MATH = "[sharedmath]";
+ public static final String SHARED_MATH_FOLDER = "shared/shared_math";
+ public static final String SHARED_LITERACY = "[sharedliteracy]";
+ public static final String SHARED_LITERACY_AUDIO_FOLDER = "shared/shared_lit"; // TODO fix this abominable inconsistency
+ public static final String SHARED_LITERACY_IMAGE_FOLDER = "shared/shared_literacy";
+ public static final String SONG = "[song]";
+ public static final String SONG_FOLDER = "?";
+ public static final String STORY_PATH = "cmu/xprize/story_reading/";
+ public static final String WORD_PROBLEMS = "[wordproblems]";
+ public static final String PUNC_STORY = "[punc]";
+
+ public static final String LOCAL_FILE = "[local_file]";
+ public static final String DOWNLOAD_PATH = "/sdcard/Download";
+ public static final String DOWNLOAD_RT_PATH = "/sdcard/Download/RoboTutor";
+ public static final String DOWNLOAD_RT_TUTOR = "/sdcard/Download/RoboTutor/assets";
+
+ public static final String DEBUG_FILE_PREFIX = "[debug_file]";
+ public static final String DEBUG_RT_PATH = "/sdcard/robo_debug";
+
+ public static final String ARITHMETIC_DATA = "tutors/add_subtract";
+ public static final String AKIRA_DATA = "tutors/akira";
+ public static final String BUBBLEPOP_DATA = "tutors/bubble_pop";
+ public static final String STORY_DATA = "tutors/story_reading";
+ public static final String WRITING_DATA = "tutors/word_copy";
+
+ public static final String FTR_USER_HEAR = "FTR_USER_HEAR";
+ public static final String FTR_USER_READ = "FTR_USER_READ";
+ public static final String FTR_USER_ECHO = "FTR_USER_ECHO";
+ public static final String FTR_USER_HIDE = "FTR_USER_HIDE";
+ public static final String FTR_USER_REVEAL = "FTR_USER_REVEAL";
+ public static final String FTR_USER_PARROT = "FTR_USER_PARROT";
+ public static final String FTR_USER_READING = "FTR_USER_READING";
+ // UHQ
+ public static final String FTR_GEN = "FTR_GEN";
+ public static final String FTR_PIC = "FTR_PIC";
+ public static final String FTR_CLO = "FTR_CLO";
+ public static final String STOP_AUDIO = "STOP_AUDIO";
+ public static final String RTC_VAR_CLOZEWORD = ".clozeWord";
+ public static final String REMOVE_CLOZE_FROM_BLANK = "REMOVE_CLOZE_FROM_BLANK";
+
+
+ public static final String NARRATE_STORY = "NARRATE_STORY";
+ public static final String TRACK_NARRATION = "TRACK_NARRATION";
+ public static final String START_NARRATION = "START_NARRATION";
+ public static final String SPEAK_UTTERANCE = "SPEAK_UTTERANCE";
+
+ public static final String SPEAK_EVENT = "SPEAK_EVENT";
+
+ public static final int INITSPLIT = -1;
+ public static final String SENTENCE_SPACE = " ";
+ public static final String WORD_SPACE = " ";
+ public static final String NO_SPACE = "";
+ public static final int MAX_AKDATA = 10;
+
+ public static final String FTR_COMPLETE = "FTR_COMPLETE";
+ public static final String FTR_PROMPT = "FTR_PROMPT";
+ public static final String FTR_PAGE_PROMPT = "FTR_PAGE_PROMPT";
+
+ public static final String START_PROGRESSIVE_UPDATE = "START_PROGRESSIVE_UPDATE";
+ public static final String START_INDETERMINATE_UPDATE = "START_INDETERMINATE_UPDATE";
+ public static final String UPDATE_PROGRESS = "UPDATE_PROGRESS";
+ public static final String PROGRESS_TITLE = "PROGRESS_TITLE";
+ public static final String PROGRESS_MSG1 = "PROGRESS_MSG1";
+ public static final String PROGRESS_MSG2 = "PROGRESS_MSG2";
+ public static final String ASSET_UPDATE_MSG = "Installing Assets: ";
+ public static final String INT_FIELD = "INT_FIELD";
+ public static final String PLEASE_WAIT = " - Please Wait.";
+
+ public static final String TUTOR_NATIVE = "native";
+ public static final String TRACK_COMPLETE = "TRACK_COMPLETE";
+
+ public static final String NEXT_PAGE = "NEXT_PAGE";
+ public static final String CLOZE_CORRECT = "CLOZE_CORRECT";
+ public static final String CLOZE_WRONG = "CLOZE_WRONG";
+ public static final String PICMATCH_CORRECT = "PICMATCH_CORRECT";
+ public static final String PICMATCH_WRONG = "PICMATCH_WRONG";
+ public static final String NEXT_SCENE = "NEXT_SCENE";
+ public static final String NEXT_WORD = "NEXT_WORD";
+
+
+ // Core log message types - anumation scenegraph and queued scenegraph
+ //
+ public static final String TUTOR_STATE_MSG = "TSTag";
+ public static final String GRAPH_MSG = "RTag";
+ public static final String QGRAPH_MSG = "RQTag";
+ public static final String LTKPLUS_MSG = "RLTag";
+ public static final String LOGSTATE = "logState";
+ public static final String BATTERY_MSG = "Battery";
+
+ public static final String AUDIO_EVENT = "AUDIO_EVENT";
+ public static final String TYPE_AUDIO = "type_audio";
+ public static final String TRACK_SEGMENT = "TRACK_SEGMENT";
+
+ // for logging and tracking student performance
+ public static final String PERFORMANCE_TAG = "PERFORMANCE_TAG";
+ public static final boolean CONSIDER_STUDENT_PERFORMANCE = true;
+ public static final boolean OVERRIDE_SELF_ASSESSMENT = false;
+ public static final double HIGH_PERFORMANCE_THRESHOLD = 0.9; // percent to be upgraded a level
+ public static final double MID_PERFORMANCE_THRESHOLD = 0.5; // percent to pass
+ public static final int MIN_ATTEMPTS_TO_GRADE = 5; // minimum number of attempts to be graded
+
+ public static final String DEFAULT_STUDENT_ID = "DEBUG";
+ public static final String STUDENT_ID_VAR = "studentId";
+ public static final String SESSION_ID_VAR = "sessionId";
+ public static final String LAST_TUTOR = "LAST_TUTOR_PLAYED";
static public HashMap colorMap = new HashMap();
@@ -187,6 +321,8 @@ public class TCONST {
public static final String COLORNORMAL = "normal";
public static final String COLORNONE = "none";
+ public static final int STROKE_STIM_UNDERLINE = 5;
+
static public HashMap fontMap = new HashMap();
@@ -217,6 +353,7 @@ public class TCONST {
public static final String LANG_EFFECT = "LANG_EFFECT";
public static final String LANG_EN = "LANG_EN";
public static final String LANG_SW = "LANG_SW";
+ public static final String MEDIA_STORY = "story";
// This maps features to 2 letter codes used to build filepaths.
static {
@@ -233,9 +370,10 @@ public class TCONST {
static final public String BASE_ASSETS = "assets";
public static final String STORY_ASSETS = "story";
+ public static final String ICON_ASSETS = "icons";
static final public String EXTERNAL = "external";
static final public String ROBOTUTOR_ASSETS = "sdcard/robotutor_assets/assets";
-
+ static final public String LOCAL_STORY_AUDIO = "sdcard/Download/RoboTutor/assets/story_questions";
static final public String LTK_PROJECT_ASSETS = "projects";
static final public String LTK_GLYPH_ASSETS = "glyphs";
@@ -255,6 +393,7 @@ public class TCONST {
static final public String DEFAULT = "default";
// CTutorNavigator Constants
+ // TODO this is so annoying... different objects use the same ENDTUTOR var
public static final String ENDTUTOR = "END_TUTOR"; // Terminate a tutor from within
public static final String KILLTUTOR = "KILL_TUTOR"; // Kill a tutor exteranlly
public static final String CONTINUETUTOR = "CONTINUE_TUTOR";
@@ -269,8 +408,13 @@ public class TCONST {
public static final String ASSETS = "ASSETS";
public static final String RESOURCES = "RESOURCE";
public static final String EXTERN = "EXTERN";
+ public static final String EXTERN_SHARED = "EXTERN_SHARED";
public static final String DEFINED = "DEFINED";
+ public static final String DEBUG_STORY_TAG = "GENERAL_TSO";
+ public static final String DEBUG_HESITATE = "MATH_HESITATE";
+ public static final String DEBUG_MENU = "DEBUG_MENU";
+
// Navigator types
final static public String SIMPLENAV = "SIMPLE_NAVIGATOR";
final static public String GRAPHNAV = "GRAPH_NAVIGATOR";
@@ -282,7 +426,6 @@ public class TCONST {
public static final String REC_GLYPH = "REC_GLYPH";
-
// CActionTrack track types
// Note these must case-match the layer names in the Flash
// timeline specification from which CActionTrack is derived
@@ -310,6 +453,11 @@ public class TCONST {
public static final String READY = "READY";
public static final String PLAY = "PLAY";
+ //UHQ
+ public static final String PLAY_CLOZE = "PLAY_CLOZE";
+ public static final String CLZ_ANIM_INCOMPLETE = "CLZ_ANIM_INCOMPLETE";
+ public static final String CLZ_ANIM_COMPLETE = "CLZ_ANIM_COMPLETE";
+ public static long CLOZE_END = 0L;
public static final String STOP = "STOP";
public static final String NEXT = "NEXT";
public static final String GOTO_NODE = "GOTO_NODE";
@@ -323,6 +471,7 @@ public class TCONST {
public static final String ENTER_SCENE = "ENTER_SCENE";
public static final String END_OF_GRAPH = "END_OF_GRAPH";
+ public static final String APPLY_NODE = "APPLY_NODE";
public static final String APPLY_BEHAVIOR = "APPLY_BEHAVIOR";
@@ -365,8 +514,6 @@ public class TCONST {
public static final String CMD_GOTO = "GOTONODE";
public static final String CMD_NEXT = "NEXT";
public static final String CMD_LAUNCH = "LAUNCH-TUTOR";
- public static final String CMD_SET_FEATURE = "FEATURE-ADD";
- public static final String CMD_DEL_FEATURE = "FEATURE-DEL";
// Intrinsic types
@@ -384,6 +531,58 @@ public class TCONST {
public static final String STARE_START = "STARE_START";
public static final String STARE_STOP = "STARE_STOP";
+
+ // Broadcasts for Intervention
+ public static final String I_TRIGGER_GESTURE = "GESTURE";
+ public static final String I_TRIGGER_STUCK = "STUCK";
+ public static final String I_TRIGGER_HESITATE = "HESITATE";
+ public static final String I_TRIGGER_FAILURE = "FAILURE";
+
+ // Intervention types
+ public static final String I_TYPE_KNOWLEDGE = "KNOWLEDGE";
+ public static final String I_TYPE_APPLICATION = "APPLICATION";
+
+ // Cancel broadcasts
+ public static final String I_CANCEL_STUCK = "X_STUCK";
+ public static final String I_CANCEL_HESITATE = "X_HESITATE";
+ public static final String I_CANCEL_GESTURE = "X_GESTURE";
+
+ public static final String I_MODAL_EXTRA = "MODAL";
+
+ public static final String EXIT_FROM_INTERVENTION = "EXIT_FROM_INTERVENTION"; // might be needed to resume paused activity, e.g. Akira
+ public static final String INTERVENTION_FOLDER = "sdcard/intervention";
+ public static final String INTERVENTION_TIMES_FILE = "TimeSpentPerAttempt.csv";
+ public static final String INTERVENTION_STUDENT_FILE = "intervention.csv";
+ public static final String UPDATE_INTERVENTION_FILE = "update_intervention.csv";
+
+ // CONSTANTS -- replace with Judith's thing.
+ public static final int FAILURE_COUNT_BPOP = 9;
+ public static final Long HESITATE_TIME_BPOP = 6000L; // NOTE THAT THIS IS NOT RESETTING
+ public static final Long STUCK_TIME_BPOP = 20000L;
+ public static final Long GESTURE_TIME_BPOP = 6000L;
+
+ public static final int FAILURE_COUNT_AKIRA = 9;
+ public static final Long HESITATE_TIME_AKIRA = 10000L;
+ public static final Long STUCK_TIME_AKIRA = -1L; // not applicable...
+ public static final Long GESTURE_TIME_AKIRA = 10000L;
+
+
+ public static final Long HESITATE_TIME_SPELL = 15000L;
+ public static final Long STUCK_TIME_SPELL = 18000L;
+ public static final Long GESTURE_TIME_SPELL = 13000L;
+
+ public static final Long HESITATE_TIME_PICMATCH = 18000L;
+ public static final Long STUCK_TIME_PICMATCH = 20000L;
+ public static final Long GESTURE_TIME_PICMATCH = 13000L;
+
+ public static final Long HESITATE_TIME_WRITE = 18000L;
+ public static final Long STUCK_TIME_WRITE = 20000L;
+ public static final long GESTURE_TIME_WRITE = 18000L;
+
+ public static final Long HESITATE_TIME_NUMCOMPARE = 9000L;
+ public static final Long STUCK_TIME_NUMCOMPARE = 12000L;
+ public static final Long GESTURE_TIME_NUMCOMPARE = 9000L;
+
public static final String FTR_STORY_STARTING = "FTR_STORY_STARTING";
public static final String FWCORRECT = "FTR_RIGHT";
@@ -391,8 +590,12 @@ public class TCONST {
public static final String FWUNKNOWN = "FTR_UNRECOGNIZED";
public static final String FTR_EOI = "FTR_NOWORDS";
public static final String FTR_EOD = "FTR_EOD";
+ public static final String CONTINUE = "CONTINUE";
public static final String ALL_CORRECT = "ALL_CORRECT";
+ public static final String LOG_CORRECT = "CORRECT";
+ public static final String LOG_INCORRECT = "INCORRECT";
+
public static final String FALSE = "FALSE";
public static final String TRUE = "TRUE";
public static final String OVALICON = "OVALICON";
@@ -423,11 +626,47 @@ public class TCONST {
public static final int ALL_EVENTS = 0xFFFFFFFF;
+ public static final String ASR_TIMED_START_EVENT = "ASR_TIMED_START_EVENT";
+ public static final String ASR_RECOGNITION_EVENT = "ASR_RECOGNITION_EVENT";
+ public static final String ASR_ERROR_EVENT = "ASR_ERROR_EVENT";
+ public static final String ASR_SILENCE_EVENT = "ASR_SILENCE_EVENT";
+ public static final String ASR_SOUND_EVENT = "ASR_SOUND_EVENT";
+ public static final String ASR_WORD_EVENT = "ASR_WORD_EVENT";
+ public static final String ASR_TIMEDSILENCE_EVENT = "ASR_TIMEDSILENCE_EVENT";
+ public static final String ASR_TIMEDSOUND_EVENT = "ASR_TIMEDSOUND_EVENT";
+ public static final String ASR_TIMEDWORD_EVENT = "ASR_TIMEDWORD_EVENT";
+ public static final String UTTERANCE_COMPLETE_EVENT = "UTTERANCE_COMPLETE_EVENT";
+
+ public static final String ASR_ALL_TIMED_EVENTS = "ASR_ALL_TIMED_EVENTS";
+ public static final String ASR_ALL_STATIC_EVENTS = "ASR_ALL_STATIC_EVENTS";
+ public static final String ASR_ALL_EVENTS = "ASR_ALL_EVENTS";
+
+ // Map script event names to bitmap ASR Listener conatants.
+ //
+ static public HashMap ASREventMap = new HashMap();
+
+ static {
+ ASREventMap.put(ASR_SILENCE_EVENT, TCONST.SILENCE_EVENT);
+ ASREventMap.put(ASR_SOUND_EVENT, TCONST.SOUND_EVENT);
+ ASREventMap.put(ASR_WORD_EVENT, TCONST.WORD_EVENT);
+ ASREventMap.put(ASR_TIMEDSILENCE_EVENT, TCONST.TIMEDSILENCE_EVENT);
+ ASREventMap.put(ASR_TIMEDSOUND_EVENT, TCONST.TIMEDSOUND_EVENT);
+ ASREventMap.put(ASR_TIMEDWORD_EVENT, TCONST.TIMEDWORD_EVENT);
+ ASREventMap.put(ASR_TIMED_START_EVENT, TCONST.TIMEDSTART_EVENT);
+
+ ASREventMap.put(ASR_ALL_TIMED_EVENTS,TCONST.ALLTIMED_EVENTS);
+ ASREventMap.put(ASR_ALL_STATIC_EVENTS,TCONST.ALL_EVENTS);
+ ASREventMap.put(ASR_ALL_EVENTS,TCONST.ALL_EVENTS);
+ }
+
+
+
public static final int NOINTERVENTION = 0;
public static final int INSPEECH = 1;
public static final int SAYWORD = 2;
public static final String STORYDATA = "storydata.json";
+ public static final String STORYMCQ = "mcq.json";
public static final String STORYINDEX = "story_index.json";
public static final String SOURCEFILE = "[file]";
public static final String ASSETFILE = "[asset]";
@@ -436,13 +675,7 @@ public class TCONST {
public static final String TTS = "TTS";
public static final String ASR = "ASR";
- public static final String GLYPHLOG = "glyphlog_";
- public static final String DATASHOP = "-DS";
- public static final String JSONLOG = ".json";
- public static final boolean APPEND = true;
- public static final boolean REPLACE = false;
-
- public static final String GLYPH_DATA = "glyphdata";
+ public static final String GLYPH_DATA = "GLYPH_DATA";
// LTK messaging constants
@@ -452,7 +685,16 @@ public class TCONST {
public static final String FW_RESPONSE = "FW_RESPONSE";
public static final String WRITINGTUTOR_FOLDER = "/WritingTutor/";
- public static final String ROBOTUTOR_FOLDER = "/RoboTutor/";
+ public static final String HOT_LOG_FOLDER = "/RTFace_Login_HOT/";
+ public static final String READY_LOG_FOLDER = "/RTFace_Login/";
+
+ public static final String HOT_LOG_FOLDER_PERF = "/RTFace_Login_HOT/"; // use same as normal logs
+ public static final String READY_LOG_FOLDER_PERF = "/RTFace_Login/"; // use same as normal logs
+
+ public static final String INTERVENTION_LOG_FOLDER = "/InterventionLogs/";
+
+ public static final String AUDIO_LOG_FOLDER = "/RTFace_Login_Audio/";
+
public static final String ROBOTUTOR_ASSET_FOLDER = "/robotutor_assets/";
public static final String GLYPHS_FOLDER = "/glyphs/";
@@ -466,11 +708,6 @@ public class TCONST {
public static final String SET_RATE = "SET_RATE";
- // Preference keys
- public static final String ENGINE_INSTANCE = "RoboTutor";
- public static final String CURRENT_TUTOR = "tutor";
-
-
// Number Listeneing Component
private static final String[] placeValue = {".ones",".tens",".hundreds",".thousands",".millions",".billions"};
@@ -496,10 +733,10 @@ public class TCONST {
public static final String DIGIT2_WORDS_VAR = ".digit2Words";
public static final String DIGIT1_WORDS_VAR = ".digit1Words";
-
// Generic error codes
public static final String GENERIC_RIGHT = "FTR_RIGHT";
public static final String GENERIC_WRONG = "FTR_WRONG";
+ public static final String NEXTTURN = "THRD_WRONG";
public static final String GENERIC_SUCCESSIVEWRONG = "FTR_SWRONG";
public static final boolean TRUE_ERROR = true;
public static final boolean TRUE_NOERROR = true;
@@ -592,33 +829,52 @@ public class TCONST {
// READING Tutor State names -- RTC Reading Tutor Component
- public static final String PAGEFLIP_BUTTON = "PAGE_FLIP_BUTTON";
- public static final String SPEAK_BUTTON = "SPEAK_BUTTON";
+ public static final String PAGEFLIP_BUTTON = "PAGE_FLIP_CLICK";
+ public static final String SPEAK_BUTTON = "SPEAK_CLICK";
public static final String EMPTY = "";
public static final int INCR = 1;
public static final int DECR = -1;
- public static final String RTC_VAR_PAGESTATE = ".pageState";
- public static final String RTC_VAR_PARASTATE = ".paraState";
- public static final String RTC_VAR_LINESTATE = ".lineState";
- public static final String RTC_VAR_WORDSTATE = ".wordState";
- public static final String RTC_VAR_ATTEMPT = ".attempt";
- public static final String LAST = "LAST";
- public static final String NOT_LAST = "NOT_LAST";
+ public static final String RTC_VAR_ECHOSTATE = ".echoState";
+ public static final String RTC_VAR_PARROTSTATE = ".parrotState";
+ // Generic question state flag
+ public static final String RTC_VAR_QUESTIONSTATE = ".questionState";
+ public static final String RTC_VAR_CLOZESTATE = ".clozeState";
+ public static final String TO_CLOZE = ".toCloze";
+
+ public static final String RTC_VAR_SILENCESTATE = ".silenceState";
+ // Generic question audio file
+ public static final String RTC_QUESTION_AUDIO = ".questionAudio";
+ public static final String RTC_SILENCE = ".silence";
+ public static final String COMPLETE = "COMPLETE";
+ public static final String RTC_VAR_PAGESTATE = ".pageState";
+ public static final String RTC_VAR_PARASTATE = ".paraState";
+ public static final String RTC_VAR_LINESTATE = ".lineState";
+ public static final String RTC_VAR_WORDSTATE = ".wordState";
+ public static final String RTC_VAR_ATTEMPT = ".attempt";
+ public static final String LAST = "LAST";
+ public static final String NOT_LAST = "NOT_LAST";
+ //UHQ
+ public static final String HAS_DISTRACTOR = ".distractor";
+
public static final String RTC_VAR_STATE = ".storyState";
public static final String RTC_PARAGRAPHCOMPLETE = "PARAGRAPH_COMPLETE";
public static final String RTC_PAGECOMPLETE = "PAGE_COMPLETE";
public static final String RTC_STORYCMPLETE = "STORY_COMPLETE";
public static final String RTC_LINECOMPLETE = "LINE_COMPLETE";
+ //Generic question state of completion
public static final String RTC_CLEAR = "";
- public static final String RTC_VAR_WORDVALUE = ".currentWord";
- public static final String RTC_VAR_INDEX = ".wordindex";
- public static final String RTC_VAR_REMAINING = ".remainingWords";
- public static final String RTC_VAR_SENTENCE = ".sentence";
+ public static final String RTC_VAR_PROMPT = ".prompt";
+ public static final String RTC_VAR_PAGE_PROMPT = ".page_prompt";
+ public static final String RTC_VAR_WORDVALUE = ".currentWord";
+ public static final String RTC_VAR_INDEX = ".wordindex";
+ public static final String RTC_VAR_REMAINING = ".remainingWords";
+ public static final String RTC_VAR_SENTENCE = ".sentence";
+ public static final String RTC_VAR_UTTERANCE = ".utterance";
//Akira Game Prompt Situation
public static final String PROMPT_1LEFT = "PROMPT_1LEFT";
@@ -630,4 +886,48 @@ public class TCONST {
public static final String PROMPT_3 = "PROMPT_3";
public static final String PROMPT_3V = "PROMPT_3V";
+
+ // Writing behavior...
+ public static final int WRITING_DATA_LIMIT = 10;
+
+ // Counting
+ public static final String COUNTING_DEBUG_LOG = "COUNTING_DEBUG_LOG";
+
+
+ // Data source debugger
+
+ public static final String TAG_DEBUG_AKIRA = "akira";
+ public static final String TAG_DEBUG_ASM = "math";
+ public static final String TAG_DEBUG_TAP_COUNT = "countingx";
+ public static final String TAG_DEBUG_BPOP_LTR = "bpop.ltr.mix";
+ public static final String TAG_DEBUG_BPOP_WRD = "bpop.wrd";
+ public static final String TAG_DEBUG_BPOP_PHON = "bpop.phon";
+ public static final String TAG_DEBUG_BPOP_NUM = "bpop.num";
+ public static final String TAG_DEBUG_BPOP_SHP = "bpop.shp";
+ public static final String TAG_DEBUG_BPOP_EX = "bpop.ex";
+
+ public static final String ROBO_DEBUG_FILE_TAP_COUNT = "countingx_test.json";
+ public static final String ROBO_DEBUG_FILE_AKIRA = "akira_test.json";
+ public static final String ROBO_DEBUG_FILE_ASM = "math_test.json";
+ public static final String ROBO_DEBUG_FILE_BPOP = "bpop.json";
+
+
+
+ // Debugger Thumb key words
+ // NEW_THUMBS trace me
+ public enum Thumb {
+ AKIRA, BPOP_NUM, GL, MN, BPOP_LTR, CX_1, CX_10, CX_100, MATH, NUMSCALE, STORY_1, STORY_2, STORY_3, STORY_4, STORY_5, SONG, STORY_NONSTORY, WRITE, SPELLING, PICMATCH, NUMCOMPARE, COMPREHENSION, PLACEVALUE, BIGMATH, NOTHING
+ }
+
+ // debug vals
+ public static final String DEBUG_AUDIO_FILE = "DEBUG_AUDIO";
+
+ public static final String PLACEMENT_TAG = "DEBUG_PLACEMENT";
+ public static final String MENU_BUG_TAG = "MENU_BUG";
+ public static final String MATH_PLACEMENT = "MATH_PLACEMENT"; // TOGGLE_PLACEMENT trace me...
+ public static final String MATH_PLACEMENT_INDEX = "MATH_PLACEMENT_INDEX";
+ public static final String WRITING_PLACEMENT = "WRITING_PLACEMENT";
+ public static final String WRITING_PLACEMENT_INDEX = "WRITING_PLACEMENT_INDEX";
+
+ public static final String DEBUG_CSV = "DEBUG_CSV";
}
diff --git a/util/src/main/java/cmu/xprize/util/TimerMaster.java b/util/src/main/java/cmu/xprize/util/TimerMaster.java
new file mode 100644
index 0000000..7431112
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/TimerMaster.java
@@ -0,0 +1,155 @@
+package cmu.xprize.util;
+
+import android.content.Intent;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+
+import java.util.Date;
+
+import static cmu.xprize.util.consts.INTERVENTION_CONST.BROADCAST_GESTURE_UPDATE;
+import static cmu.xprize.util.consts.INTERVENTION_CONST.BROADCAST_HESITATION_UPDATE;
+import static cmu.xprize.util.consts.INTERVENTION_CONST.BROADCAST_STUCK_UPDATE;
+import static cmu.xprize.util.consts.INTERVENTION_CONST.EXTRA_TIME_EXPECT;
+import static cmu.xprize.util.TCONST.I_CANCEL_GESTURE;
+import static cmu.xprize.util.TCONST.I_CANCEL_HESITATE;
+import static cmu.xprize.util.TCONST.I_CANCEL_STUCK;
+import static cmu.xprize.util.TCONST.I_TRIGGER_GESTURE;
+import static cmu.xprize.util.TCONST.I_TRIGGER_HESITATE;
+import static cmu.xprize.util.TCONST.I_TRIGGER_STUCK;
+
+/**
+ * TimerMaster
+ * Use this class to manage timers for Interventions
+ * Created by kevindeland on 9/4/19.
+ */
+
+public class TimerMaster {
+
+ private IInterventionSource _intervention;
+ private CMessageQueueFactory _queue;
+ private LocalBroadcastManager _manager;
+ private long _hesitateDelay, _stuckDelay, _gestureDelay;
+
+ private static final String HESITATION_TIMER_RUNNABLE = "HESITATION_TIMER";
+ private static final String STUCK_TIMER_RUNNABLE = "STUCK_TIMER";
+ private static final String GESTURE_TIMER_RUNNABLE = "GESTURE_TIMER";
+
+ // need a boolean so we only trigger this once
+ private boolean gestureTriggered;
+
+ private String _TAG;
+
+
+ /**
+ * Constructor
+ * @param intervention has a "triggerIntervention" command
+ * @param queue responsible for posting delayed commands to the Queue. Has an IMessageQueueRunner
+ * associated with it that will know what to do when the timer has expired.
+ * @param hesitateDelay delay time to trigger HESITATE
+ * @param stuckDelay delay time to trigger STUCK
+ * @param gestureDelay delay time to trigger GESTURE
+ */
+ public TimerMaster(IInterventionSource intervention, CMessageQueueFactory queue,
+ LocalBroadcastManager manager, String TAG,
+ long hesitateDelay, long stuckDelay, long gestureDelay) {
+
+ this._intervention = intervention;
+ this._queue = queue;
+ this._manager = manager;
+ this._hesitateDelay = hesitateDelay;
+ this._stuckDelay = stuckDelay;
+ this._gestureDelay = gestureDelay;
+ this._TAG = TAG;
+ }
+
+
+ /**
+ * Stuck Timer only reset when a new problem begins
+ */
+ public void resetStuckTimer() {
+ cancelStuckTimer();
+ triggerStuckTimer();
+ }
+
+ private void cancelStuckTimer() {
+ Log.v(_TAG, "cancel stuck timer");
+ _queue.cancelPost(STUCK_TIMER_RUNNABLE);
+ _intervention.triggerIntervention(I_CANCEL_STUCK);
+ }
+
+ private void triggerStuckTimer() {
+ Log.v(_TAG, String.format("trigger stuck timer: %s, %s, %d",
+ STUCK_TIMER_RUNNABLE, I_TRIGGER_STUCK, _stuckDelay));
+ _queue.postNamed(STUCK_TIMER_RUNNABLE, I_TRIGGER_STUCK, _stuckDelay);
+
+ Intent stuckIntent = new Intent(BROADCAST_STUCK_UPDATE);
+ long expectedTrigger = (new Date()).getTime() + _stuckDelay;
+ stuckIntent.putExtra(EXTRA_TIME_EXPECT, expectedTrigger);
+ _manager.sendBroadcast(stuckIntent);
+ }
+
+ /**
+ * This should be called whenever ANY View is touched... without overriding existing functions.
+ */
+ public void resetHesitationTimer() {
+ cancelHesitationTimer();
+ triggerHesitationTimer();
+ }
+
+ private void cancelHesitationTimer() {
+ Log.v(_TAG, "cancel hesitation timer");
+ _queue.cancelPost(HESITATION_TIMER_RUNNABLE);
+ _intervention.triggerIntervention(I_CANCEL_HESITATE);
+ }
+
+ private void triggerHesitationTimer() {
+ Log.v(_TAG, String.format("trigger hesitation timer: %s, %s, %d",
+ HESITATION_TIMER_RUNNABLE, I_TRIGGER_HESITATE, _hesitateDelay));
+ _queue.postNamed(HESITATION_TIMER_RUNNABLE, I_TRIGGER_HESITATE, _hesitateDelay);
+
+ Intent hesitateIntent = new Intent(BROADCAST_HESITATION_UPDATE);
+ long expectedTrigger = (new Date()).getTime() + _hesitateDelay;
+ hesitateIntent.putExtra(EXTRA_TIME_EXPECT, expectedTrigger);
+ _manager.sendBroadcast(hesitateIntent);
+ }
+
+ /**
+ * This should be called when a normal gesture is made
+ */
+ public void resetGestureTimer() {
+ _queue.cancelPost(GESTURE_TIMER_RUNNABLE);
+ _intervention.triggerIntervention(I_CANCEL_GESTURE);
+
+ // note: must remove all gesture timers
+ _queue.postNamed(GESTURE_TIMER_RUNNABLE, I_TRIGGER_GESTURE, _gestureDelay);
+ gestureTriggered = true;
+ }
+
+ /**
+ * trigger gesture timer
+ */
+ public void triggerGestureTimer() {
+ if (gestureTriggered) return; // only trigger once
+
+ _queue.postNamed(GESTURE_TIMER_RUNNABLE, I_TRIGGER_GESTURE, _gestureDelay);
+ gestureTriggered = true;
+
+ Intent gestureIntent = new Intent(BROADCAST_GESTURE_UPDATE);
+ long expectedTrigger = (new Date()).getTime() + _gestureDelay;
+ gestureIntent.putExtra(EXTRA_TIME_EXPECT, expectedTrigger);
+ _manager.sendBroadcast(gestureIntent);
+ }
+
+ /**
+ * cancel gesture timer
+ */
+ public void cancelGestureTimer() {
+ if (!gestureTriggered) return; // don't need to cancel if not triggered
+
+ _queue.cancelPost(GESTURE_TIMER_RUNNABLE);
+ _intervention.triggerIntervention(I_CANCEL_GESTURE);
+ gestureTriggered = false;
+ }
+
+
+}
diff --git a/util/src/main/java/cmu/xprize/util/View_Helper.java b/util/src/main/java/cmu/xprize/util/View_Helper.java
index 7ae37f0..aee72b4 100644
--- a/util/src/main/java/cmu/xprize/util/View_Helper.java
+++ b/util/src/main/java/cmu/xprize/util/View_Helper.java
@@ -1,5 +1,5 @@
/**
- Copyright 2015 Kevin Willows
+ Copyright(c) 2015-2017 Kevin Willows
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/util/src/main/java/cmu/xprize/util/Word2NumFSM.java b/util/src/main/java/cmu/xprize/util/Word2NumFSM.java
index 79636c8..f065463 100644
--- a/util/src/main/java/cmu/xprize/util/Word2NumFSM.java
+++ b/util/src/main/java/cmu/xprize/util/Word2NumFSM.java
@@ -1,7 +1,6 @@
//*********************************************************************************
//
-// Copyright(c) 2016 Carnegie Mellon University. All Rights Reserved.
-// Copyright(c) Kevin Willows All Rights Reserved
+// Copyright(c) 2016-2017 Kevin Willows All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/util/src/main/java/cmu/xprize/util/consts/INTERVENTION_CONST.java b/util/src/main/java/cmu/xprize/util/consts/INTERVENTION_CONST.java
new file mode 100644
index 0000000..031116f
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/consts/INTERVENTION_CONST.java
@@ -0,0 +1,22 @@
+package cmu.xprize.util.consts;
+
+/**
+ * RoboTutor
+ *
+ * Created by kevindeland on 2019-11-04.
+ */
+public class INTERVENTION_CONST {
+
+ // whether to show the intervention button
+ public static final boolean CONFIG_INTERVENTION = true;
+ public static final boolean CONFIG_INTERVENTION_DEBUGGER = false;
+
+ // broadcasting intervention updates
+ public static final String EXTRA_TIME_EXPECT = "TIME_EXPECT";
+ public static final String BROADCAST_STUCK_UPDATE = "STUCK_UPDATE";
+ public static final String BROADCAST_HESITATION_UPDATE = "HESITATE_UPDATE";
+ public static final String BROADCAST_GESTURE_UPDATE = "GESTURE_UPDATE";
+ public static final String BROADCAST_FAILURE_UPDATE = "FAILURE_UPDATE";
+ public static final String FAILS_NEEDED = "FAILS_NEEDED";
+ public static final String FAILS_HAPPENED = "FAILS_HAPPENED";
+}
diff --git a/util/src/main/java/cmu/xprize/util/gesture/ExpectTapGestureListener.java b/util/src/main/java/cmu/xprize/util/gesture/ExpectTapGestureListener.java
new file mode 100644
index 0000000..bdc124b
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/gesture/ExpectTapGestureListener.java
@@ -0,0 +1,82 @@
+package cmu.xprize.util.gesture;
+
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import cmu.xprize.util.IInterventionSource;
+import cmu.xprize.util.TCONST;
+import cmu.xprize.util.TimerMaster;
+
+/**
+ * ExpectTapGestureListener
+ *
Expects a tap
+ * Created by kevindeland on 9/1/19.
+ */
+
+public class ExpectTapGestureListener extends GestureDetector.SimpleOnGestureListener {
+
+ private TimerMaster iTimer;
+ private String TAG = "TAP_GESTURE";
+
+ /**
+ * Intervention should not be triggered immediately, so the listener should trigger the
+ * gesture timer within the TimerMaster.
+ *
+ * @param timer a TimerMaster that triggers and resets the gesture timer
+ */
+ public ExpectTapGestureListener(TimerMaster timer) {
+ this.iTimer = timer;
+ }
+
+ @Override
+ public boolean onDown(MotionEvent event) {
+ Log.d(TAG,"onDown: ");
+
+ // don't return false here or else none of the other
+ // gestures will work
+ return true;
+ }
+
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ Log.i(TAG, "onSingleTapConfirmed: ");
+ iTimer.cancelGestureTimer();
+
+ return true;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ Log.i(TAG, "onLongPress: ");
+ iTimer.triggerGestureTimer();
+
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ Log.i(TAG, "onDoubleTap: ");
+ iTimer.triggerGestureTimer();
+
+
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ Log.i(TAG, "onScroll: ");
+
+ return true;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ Log.d(TAG, "onFling: ");
+ iTimer.triggerGestureTimer();
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/util/src/main/java/cmu/xprize/util/gesture/ExpectWriteGestureListener.java b/util/src/main/java/cmu/xprize/util/gesture/ExpectWriteGestureListener.java
new file mode 100644
index 0000000..36d526a
--- /dev/null
+++ b/util/src/main/java/cmu/xprize/util/gesture/ExpectWriteGestureListener.java
@@ -0,0 +1,80 @@
+package cmu.xprize.util.gesture;
+
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import cmu.xprize.util.IInterventionSource;
+import cmu.xprize.util.TCONST;
+import cmu.xprize.util.TimerMaster;
+
+/**
+ * ExpectWriteGestureListener
+ * Expects a write (aka fling/scroll)
+ * Created by kevindeland on 9/1/19.
+ */
+
+public class ExpectWriteGestureListener extends GestureDetector.SimpleOnGestureListener {
+
+ private TimerMaster iTimer;
+ private String TAG = "WRITE_GESTURE";
+
+ /**
+ * Intervention should not be triggered immediately, so the listener should trigger the
+ * gesture timer within the TimerMaster.
+ *
+ * @param timer a TimerMaster that triggers and resets the gesture timer
+ */
+ public ExpectWriteGestureListener(TimerMaster timer) {
+ this.iTimer = timer;
+ }
+
+ @Override
+ public boolean onDown(MotionEvent event) {
+ Log.d(TAG,"onDown: ");
+
+ // don't return false here or else none of the other
+ // gestures will work
+ return true;
+ }
+
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ Log.i(TAG, "onSingleTapConfirmed: ");
+ iTimer.triggerGestureTimer();
+ return true;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ Log.i(TAG, "onLongPress: ");
+ iTimer.triggerGestureTimer();
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ Log.i(TAG, "onDoubleTap: ");
+ iTimer.triggerGestureTimer();
+
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ Log.i(TAG, "onScroll: ");
+
+
+ return true;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ Log.d(TAG, "onFling: ");
+ iTimer.resetGestureTimer();
+
+ return true;
+ }
+}
diff --git a/util/src/main/res/drawable/robotutor_normal.xml b/util/src/main/res/drawable/robotutor_normal.xml
index 1f15dcf..7402709 100644
--- a/util/src/main/res/drawable/robotutor_normal.xml
+++ b/util/src/main/res/drawable/robotutor_normal.xml
@@ -13,7 +13,6 @@
android:pathData="M568,318H14c-6.6,0-12-5.4-12-12V14C2,7.4,7.4,2,14,2h554c6.6,0,12,5.4,12,12v292C580,312.6,574.6,318,568,318z" />
+
+
+ android:layout_gravity="center"
+ android:indeterminate="false"
+ android:visibility="visible"/>
+
+
+
+
+ android:layout_margin="20dp"
+ android:gravity="center"
+ android:textSize="10sp"
+ android:textStyle="normal"
+ android:textColor="#FFFFFF"
+ android:visibility="invisible"/>
\ No newline at end of file
diff --git a/util/src/test/java/cmu/xprize/common/CTutorData_Metadata_Test.java b/util/src/test/java/cmu/xprize/common/CTutorData_Metadata_Test.java
new file mode 100644
index 0000000..105557b
--- /dev/null
+++ b/util/src/test/java/cmu/xprize/common/CTutorData_Metadata_Test.java
@@ -0,0 +1,215 @@
+package cmu.xprize.common;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import cmu.xprize.util.CAt_Data;
+import cmu.xprize.util.CFileNameHasher;
+import cmu.xprize.util.CTutorData_Metadata;
+import cmu.xprize.util.TCONST;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class CTutorData_Metadata_Test {
+
+
+ private CTutorData_Metadata metadata;
+
+ private JSONObject devData;
+ private JSONObject mathTransitions;
+ private JSONObject writeTransitions;
+ private JSONObject storyTransitions;
+
+ @Before
+ public void setup() throws IOException, JSONException {
+
+ metadata = new CTutorData_Metadata();
+ parseDevData();
+ }
+
+ @Test
+ public void testThumbs() throws Exception {
+
+ JSONObject whichTransitions = storyTransitions;
+
+ Iterator it = whichTransitions.keys();
+ while(it.hasNext()) {
+
+ String tutorKey = it.next();
+
+ // --- begin tutor Data ---
+ CAt_Data data = new CAt_Data();
+
+
+
+ data.loadJSON((JSONObject) whichTransitions.get(tutorKey), null);
+
+ //System.out.println(tutorKey);
+
+ TCONST.Thumb thumb = CTutorData_Metadata.getThumbImage(data);
+
+ assertNotNull("Tutor: " + tutorKey + " bad result", thumb);
+
+ }
+ }
+
+ @Test
+ public void testWriteTransitions() throws Exception {
+
+ JSONObject whichTransitions = writeTransitions;
+
+ Iterator it = whichTransitions.keys();
+ while(it.hasNext()) {
+ String tutorKey = it.next();
+
+ // --- begin tutor Data ---
+ CAt_Data data = new CAt_Data();
+
+
+
+ data.loadJSON((JSONObject) whichTransitions.get(tutorKey), null);
+
+ //System.out.println(tutorKey);
+
+ ArrayList displayText = CTutorData_Metadata.parseNameIntoLabels(data);
+
+ assertNotNull("Tutor: " + tutorKey + " bad result", displayText);
+
+ //assertEquals("Tutor: " + tutorKey + " doesn't begin with ID.", displayText.get(0), "" + tutorKey + "");
+
+ //assertTrue("Tutor: " + tutorKey + " has size of " + displayText.size(), displayText.size() > 1);
+
+
+
+ if(true) continue;
+
+ // --- begin tokenizing tutorID ---
+ String[] tokens = tutorKey.split(":");
+ String prefix = tokens[0];
+ String[] prefixTokens = prefix.split("\\.");
+
+ String suffix = tokens[1];
+ String[] suffixTokens = suffix.split("\\.");
+
+ switch(prefixTokens[0]) {
+
+ case "write":
+
+ switch (prefixTokens[1]) {
+ case "ltr":
+
+ System.out.println(data.tutor_id);
+ break;
+
+
+ case "wrd":
+
+ System.out.println(Arrays.toString(suffixTokens));
+
+ switch (suffixTokens[0]) {
+ case "lc":
+ System.out.println(String.format("tutor: %s -- (%s, %s) -- %s", data.tutor_id, data.cell_column, data.cell_row, "lowercase"));
+ break;
+
+ case "syl":
+ System.out.println(String.format("tutor: %s -- (%s, %s) -- %s", data.tutor_id, data.cell_column, data.cell_row, "syllables"));
+ }
+
+
+ break;
+ }
+
+ break;
+
+
+ case "bpop":
+
+ switch (prefixTokens[1]) {
+
+ case "ltr":
+
+ break;
+
+ case "wrd":
+
+ String wordCase;
+
+ //System.out.println(String.format("tutor: %s\nBubblePop Words\nCase=%s", data.tutor_id, wordCase));
+ break;
+ }
+ break;
+
+
+ }
+ }
+ }
+
+ @Test
+ public void testMathTransitions() throws Exception {
+
+ JSONObject whichTransitions = mathTransitions;
+
+ Iterator it = whichTransitions.keys();
+ while(it.hasNext()) {
+
+ String tutorKey = it.next();
+
+ // --- begin tutor Data ---
+ CAt_Data data = new CAt_Data();
+
+
+
+ data.loadJSON((JSONObject) whichTransitions.get(tutorKey), null);
+
+ //System.out.println(tutorKey);
+
+ ArrayList displayText = CTutorData_Metadata.parseNameIntoLabels(data);
+
+ assertNotNull("Tutor: " + tutorKey + " bad result", displayText);
+
+ }
+ }
+
+ private void parseDevData() throws IOException, JSONException {
+
+ InputStream inputStream = null;
+
+ inputStream = new FileInputStream("/Users/kevindeland/RoboTutor/RoboTutor/app/src/main/assets/tutors/activity_selector/assets/data/sw/dev_data.json");
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 8);
+ StringBuilder sb = new StringBuilder();
+
+ String line = null;
+ while ((line = reader.readLine()) != null)
+ {
+ sb.append(line + "\n");
+ }
+ String result = sb.toString();
+
+ devData = new JSONObject(result);
+
+ mathTransitions = (JSONObject) devData.get("mathTransitions");
+ writeTransitions = (JSONObject) devData.get("writeTransitions");
+ storyTransitions = (JSONObject) devData.get("storyTransitions");
+ }
+
+
+}
\ No newline at end of file
diff --git a/util/src/test/java/cmu/xprize/common/ExampleUnitTest.java b/util/src/test/java/cmu/xprize/common/ExampleUnitTest.java
index 5a520dc..f42815b 100644
--- a/util/src/test/java/cmu/xprize/common/ExampleUnitTest.java
+++ b/util/src/test/java/cmu/xprize/common/ExampleUnitTest.java
@@ -1,7 +1,11 @@
-package cmu.xprize.util;
+package cmu.xprize.common;
+
+import android.util.Log;
import org.junit.Test;
+import cmu.xprize.util.CFileNameHasher;
+
import static org.junit.Assert.*;
/**
@@ -12,4 +16,20 @@ public class ExampleUnitTest {
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
+
+ @Test
+ public void checkFileNameHasher() {
+
+ CFileNameHasher hasher = CFileNameHasher.getInstance();
+
+ String[] inputs = {"nge", "n'ge", "n_ge"};
+
+ for (String input:
+ inputs) {
+ String hash = hasher.generateHash(input);
+ System.out.println("HASH_TEST for " + input + ":\t\t" + hash);
+ }
+
+ }
+
}
\ No newline at end of file