mirror of
https://github.com/TimeCrafters/UltimateGoal.git
synced 2025-12-13 13:12:33 +00:00
Added module for TimeCraftersConfigurationTool to support adding server to FtcRobotController activity
This commit is contained in:
1
TimeCraftersConfigurationTool/.gitignore
vendored
Normal file
1
TimeCraftersConfigurationTool/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
42
TimeCraftersConfigurationTool/build.gradle
Normal file
42
TimeCraftersConfigurationTool/build.gradle
Normal file
@@ -0,0 +1,42 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "30.0.1"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
// NOTE: Keep RobotCore version in sync with FtcRobotController modules version
|
||||
implementation 'org.firstinspires.ftc:RobotCore:6.0.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url = "https://dl.bintray.com/first-tech-challenge/ftcsdk/" }
|
||||
|
||||
flatDir {
|
||||
dirs '../libs'
|
||||
}
|
||||
}
|
||||
0
TimeCraftersConfigurationTool/consumer-rules.pro
Normal file
0
TimeCraftersConfigurationTool/consumer-rules.pro
Normal file
21
TimeCraftersConfigurationTool/proguard-rules.pro
vendored
Normal file
21
TimeCraftersConfigurationTool/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# 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
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("org.timecrafters.TimeCraftersConfigurationTool.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.timecrafters.TimeCraftersConfigurationTool">
|
||||
|
||||
/
|
||||
</manifest>
|
||||
@@ -0,0 +1,150 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Config;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Settings;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.TAC;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ActionDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ActionSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigurationDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigurationSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.GroupDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.GroupSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.PresetsDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.PresetsSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.VariableDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.VariableSerializer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
|
||||
public class TimeCraftersConfiguration {
|
||||
private static final String TAG = "TCT|TCConfig";
|
||||
private Config config;
|
||||
|
||||
public TimeCraftersConfiguration() {
|
||||
Settings settings = loadSettings();
|
||||
this.config = loadConfig(settings.config);
|
||||
}
|
||||
|
||||
public TimeCraftersConfiguration(String configName) {
|
||||
this.config = loadConfig(configName);
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public Group group(String groupName) {
|
||||
for (final Group group : config.getGroups()) {
|
||||
if (group.name.trim().equals(groupName.trim())) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
throw(new RuntimeException("Failed to find a group named:\"" + groupName.trim() + "\" in config \"" + config.getName() + "\""));
|
||||
}
|
||||
|
||||
public Action action(String groupName, String actionName) {
|
||||
final Group group = group(groupName);
|
||||
|
||||
for (Action action : group.getActions()) {
|
||||
if (action.name.trim().equals(actionName.trim())) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
throw(new RuntimeException("Failed to find an action named:\"" + actionName.trim() + "\" in group \"" + groupName.trim() + "\" in config \"" + config.getName() + "\""));
|
||||
}
|
||||
|
||||
public Variable variable(String groupName, String actionName, String variableName) {
|
||||
final Action action = action(groupName, groupName);
|
||||
|
||||
for (Variable variable : action.getVariables()) {
|
||||
if (variable.name.trim().equals(variableName.trim())) {
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
|
||||
throw(new RuntimeException("Failed to find a variable named \"" + variableName.trim() + "\" in action:\"" + actionName.trim() +
|
||||
"\" in group \"" + groupName.trim() + "\" in config \"" + config.getName() + "\""));
|
||||
}
|
||||
|
||||
private Settings loadSettings() {
|
||||
File settingsFile = new File(TAC.SETTINGS_PATH);
|
||||
|
||||
if (!settingsFile.exists()) {
|
||||
throw( new RuntimeException("Unable to load settings.json, file does not exist!") );
|
||||
}
|
||||
|
||||
try {
|
||||
return gsonForSettings().fromJson(new FileReader(settingsFile), Settings.class);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw( new RuntimeException("Unable to load settings.json") );
|
||||
}
|
||||
}
|
||||
|
||||
private Config loadConfig(String name) {
|
||||
if (name.equals("")) {
|
||||
throw(new RuntimeException("Cannot load a config with an empty name!"));
|
||||
}
|
||||
|
||||
String path = TAC.CONFIGS_PATH + File.separator + name + ".json";
|
||||
File configFile = new File(path);
|
||||
|
||||
if (configFile.exists() && configFile.isFile()) {
|
||||
try {
|
||||
Config config = gsonForConfig().fromJson(new FileReader(configFile), Config.class);
|
||||
config.setName(name);
|
||||
|
||||
return config;
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
throw(new RuntimeException("Unable to find a config file named \"" + name + "\""));
|
||||
}
|
||||
} else {
|
||||
throw(new RuntimeException("Unable to find a config file named \"" + name + "\""));
|
||||
}
|
||||
}
|
||||
|
||||
private Gson gsonForSettings() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(Settings.class, new SettingsSerializer())
|
||||
.registerTypeAdapter(Settings.class, new SettingsDeserializer())
|
||||
.create();
|
||||
}
|
||||
|
||||
public Gson gsonForConfig() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(Config.class, new ConfigSerializer())
|
||||
.registerTypeAdapter(Config.class, new ConfigDeserializer())
|
||||
|
||||
.registerTypeAdapter(Configuration.class, new ConfigurationSerializer())
|
||||
.registerTypeAdapter(Configuration.class, new ConfigurationDeserializer())
|
||||
|
||||
.registerTypeAdapter(Group.class, new GroupSerializer())
|
||||
.registerTypeAdapter(Group.class, new GroupDeserializer())
|
||||
|
||||
.registerTypeAdapter(Action.class, new ActionSerializer())
|
||||
.registerTypeAdapter(Action.class, new ActionDeserializer())
|
||||
|
||||
.registerTypeAdapter(Variable.class, new VariableSerializer())
|
||||
.registerTypeAdapter(Variable.class, new VariableDeserializer())
|
||||
|
||||
.registerTypeAdapter(Presets.class, new PresetsSerializer())
|
||||
.registerTypeAdapter(Presets.class, new PresetsDeserializer())
|
||||
.create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.SoundPool;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.R;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ActionDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ActionSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigurationDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigurationSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.GroupDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.GroupSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.PresetsDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.PresetsSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.VariableDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.VariableSerializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.tacnet.PacketHandler;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.tacnet.Server;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Backend {
|
||||
private static final String TAG = "Backend";
|
||||
static private HashMap<String, Object> storage = new HashMap<>();
|
||||
static private Backend instance;
|
||||
public Context applicationContext;
|
||||
private TACNET tacnet;
|
||||
private Server server;
|
||||
private Exception lastServerError;
|
||||
private Config config;
|
||||
private Settings settings;
|
||||
private boolean configChanged, settingsChanged;
|
||||
private MediaPlayer mediaPlayer;
|
||||
|
||||
public static HashMap<String, Object> getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public Backend() {
|
||||
if (Backend.instance() != null) {
|
||||
throw(new RuntimeException("Backend instance already exists!"));
|
||||
} else {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
if (!settings.config.isEmpty()) {
|
||||
loadConfig(settings.config);
|
||||
}
|
||||
tacnet = new TACNET();
|
||||
|
||||
configChanged = false;
|
||||
settingsChanged = false;
|
||||
}
|
||||
|
||||
static public Backend instance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public TACNET tacnet() {
|
||||
return tacnet;
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public void startServer() {
|
||||
try {
|
||||
server = new Server(settings.port);
|
||||
server.start();
|
||||
} catch (IOException error) {
|
||||
lastServerError = error;
|
||||
}
|
||||
}
|
||||
|
||||
public void stopServer() {
|
||||
if (server != null) {
|
||||
try {
|
||||
server.stop();
|
||||
server = null;
|
||||
} catch (IOException error) {
|
||||
lastServerError = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Exception getLastServerError() {
|
||||
return lastServerError;
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public Settings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public void configChanged() {
|
||||
config.getConfiguration().updatedAt = new Date();
|
||||
config.getConfiguration().revision += 1;
|
||||
configChanged = true;
|
||||
|
||||
saveConfig();
|
||||
|
||||
/* Automatically upload whole config to server
|
||||
* TODO: Implement a more atomic remote config updating
|
||||
* */
|
||||
if (config != null && tacnet.isConnected()) {
|
||||
String json = gsonForConfig().toJson(config);
|
||||
tacnet.puts(PacketHandler.packetUploadConfig(config.getName(), json).toString());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasConfigChanged() { return configChanged; }
|
||||
|
||||
public String configPath(String name) {
|
||||
return TAC.CONFIGS_PATH + File.separator + name + ".json";
|
||||
}
|
||||
|
||||
public void loadConfig(String name) {
|
||||
if (name.equals("")) {
|
||||
config = null;
|
||||
return;
|
||||
}
|
||||
|
||||
String path = configPath(name);
|
||||
File file = new File(path);
|
||||
|
||||
if (file.exists() && file.isFile()) {
|
||||
config = gsonForConfig().fromJson(readFromFile(path), Config.class);
|
||||
config.setName(name);
|
||||
}
|
||||
}
|
||||
|
||||
public Config loadConfigWithoutMutatingBackend(String name) {
|
||||
if (name.equals("")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String path = configPath(name);
|
||||
File file = new File(path);
|
||||
|
||||
if (file.exists() && file.isFile()) {
|
||||
Config config = gsonForConfig().fromJson(readFromFile(path), Config.class);
|
||||
config.setName(name);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isConfigValid(String json) {
|
||||
try {
|
||||
gsonForConfig().fromJson(json, Config.class);
|
||||
|
||||
return true;
|
||||
} catch (JsonSyntaxException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean saveConfig() {
|
||||
if (config == null) { return false; }
|
||||
|
||||
final String path = configPath(getConfig().getName());
|
||||
configChanged = false;
|
||||
|
||||
return writeToFile(path, gsonForConfig().toJson(config));
|
||||
}
|
||||
|
||||
public boolean moveConfig(String oldName, String newName) {
|
||||
final String oldPath = configPath(oldName);
|
||||
final String newPath = configPath(newName);
|
||||
|
||||
final File oldFile = new File(oldPath);
|
||||
final File newFile = new File(newPath);
|
||||
|
||||
if (!oldFile.exists() || !oldFile.isFile()) {
|
||||
Log.e(TAG, "moveConfig: Can not move config file \"" + oldPath + "\" does not exists!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newFile.exists() && newFile.isFile()) {
|
||||
Log.e(TAG, "moveConfig: Config file \"" + newPath + "\" already exists!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return oldFile.renameTo(newFile);
|
||||
}
|
||||
|
||||
public boolean deleteConfig(String name) {
|
||||
File file = new File(configPath(name));
|
||||
|
||||
return file.delete();
|
||||
}
|
||||
|
||||
public void writeNewConfig(String name) {
|
||||
String path = configPath(name);
|
||||
File file = new File(path);
|
||||
|
||||
Config config = new Config(name);
|
||||
|
||||
Gson gson = new Gson();
|
||||
|
||||
try {
|
||||
FileWriter fileWriter = new FileWriter(file);
|
||||
fileWriter.write(gsonForConfig().toJson(config));
|
||||
fileWriter.close();
|
||||
} catch (IOException error) {
|
||||
/* TODO */
|
||||
Log.d(TAG, "writeNewConfig: IO Error: " + error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<String> configsList() {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
|
||||
File directory = new File(TAC.CONFIGS_PATH);
|
||||
FilenameFilter filter = new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".json");
|
||||
}
|
||||
};
|
||||
File fileList[] = directory.listFiles(filter);
|
||||
for (File file : fileList) {
|
||||
list.add(file.getName().replace(".json", ""));
|
||||
}
|
||||
|
||||
Collections.sort(list, new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
return o1.toLowerCase().compareTo(o2.toLowerCase());
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public Gson gsonForConfig() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(Config.class, new ConfigSerializer())
|
||||
.registerTypeAdapter(Config.class, new ConfigDeserializer())
|
||||
|
||||
.registerTypeAdapter(Configuration.class, new ConfigurationSerializer())
|
||||
.registerTypeAdapter(Configuration.class, new ConfigurationDeserializer())
|
||||
|
||||
.registerTypeAdapter(Group.class, new GroupSerializer())
|
||||
.registerTypeAdapter(Group.class, new GroupDeserializer())
|
||||
|
||||
.registerTypeAdapter(Action.class, new ActionSerializer())
|
||||
.registerTypeAdapter(Action.class, new ActionDeserializer())
|
||||
|
||||
.registerTypeAdapter(Variable.class, new VariableSerializer())
|
||||
.registerTypeAdapter(Variable.class, new VariableDeserializer())
|
||||
|
||||
.registerTypeAdapter(Presets.class, new PresetsSerializer())
|
||||
.registerTypeAdapter(Presets.class, new PresetsDeserializer())
|
||||
.create();
|
||||
}
|
||||
|
||||
public void settingsChanged() {
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
public boolean haveSettingsChanged() {
|
||||
return settingsChanged;
|
||||
}
|
||||
|
||||
public void loadSettings() {
|
||||
File settingsFile = new File(TAC.SETTINGS_PATH);
|
||||
|
||||
if (!settingsFile.exists()) {
|
||||
Log.i(TAG, "Writing default settings.json");
|
||||
writeDefaultSettings();
|
||||
}
|
||||
|
||||
try {
|
||||
settings = gsonForSettings().fromJson(new FileReader(settingsFile), Settings.class);
|
||||
} catch (FileNotFoundException e) {
|
||||
// TODO
|
||||
Log.e(TAG, "Unable to load settings.json");
|
||||
}
|
||||
}
|
||||
|
||||
public void saveSettings() {
|
||||
Log.i(TAG, "Settings: " + gsonForSettings().toJson(settings));
|
||||
writeToFile(TAC.SETTINGS_PATH, gsonForSettings().toJson(settings));
|
||||
}
|
||||
|
||||
public void writeDefaultSettings() {
|
||||
settings = new Settings(TACNET.DEFAULT_HOSTNAME, TACNET.DEFAULT_PORT, "");
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
public Gson gsonForSettings() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(Settings.class, new SettingsSerializer())
|
||||
.registerTypeAdapter(Settings.class, new SettingsDeserializer())
|
||||
.create();
|
||||
}
|
||||
|
||||
public String readFromFile(String path) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
try {
|
||||
BufferedReader br = new BufferedReader( new FileReader(path) );
|
||||
String line;
|
||||
|
||||
while((line = br.readLine()) != null) {
|
||||
text.append(line);
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
public boolean writeToFile(String filePath, String content) {
|
||||
try {
|
||||
if (filePath.startsWith(TAC.ROOT_PATH)) {
|
||||
createFolders(filePath);
|
||||
|
||||
FileWriter writer = new FileWriter(filePath);
|
||||
writer.write(content);
|
||||
writer.close();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "writeToFile disallowed path: " + filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void createFolders(String filePath) throws IOException {
|
||||
File rootPath = new File(TAC.ROOT_PATH);
|
||||
File configsPath = new File(TAC.CONFIGS_PATH);
|
||||
|
||||
if (!rootPath.exists()) {
|
||||
rootPath.mkdir();
|
||||
}
|
||||
|
||||
if (!configsPath.exists()) {
|
||||
configsPath.mkdir();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
public class Config {
|
||||
private String name;
|
||||
private Configuration configuration;
|
||||
private ArrayList<Group> groups;
|
||||
private Presets presets;
|
||||
|
||||
public Config(String name) {
|
||||
this.name = name;
|
||||
this.configuration = new Configuration(new Date(), new Date(), TAC.CONFIG_SPEC_VERSION, 0);
|
||||
groups = new ArrayList<>();
|
||||
presets = new Presets(new ArrayList<Group>(), new ArrayList<Action>());
|
||||
}
|
||||
|
||||
public Config(Configuration configuration, ArrayList<Group> groups, Presets presets) {
|
||||
this.configuration = configuration;
|
||||
this.groups = groups;
|
||||
this.presets = presets;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public Presets getPresets() {
|
||||
return presets;
|
||||
}
|
||||
|
||||
public ArrayList<Group> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend;
|
||||
|
||||
public class Settings {
|
||||
public String hostname, config;
|
||||
public int port;
|
||||
|
||||
public Settings(String hostname, int port, String config) {
|
||||
this.hostname = hostname;
|
||||
this.port = port;
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class TAC {
|
||||
// TODO: Update filesystem handling
|
||||
public static final String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "TimeCrafters_Configuration_Tool",
|
||||
CONFIGS_PATH = ROOT_PATH + File.separator + "/configs",
|
||||
SETTINGS_PATH = ROOT_PATH + File.separator + "settings.json";
|
||||
|
||||
public static final int CONFIG_SPEC_VERSION = 2;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.tacnet.Client;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.tacnet.Connection;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TACNET {
|
||||
private final static String TAG = "TACNET|TACNET";
|
||||
public static final String DEFAULT_HOSTNAME = "192.168.49.1";
|
||||
public static final int DEFAULT_PORT = 8962;
|
||||
|
||||
public static final int SYNC_INTERVAL = 250; // ms
|
||||
public static final int HEARTBEAT_INTERVAL = 1_500; // ms
|
||||
|
||||
public enum Status {
|
||||
CONNECTED,
|
||||
CONNECTING,
|
||||
CONNECTION_ERROR,
|
||||
NOT_CONNECTED,
|
||||
}
|
||||
|
||||
private Connection connection;
|
||||
|
||||
public void connect(String hostname, int port) {
|
||||
if (connection != null && connection.isConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
connection = new Connection(hostname, port);
|
||||
|
||||
connection.connect(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(TAG, "run: " + connection.lastSocketError());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Status status() {
|
||||
if (isConnecting()) {
|
||||
return Status.CONNECTING;
|
||||
} else if (isConnectionError()) {
|
||||
return Status.CONNECTION_ERROR;
|
||||
} else if (isConnected()) {
|
||||
return Status.CONNECTED;
|
||||
} else {
|
||||
return Status.NOT_CONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return connection != null && connection.isConnected();
|
||||
}
|
||||
|
||||
public boolean isConnecting() {
|
||||
return connection != null && !connection.isConnected() && !connection.socketError();
|
||||
}
|
||||
|
||||
public boolean isConnectionError() {
|
||||
return connection != null && connection.socketError();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (IOException e) {}
|
||||
|
||||
connection = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
if (isConnected()) {
|
||||
return connection.getClient();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void puts(String message) {
|
||||
if (isConnected()) {
|
||||
connection.puts(message);
|
||||
}
|
||||
}
|
||||
|
||||
public String gets() {
|
||||
if (isConnected()) {
|
||||
return connection.gets();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static long milliseconds() {
|
||||
return SystemClock.elapsedRealtime();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Action {
|
||||
public String name, comment;
|
||||
public boolean enabled;
|
||||
private ArrayList<Variable> variables;
|
||||
|
||||
public Action(String name, String comment, boolean enabled, ArrayList<Variable> variables) {
|
||||
this.name = name;
|
||||
this.comment = comment;
|
||||
this.enabled = enabled;
|
||||
this.variables = variables;
|
||||
}
|
||||
|
||||
public ArrayList<Variable> getVariables() { return variables; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend.config;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class Configuration {
|
||||
public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss Z";
|
||||
|
||||
public Date createdAt, updatedAt;
|
||||
private int specVersion;
|
||||
public int revision;
|
||||
|
||||
public Configuration(Date createdAt, Date updatedAt, int specVersion, int revision) {
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
this.specVersion = specVersion;
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
public int getSpecVersion() { return specVersion; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Group {
|
||||
public String name;
|
||||
private ArrayList<Action> actions;
|
||||
|
||||
public Group(String name, ArrayList<Action> actions) {
|
||||
this.name = name;
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
public static boolean nameIsUnique(ArrayList<Group> groups, String name) {
|
||||
for (Group group: groups) {
|
||||
if (group.name.equals(name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public ArrayList<Action> getActions() {
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend.config;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Presets {
|
||||
private ArrayList<Group> groups;
|
||||
private ArrayList<Action> actions;
|
||||
|
||||
public Presets(ArrayList<Group> groups, ArrayList<Action> actions) {
|
||||
this.groups = groups;
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
public ArrayList<Group> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
|
||||
public ArrayList<Action> getActions() {
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend.config;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Variable {
|
||||
public String name;
|
||||
private String value;
|
||||
|
||||
public Variable(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String rawValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public <T> T value() {
|
||||
return valueOf(value);
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static public <T> T valueOf(String value) {
|
||||
String[] split = value.split("x", 2);
|
||||
// Log.d("Variable", "valueOf split: " + Arrays.toString(split));
|
||||
|
||||
switch (split[0]) {
|
||||
case "B": {
|
||||
return (T) Boolean.valueOf(split[(split.length-1)]);
|
||||
}
|
||||
case "D": {
|
||||
return (T) Double.valueOf(split[(split.length-1)]);
|
||||
}
|
||||
case "F": {
|
||||
return (T) Float.valueOf(split[(split.length-1)]);
|
||||
}
|
||||
case "I": {
|
||||
return (T) Integer.valueOf(split[(split.length-1)]);
|
||||
}
|
||||
case "L": {
|
||||
return (T) Long.valueOf(split[(split.length-1)]);
|
||||
}
|
||||
case "S": {
|
||||
String string = "";
|
||||
int i = 0;
|
||||
for(String str : split) {
|
||||
if (i == 0) { i++; continue; }
|
||||
|
||||
string += str;
|
||||
}
|
||||
return (T) string;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public String typeOf(String value) {
|
||||
String[] split = value.split("x");
|
||||
|
||||
switch (split[0]) {
|
||||
case "B": {
|
||||
return "Boolean";
|
||||
}
|
||||
case "D": {
|
||||
return "Double";
|
||||
}
|
||||
case "F": {
|
||||
return "Float";
|
||||
}
|
||||
case "I": {
|
||||
return "Integer";
|
||||
}
|
||||
case "L": {
|
||||
return "Long";
|
||||
}
|
||||
case "S": {
|
||||
return "String";
|
||||
}
|
||||
default: {
|
||||
return "=!UNKNOWN!=";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ActionDeserializer implements JsonDeserializer<Action> {
|
||||
@Override
|
||||
public Action deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
|
||||
final String name = jsonObject.get("name").getAsString();
|
||||
final String comment = jsonObject.get("comment").getAsString();
|
||||
final boolean enabled = jsonObject.get("enabled").getAsBoolean();
|
||||
Variable[] variablesArray = context.deserialize(jsonObject.get("variables"), Variable[].class);
|
||||
|
||||
List<Variable> variablesList = Arrays.asList(variablesArray);
|
||||
ArrayList<Variable> variables = new ArrayList<>(variablesList);
|
||||
|
||||
return new Action(name, comment, enabled, variables);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class ActionSerializer implements JsonSerializer<Action> {
|
||||
@Override
|
||||
public JsonElement serialize(Action action, Type type, JsonSerializationContext context) {
|
||||
JsonObject container = new JsonObject();
|
||||
|
||||
container.add("name", new JsonPrimitive(action.name));
|
||||
container.add("comment", new JsonPrimitive(action.comment));
|
||||
container.add("enabled", new JsonPrimitive(action.enabled));
|
||||
container.add("variables", context.serialize(action.getVariables().toArray(), Variable[].class));
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Config;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigDeserializer implements JsonDeserializer<Config> {
|
||||
@Override
|
||||
public Config deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
JsonObject data = jsonObject.get("data").getAsJsonObject();
|
||||
|
||||
Configuration configuration = context.deserialize(jsonObject.get("config"), Configuration.class);
|
||||
Group[] groupsArray = context.deserialize(data.get("groups"), Group[].class);
|
||||
List<Group> groupsList = Arrays.asList(groupsArray);
|
||||
ArrayList<Group> groups = new ArrayList<>(groupsList);
|
||||
|
||||
Presets presets = context.deserialize(data.get("presets"), Presets.class);
|
||||
|
||||
return new Config(configuration, groups, presets);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Config;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class ConfigSerializer implements JsonSerializer<Config> {
|
||||
@Override
|
||||
public JsonElement serialize(Config config, Type type, JsonSerializationContext context) {
|
||||
JsonObject container = new JsonObject();
|
||||
JsonObject result = new JsonObject();
|
||||
JsonObject presets = new JsonObject();
|
||||
container.add("config", context.serialize(config.getConfiguration(), Configuration.class));
|
||||
result.add("groups", context.serialize(config.getGroups().toArray(), Group[].class));
|
||||
|
||||
presets.add("groups", context.serialize(config.getPresets().getGroups().toArray(), Group[].class));
|
||||
presets.add("actions", context.serialize(config.getPresets().getActions().toArray(), Action[].class));
|
||||
|
||||
result.add("presets", presets);
|
||||
container.add("data", result);
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class ConfigurationDeserializer implements JsonDeserializer<Configuration> {
|
||||
@Override
|
||||
public Configuration deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject config = json.getAsJsonObject();
|
||||
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(Configuration.DATE_FORMAT);
|
||||
Date createdAt = new Date();
|
||||
Date updatedAt = new Date();
|
||||
try {
|
||||
createdAt = dateFormat.parse(config.get("created_at").getAsString());
|
||||
updatedAt = dateFormat.parse(config.get("updated_at").getAsString());
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
final int spec_version = config.get("spec_version").getAsInt();
|
||||
final int revision = config.get("revision").getAsInt();
|
||||
|
||||
return new Configuration(createdAt, updatedAt, spec_version, revision);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
public class ConfigurationSerializer implements JsonSerializer<Configuration> {
|
||||
@Override
|
||||
public JsonElement serialize(Configuration configuration, Type type, JsonSerializationContext context) {
|
||||
JsonObject container = new JsonObject();
|
||||
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(Configuration.DATE_FORMAT);
|
||||
|
||||
container.add("created_at", new JsonPrimitive(dateFormat.format(configuration.createdAt)));
|
||||
container.add("updated_at", new JsonPrimitive(dateFormat.format(configuration.updatedAt)));
|
||||
container.add("spec_version", new JsonPrimitive(configuration.getSpecVersion()));
|
||||
container.add("revision", new JsonPrimitive(configuration.revision));
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Config;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class GroupDeserializer implements JsonDeserializer<Group> {
|
||||
@Override
|
||||
public Group deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
|
||||
final String name = jsonObject.get("name").getAsString();
|
||||
Action[] actionsArray = context.deserialize(jsonObject.get("actions"), Action[].class);
|
||||
|
||||
List<Action> actionsList = Arrays.asList(actionsArray);
|
||||
ArrayList<Action> actions = new ArrayList<>(actionsList);
|
||||
|
||||
return new Group(name, actions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Config;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class GroupSerializer implements JsonSerializer<Group> {
|
||||
@Override
|
||||
public JsonElement serialize(Group group, Type type, JsonSerializationContext context) {
|
||||
JsonObject container = new JsonObject();
|
||||
|
||||
container.add("name", new JsonPrimitive(group.name));
|
||||
container.add("actions", context.serialize(group.getActions().toArray(), Action[].class));
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PresetsDeserializer implements JsonDeserializer<Presets> {
|
||||
@Override
|
||||
public Presets deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
|
||||
Group[] GroupsArray = context.deserialize(jsonObject.get("groups"), Group[].class);
|
||||
Action[] actionsArray = context.deserialize(jsonObject.get("actions"), Action[].class);
|
||||
|
||||
List<Group> groupsList = Arrays.asList(GroupsArray);
|
||||
ArrayList<Group> groups = new ArrayList<>(groupsList);
|
||||
List<Action> actionsList = Arrays.asList(actionsArray);
|
||||
ArrayList<Action> actions = new ArrayList<>(actionsList);
|
||||
|
||||
return new Presets(groups, actions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class PresetsSerializer implements JsonSerializer<Presets> {
|
||||
@Override
|
||||
public JsonElement serialize(Presets presets, Type type, JsonSerializationContext context) {
|
||||
JsonObject container = new JsonObject();
|
||||
|
||||
container.add("groups", context.serialize(presets.getGroups().toArray(), Group[].class));
|
||||
container.add("actions", context.serialize(presets.getActions().toArray(), Action[].class));
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Settings;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class SettingsDeserializer implements JsonDeserializer<Settings> {
|
||||
@Override
|
||||
public Settings deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
JsonObject data = jsonObject.get("data").getAsJsonObject();
|
||||
|
||||
return new Settings(
|
||||
data.get("hostname").getAsString(),
|
||||
data.get("port").getAsInt(),
|
||||
data.get("config").getAsString()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Settings;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class SettingsSerializer implements JsonSerializer<Settings> {
|
||||
@Override
|
||||
public JsonElement serialize(Settings settings, Type type, JsonSerializationContext context) {
|
||||
JsonObject container = new JsonObject();
|
||||
JsonObject result = new JsonObject();
|
||||
result.add("hostname", new JsonPrimitive(settings.hostname));
|
||||
result.add("port", new JsonPrimitive(settings.port));
|
||||
result.add("config", new JsonPrimitive(settings.config));
|
||||
|
||||
container.add("data", result);
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class VariableDeserializer implements JsonDeserializer<Variable> {
|
||||
@Override
|
||||
public Variable deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
|
||||
final String name = jsonObject.get("name").getAsString();
|
||||
final String value = jsonObject.get("value").getAsString();
|
||||
|
||||
return new Variable(name, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class VariableSerializer implements JsonSerializer<Variable> {
|
||||
@Override
|
||||
public JsonElement serialize(Variable variable, Type type, JsonSerializationContext context) {
|
||||
JsonObject container = new JsonObject();
|
||||
|
||||
container.add("name", new JsonPrimitive(variable.name));
|
||||
container.add("value", new JsonPrimitive(variable.rawValue()));
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.tacnet;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.TACNET;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Client {
|
||||
private Socket socket;
|
||||
private BufferedReader bufferedReader;
|
||||
private BufferedWriter bufferedWriter;
|
||||
private String uuid;
|
||||
|
||||
private ArrayList<String> readQueue;
|
||||
private ArrayList<String> writeQueue;
|
||||
|
||||
final private Object readQueueLock = new Object();
|
||||
final private Object writeQueueLock = new Object();
|
||||
|
||||
private long syncInterval = TACNET.SYNC_INTERVAL;
|
||||
|
||||
private int packetsSent, packetsReceived = 0;
|
||||
private long dataSent, dataReceived = 0;
|
||||
|
||||
private String TAG = "TACNET|Client";
|
||||
|
||||
private boolean socketError = false;
|
||||
private String lastSocketError;
|
||||
|
||||
public Client() {
|
||||
this.uuid = (UUID.randomUUID()).toString();
|
||||
|
||||
this.readQueue = new ArrayList<>();
|
||||
this.writeQueue = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void setSyncInterval(long milliseconds) {
|
||||
syncInterval = milliseconds;
|
||||
}
|
||||
|
||||
public void setSocket(Socket socket) throws IOException {
|
||||
this.socket = socket;
|
||||
|
||||
// This socket is for a "Connection" thus set a connect timeout
|
||||
if (!this.socket.isBound()) {
|
||||
this.socket.connect(new InetSocketAddress(Backend.instance().getSettings().hostname, Backend.instance().getSettings().port), 1500);
|
||||
}
|
||||
|
||||
this.bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
this.bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
|
||||
startReader();
|
||||
startWriter();
|
||||
}
|
||||
|
||||
public ArrayList<String> readQueue() {
|
||||
return readQueue;
|
||||
}
|
||||
|
||||
public ArrayList<String> writeQueue() {
|
||||
return writeQueue;
|
||||
}
|
||||
|
||||
private void startReader() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while(!socket.isClosed()) {
|
||||
// READER
|
||||
try {
|
||||
String message = read();
|
||||
if (!message.equals("")) {
|
||||
Log.i(TAG, "Read: " + message);
|
||||
|
||||
synchronized (readQueueLock) {
|
||||
readQueue.add(message);
|
||||
|
||||
packetsReceived++;
|
||||
dataReceived += message.length();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Read error: " + e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(syncInterval);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void startWriter() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while(!socket.isClosed()) {
|
||||
// WRITER
|
||||
String message;
|
||||
|
||||
synchronized (writeQueueLock) {
|
||||
for (Iterator itr = writeQueue.iterator(); itr.hasNext(); ) {
|
||||
try {
|
||||
message = (String) itr.next();
|
||||
|
||||
write(message);
|
||||
|
||||
packetsSent++;
|
||||
dataSent += message.length();
|
||||
|
||||
Log.i(TAG, "Write: " + message);
|
||||
itr.remove();
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Write error: " + e.getLocalizedMessage());
|
||||
socketError = true;
|
||||
lastSocketError = e.getLocalizedMessage();
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException k) {
|
||||
Log.e(TAG, "Failed to close socket: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(syncInterval);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void sync(Runnable runner) {
|
||||
runner.run();
|
||||
}
|
||||
|
||||
public void handleReadQueue() {
|
||||
String message = this.gets();
|
||||
|
||||
while (message != null) {
|
||||
Log.i(TAG, "Writing to Queue: " + message);
|
||||
this.puts(message);
|
||||
|
||||
message = this.gets();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public String uuid() {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return this.socket != null && !this.socket.isClosed();
|
||||
}
|
||||
|
||||
public boolean isBound() {
|
||||
return this.socket == null || this.socket.isBound();
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return this.socket == null || this.socket.isClosed();
|
||||
}
|
||||
|
||||
public void write(String message) throws IOException {
|
||||
bufferedWriter.write(message + "\r\n\n");
|
||||
bufferedWriter.flush();
|
||||
}
|
||||
|
||||
public String read() throws IOException {
|
||||
String message = "";
|
||||
String readLine;
|
||||
|
||||
while((readLine = bufferedReader.readLine()) != null) {
|
||||
message+=readLine;
|
||||
if (readLine.isEmpty()) { break; }
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public void puts(String message) {
|
||||
synchronized (writeQueueLock) {
|
||||
writeQueue.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
public String gets() {
|
||||
String message = null;
|
||||
|
||||
synchronized (readQueueLock) {
|
||||
if (readQueue.size() > 0) {
|
||||
message = readQueue.get(0);
|
||||
|
||||
readQueue.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public String encode(String message) {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String decode(String blob) {
|
||||
return blob;
|
||||
}
|
||||
|
||||
public int getPacketsSent() { return packetsSent; }
|
||||
public int getPacketsReceived() { return packetsReceived; }
|
||||
public long getDataSent() { return dataSent; }
|
||||
public long getDataReceived() { return dataReceived; }
|
||||
|
||||
public boolean socketError() {
|
||||
return socketError;
|
||||
}
|
||||
public String lastSocketError() {
|
||||
return lastSocketError;
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
this.bufferedWriter.flush();
|
||||
}
|
||||
|
||||
public void close(String reason) throws IOException {
|
||||
write(reason);
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (this.socket != null) {
|
||||
this.socket.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.tacnet;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.TACNET;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
public class Connection {
|
||||
private PacketHandler packetHandler;
|
||||
private Client client;
|
||||
private String hostname;
|
||||
private int port;
|
||||
private String lastSocketError = null;
|
||||
private boolean socketError = false;
|
||||
|
||||
private long lastSyncTime = 0;
|
||||
private long syncInterval = TACNET.SYNC_INTERVAL;
|
||||
|
||||
private Runnable connectionHandlingRunner;
|
||||
private long lastHeartBeatSent = 0;
|
||||
private long heartBeatInterval = TACNET.HEARTBEAT_INTERVAL;
|
||||
|
||||
private String TAG = "TACNET|Connection";
|
||||
|
||||
public Connection(String hostname, int port) {
|
||||
this.hostname = hostname;
|
||||
this.port = port;
|
||||
this.packetHandler = new PacketHandler(true);
|
||||
|
||||
this.connectionHandlingRunner = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleConnection();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void connect(final Runnable errorCallback) {
|
||||
if (client != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
client = new Client();
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
client.setSocket(new Socket());
|
||||
Log.i(TAG, "Connected to: " + hostname + ":" + port);
|
||||
|
||||
while(client != null && !client.isClosed()) {
|
||||
if (System.currentTimeMillis() > lastSyncTime + syncInterval) {
|
||||
lastSyncTime = System.currentTimeMillis();
|
||||
|
||||
client.sync(connectionHandlingRunner);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
socketError = true;
|
||||
lastSocketError = e.getLocalizedMessage();
|
||||
|
||||
errorCallback.run();
|
||||
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void handleConnection() {
|
||||
if (client != null && !client.isClosed()) {
|
||||
String message = client.gets();
|
||||
|
||||
if (message != null) {
|
||||
packetHandler.handle(message);
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() > lastHeartBeatSent + heartBeatInterval) {
|
||||
lastHeartBeatSent = System.currentTimeMillis();
|
||||
|
||||
client.puts(PacketHandler.packetHeartBeat().toString());
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(syncInterval);
|
||||
} catch (InterruptedException e) {
|
||||
// Failed to sleep I suppose.
|
||||
}
|
||||
|
||||
} else {
|
||||
client = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void puts(String message) {
|
||||
this.client.puts(message);
|
||||
}
|
||||
|
||||
public String gets() {
|
||||
return this.client.gets();
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return this.client == null || this.client.isClosed();
|
||||
}
|
||||
public boolean isConnected() {
|
||||
return this.client != null && this.client.isConnected();
|
||||
}
|
||||
public boolean socketError() {
|
||||
return socketError ? socketError : client.socketError();
|
||||
}
|
||||
public String lastSocketError() {
|
||||
return lastSocketError != null ? lastSocketError : client.lastSocketError();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.client.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.tacnet;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Packet {
|
||||
final static public String PROTOCOL_VERSION = "1";
|
||||
final static public String PROTOCOL_SEPERATOR = "|";
|
||||
final static public String PROTOCOL_HEARTBEAT = "heartbeat";
|
||||
private static final String TAG = "TACNET|Packet";
|
||||
|
||||
|
||||
// NOTE: PacketType is cast to a char, no more than 255 packet types can exist unless
|
||||
// header is updated.
|
||||
public enum PacketType {
|
||||
HANDSHAKE(0),
|
||||
HEARTBEAT(1),
|
||||
ERROR(2),
|
||||
|
||||
DOWNLOAD_CONFIG(10),
|
||||
UPLOAD_CONFIG(11),
|
||||
LIST_CONFIGS(12),
|
||||
SELECT_CONFIG(13),
|
||||
ADD_CONFIG(14),
|
||||
UPDATE_CONFIG(15),
|
||||
DELETE_CONFIG(16),
|
||||
|
||||
ADD_GROUP(20),
|
||||
UPDATE_GROUP(21),
|
||||
DELETE_GROUP(22),
|
||||
|
||||
ADD_ACTION(30),
|
||||
UPDATE_ACTION(31),
|
||||
DELETE_ACTION(32),
|
||||
|
||||
ADD_VARIABLE(40),
|
||||
UPDATE_VARIABLE(41),
|
||||
DELETE_VARIABLE(42);
|
||||
|
||||
private int id;
|
||||
final public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
PacketType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
private String protocolVersion;
|
||||
private PacketType packetType;
|
||||
private int contentLength;
|
||||
private String content;
|
||||
|
||||
String rawMessage;
|
||||
|
||||
Packet(String protocolVersion, PacketType packetType, int contentLength, String content) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.packetType = packetType;
|
||||
this.contentLength = contentLength;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
static public Packet fromStream(String message) {
|
||||
String version;
|
||||
PacketType type = null;
|
||||
int length;
|
||||
String body;
|
||||
|
||||
String[] slice = message.split("\\|", 4);
|
||||
|
||||
if (slice.length < 4) {
|
||||
Log.i(TAG, "Failed to split packet along first 4 " + PROTOCOL_SEPERATOR + ". Raw return: " + Arrays.toString(slice));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!slice[0].equals(PROTOCOL_VERSION)) {
|
||||
Log.i(TAG, "Incompatible protocol version received, expected: " + PROTOCOL_VERSION + " got: " + slice[0]);
|
||||
return null;
|
||||
}
|
||||
|
||||
version = slice[0];
|
||||
// type = PacketType.values()[Integer.parseInt(slice[1])];
|
||||
length = Integer.parseInt(slice[2]);
|
||||
body = slice[slice.length - 1];
|
||||
|
||||
int typeId = Integer.parseInt(slice[1]);
|
||||
for (PacketType packetType : PacketType.values()) {
|
||||
if (packetType.getId() == typeId) {
|
||||
type = packetType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Packet(version, type, length, body);
|
||||
}
|
||||
|
||||
static public Packet create(PacketType packetType, String message) {
|
||||
return new Packet(PROTOCOL_VERSION, packetType, message.length(), message);
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
if (rawMessage == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String[] parts = rawMessage.split(PROTOCOL_SEPERATOR);
|
||||
|
||||
return parts[0].equals(PROTOCOL_VERSION) &&
|
||||
isPacketTypeValid( Integer.parseInt(parts[1]));
|
||||
}
|
||||
|
||||
public boolean isPacketTypeValid(int rawPacketType) {
|
||||
return PacketType.values().length >= rawPacketType && PacketType.values()[rawPacketType] != null;
|
||||
}
|
||||
|
||||
public String encodeHeader() {
|
||||
String string = "";
|
||||
string += PROTOCOL_VERSION;
|
||||
string += PROTOCOL_SEPERATOR;
|
||||
string += packetType.getId();
|
||||
string += PROTOCOL_SEPERATOR;
|
||||
string += contentLength;
|
||||
string += PROTOCOL_SEPERATOR;
|
||||
return string;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return ("" + encodeHeader() + content);
|
||||
}
|
||||
|
||||
public String getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public PacketType getPacketType() {
|
||||
return packetType;
|
||||
}
|
||||
|
||||
public int getContentLength() {
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.tacnet;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Config;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.TAC;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PacketHandler {
|
||||
private static final String TAG = "TACNET|PacketHandler";
|
||||
private boolean hostIsAConnection = false;
|
||||
|
||||
public PacketHandler(boolean isHostAConnection) {
|
||||
this.hostIsAConnection = isHostAConnection;
|
||||
}
|
||||
|
||||
public void handle(String message) {
|
||||
Packet packet = Packet.fromStream(message);
|
||||
|
||||
if (packet != null && packet.isValid()) {
|
||||
Log.i(TAG, "Received packet of type: " + packet.getPacketType());
|
||||
handOff(packet);
|
||||
} else {
|
||||
if (packet == null) {
|
||||
Log.i(TAG, "Rejected raw packet: " + message);
|
||||
} else {
|
||||
Log.i(TAG, "Rejected packet: " + packet.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handOff(Packet packet) {
|
||||
switch(packet.getPacketType()) {
|
||||
case HANDSHAKE: {
|
||||
handleHandShake(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
case HEARTBEAT: {
|
||||
handleHeartBeat(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
case ERROR: {
|
||||
handleError(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
case DOWNLOAD_CONFIG: {
|
||||
handleDownloadConfig(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
case UPLOAD_CONFIG: {
|
||||
handleUploadConfig(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
case LIST_CONFIGS: {
|
||||
handleListConfigs(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
case ADD_CONFIG: {
|
||||
handleAddConfig(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
case UPDATE_CONFIG: {
|
||||
handleUpdateConfig(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
case DELETE_CONFIG: {
|
||||
handleDeleteConfig(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
// case CHANGE_ACTION: {
|
||||
// handleChangeAction(packet);
|
||||
// return;
|
||||
// }
|
||||
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NO-OP
|
||||
private void handleHandShake(Packet packet) {}
|
||||
// NO-OP
|
||||
private void handleHeartBeat(Packet packet) {}
|
||||
// NO-OP
|
||||
private void handleError(Packet packet) {}
|
||||
|
||||
private void handleUploadConfig(Packet packet) {
|
||||
String[] split = packet.getContent().split("\\" + Packet.PROTOCOL_SEPERATOR, 2);
|
||||
final String configName = split[0];
|
||||
final String json = split[1];
|
||||
|
||||
if (configName.length() == 0 && !Backend.instance().isConfigValid(json)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String path = TAC.CONFIGS_PATH + File.separator + configName + ".json";
|
||||
|
||||
Backend.instance().writeToFile(path, json);
|
||||
|
||||
if (Backend.instance().getConfig().getName().equals(configName)) {
|
||||
Backend.instance().loadConfig(configName);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDownloadConfig(Packet packet) {
|
||||
final String configName = packet.getContent();
|
||||
|
||||
Log.i(TAG, "Got request for config: " + packet.getContent());
|
||||
Packet pkt;
|
||||
if (Backend.instance().configsList().contains(configName)) {
|
||||
final String path = TAC.CONFIGS_PATH + File.separator + configName + ".json";
|
||||
|
||||
String content = Backend.instance().readFromFile(path);
|
||||
pkt = packetUploadConfig(configName, content);
|
||||
} else { // Error
|
||||
pkt = packetError("Remote config not found", "The requested config " + configName + " does not exist over here.");
|
||||
}
|
||||
|
||||
if (hostIsAConnection) {
|
||||
Backend.instance().tacnet().puts(pkt.toString());
|
||||
} else {
|
||||
Backend.instance().getServer().getActiveClient().puts(pkt.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: reply with config_name,456|other_config,10 (config name,revision)
|
||||
private void handleListConfigs(Packet packet) {
|
||||
if (hostIsAConnection) {
|
||||
final String[] remoteConfigs = packet.getContent().split("\\" + Packet.PROTOCOL_SEPERATOR);
|
||||
ArrayList<String> diff = Backend.instance().configsList();
|
||||
|
||||
for (String part : remoteConfigs) {
|
||||
final String[] configInfo = part.split(",", 2);
|
||||
final String name = configInfo[0];
|
||||
final int revision = Integer.parseInt(configInfo[1]);
|
||||
|
||||
diff.remove(name);
|
||||
|
||||
File file = new File(Backend.instance().configPath(name));
|
||||
|
||||
if (file.exists()) {
|
||||
final Config config = Backend.instance().loadConfigWithoutMutatingBackend(name);
|
||||
|
||||
if (config.getConfiguration().revision < revision) {
|
||||
Log.i(TAG, "handleListConfigs: requesting config: " + name + " since local " + config.getName() + " is @ " + config.getConfiguration().revision);
|
||||
Backend.instance().tacnet().puts(PacketHandler.packetDownloadConfig(name).toString());
|
||||
} else if (config.getConfiguration().revision > revision) {
|
||||
Log.i(TAG, "handleListConfigs: sending config: " + name + " since local " + config.getName() + " is @ " + config.getConfiguration().revision);
|
||||
Backend.instance().tacnet().puts(PacketHandler.packetUploadConfig(name, Backend.instance().gsonForConfig().toJson(config)).toString());
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "handleListConfigs: requesting config: " + name + " since there is no local file with that name");
|
||||
|
||||
Backend.instance().tacnet().puts( PacketHandler.packetDownloadConfig(name).toString() );
|
||||
}
|
||||
}
|
||||
|
||||
for (String name : diff) {
|
||||
final Config config = Backend.instance().loadConfigWithoutMutatingBackend(name);
|
||||
|
||||
Backend.instance().tacnet().puts(PacketHandler.packetUploadConfig(name, Backend.instance().gsonForConfig().toJson(config)).toString());
|
||||
}
|
||||
|
||||
} else {
|
||||
Backend.instance().getServer().getActiveClient().puts(PacketHandler.packetListConfigs().toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSelectConfig(Packet packet) {
|
||||
final String configName = packet.getContent();
|
||||
|
||||
Backend.instance().getSettings().config = configName;
|
||||
Backend.instance().saveSettings();
|
||||
Backend.instance().loadConfig(configName);
|
||||
}
|
||||
|
||||
private void handleAddConfig(Packet packet) {
|
||||
final String configName = packet.getContent();
|
||||
|
||||
if (Backend.instance().configsList().contains(configName)) {
|
||||
if (!hostIsAConnection) {
|
||||
Backend.instance().getServer().getActiveClient().puts(
|
||||
packetError("Config already exists!", "A config with the name " +
|
||||
configName + " already exists here.").toString()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Backend.instance().writeNewConfig(configName);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUpdateConfig(Packet packet) {
|
||||
final String[] split = packet.getContent().split("\\" + Packet.PROTOCOL_SEPERATOR, 2);
|
||||
final String oldConfigName = split[0];
|
||||
final String newConfigName = split[1];
|
||||
|
||||
if (Backend.instance().configsList().contains(newConfigName)) {
|
||||
if (!hostIsAConnection) {
|
||||
Backend.instance().getServer().getActiveClient().puts(
|
||||
packetError("Config already exists!", "A config with the name " +
|
||||
newConfigName + " already exists here.").toString()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Backend.instance().moveConfig(oldConfigName, newConfigName);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeleteConfig(Packet packet) {
|
||||
final String configName = packet.getContent();
|
||||
|
||||
Backend.instance().deleteConfig(configName);
|
||||
}
|
||||
|
||||
private void handleAddGroup(Packet packet) {
|
||||
final String[] split = packet.getContent().split("\\" + Packet.PROTOCOL_SEPERATOR, 2);
|
||||
final String configName = packet.getContent();
|
||||
final String groupName = packet.getContent();
|
||||
|
||||
if (Backend.instance().getConfig().getName().equals(configName)) {
|
||||
if (Group.nameIsUnique(Backend.instance().getConfig().getGroups(), groupName)) {
|
||||
Group group = new Group(groupName, new ArrayList<Action>());
|
||||
Backend.instance().getConfig().getGroups().add(group);
|
||||
} else {
|
||||
Backend.instance().getServer().getActiveClient().puts(
|
||||
packetError("Group name collision", "A group with the name " + groupName + " already exists").toString()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Backend.instance().getServer().getActiveClient().puts(
|
||||
packetError("Active config mismatch", "Active config is not " + configName).toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUpdateGroup(Packet packet) {}
|
||||
|
||||
private void handleDeleteGroup(Packet packet) {}
|
||||
|
||||
private void handleAddAction(Packet packet) {}
|
||||
|
||||
private void handleChangeAction(Packet packet) {
|
||||
// TODO: Handle renaming action and updating comment.
|
||||
}
|
||||
|
||||
private void handleDeleteAction(Packet packet) {}
|
||||
|
||||
private void handleAddVariable(Packet packet) {}
|
||||
|
||||
private void handleUpdateVariable(Packet packet) {}
|
||||
|
||||
private void handleChangeVariable(Packet packet) {}
|
||||
|
||||
private void handleDeleteVariable(Packet packet) {}
|
||||
|
||||
/**************************************
|
||||
PACKET HELPER FUNCTIONS
|
||||
**************************************/
|
||||
|
||||
static public Packet packetHandShake(String clientUUID) {
|
||||
return Packet.create(Packet.PacketType.HANDSHAKE, clientUUID);
|
||||
}
|
||||
|
||||
static public Packet packetHeartBeat() {
|
||||
return Packet.create(Packet.PacketType.HEARTBEAT, Packet.PROTOCOL_HEARTBEAT);
|
||||
}
|
||||
|
||||
static private Packet packetError(String errorTitle, String errorMessage) {
|
||||
return Packet.create(Packet.PacketType.ERROR, errorTitle + Packet.PROTOCOL_SEPERATOR + errorMessage);
|
||||
}
|
||||
|
||||
static public Packet packetUploadConfig(String configName, String json) {
|
||||
return Packet.create(Packet.PacketType.UPLOAD_CONFIG, configName + Packet.PROTOCOL_SEPERATOR + json);
|
||||
}
|
||||
|
||||
static public Packet packetDownloadConfig(String configName) {
|
||||
return Packet.create(Packet.PacketType.DOWNLOAD_CONFIG, configName);
|
||||
}
|
||||
|
||||
static public Packet packetListConfigs() {
|
||||
String configsList = "";
|
||||
final ArrayList<String> configs = Backend.instance().configsList();
|
||||
|
||||
int i = 0;
|
||||
for (final String configName : configs) {
|
||||
final String path = Backend.instance().configPath(configName);
|
||||
Config config = Backend.instance().gsonForConfig().fromJson(Backend.instance().readFromFile(path), Config.class);
|
||||
|
||||
configsList += configName + "," + config.getConfiguration().revision;
|
||||
|
||||
if (i != configs.size() - 1) {
|
||||
configsList += Packet.PROTOCOL_SEPERATOR;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return Packet.create(Packet.PacketType.LIST_CONFIGS, configsList);
|
||||
}
|
||||
|
||||
static public Packet packetSelectConfig(String configName) {
|
||||
return Packet.create(Packet.PacketType.SELECT_CONFIG, configName);
|
||||
}
|
||||
|
||||
static public Packet packetAddConfig(String configName) {
|
||||
return Packet.create(Packet.PacketType.ADD_CONFIG, configName);
|
||||
}
|
||||
|
||||
static public Packet packetUpdateConfig(String oldConfigName, String newConfigName) {
|
||||
return Packet.create(Packet.PacketType.UPDATE_CONFIG, oldConfigName + Packet.PROTOCOL_SEPERATOR + newConfigName);
|
||||
}
|
||||
|
||||
static public Packet packetDeleteConfig(String configName) {
|
||||
return Packet.create(Packet.PacketType.DELETE_CONFIG, configName);
|
||||
}
|
||||
|
||||
static public Packet packetAddGroup(String configName, String groupName) {
|
||||
return Packet.create(Packet.PacketType.ADD_GROUP, configName + Packet.PROTOCOL_SEPERATOR + groupName);
|
||||
}
|
||||
|
||||
static public Packet packetUpdateGroup(String configName, String oldGroupName, String newGroupName) {
|
||||
return Packet.create(Packet.PacketType.UPDATE_GROUP, configName + Packet.PROTOCOL_SEPERATOR +
|
||||
oldGroupName + Packet.PROTOCOL_SEPERATOR + newGroupName);
|
||||
}
|
||||
|
||||
static public Packet packetDeleteGroup(String configName, String groupName) {
|
||||
return Packet.create(Packet.PacketType.DELETE_GROUP, configName + Packet.PROTOCOL_SEPERATOR + groupName);
|
||||
}
|
||||
|
||||
// TODO
|
||||
static public Packet packetAddAction(String configName, String groupName, String actionName) {
|
||||
return Packet.create(Packet.PacketType.ADD_ACTION, configName + Packet.PROTOCOL_SEPERATOR + groupName);
|
||||
}
|
||||
|
||||
// TODO
|
||||
static public Packet packetUpdateAction(String configName, String oldGroupName, String newGroupName) {
|
||||
return Packet.create(Packet.PacketType.UPDATE_ACTION, configName + Packet.PROTOCOL_SEPERATOR +
|
||||
oldGroupName + Packet.PROTOCOL_SEPERATOR + newGroupName);
|
||||
}
|
||||
|
||||
// TODO
|
||||
static public Packet packetDeleteAction(String configName, String groupName) {
|
||||
return Packet.create(Packet.PacketType.DELETE_ACTION, configName + Packet.PROTOCOL_SEPERATOR + groupName);
|
||||
}
|
||||
|
||||
// TODO
|
||||
static public Packet packetAddVariable(String configName, String groupName) {
|
||||
return Packet.create(Packet.PacketType.ADD_VARIABLE, configName + Packet.PROTOCOL_SEPERATOR + groupName);
|
||||
}
|
||||
|
||||
// TODO
|
||||
static public Packet packetUpdateVariable(String configName, String oldGroupName, String newGroupName) {
|
||||
return Packet.create(Packet.PacketType.UPDATE_VARIABLE, configName + Packet.PROTOCOL_SEPERATOR +
|
||||
oldGroupName + Packet.PROTOCOL_SEPERATOR + newGroupName);
|
||||
}
|
||||
|
||||
static public Packet packetDeleteVariable(String configName, String groupName, String actionName, String variableName) {
|
||||
return Packet.create(Packet.PacketType.DELETE_VARIABLE, configName + Packet.PROTOCOL_SEPERATOR + groupName +
|
||||
Packet.PROTOCOL_SEPERATOR + actionName +
|
||||
Packet.PROTOCOL_SEPERATOR + variableName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.tacnet;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.TACNET;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
|
||||
public class Server {
|
||||
private ServerSocket server;
|
||||
private int port;
|
||||
private Client activeClient;
|
||||
private long lastSyncTime = 0;
|
||||
private long syncInterval = TACNET.SYNC_INTERVAL;
|
||||
|
||||
private String TAG = "TACNET|Server";
|
||||
|
||||
private int packetsSent, packetsReceived, clientLastPacketsSent, clientLastPacketsReceived = 0;
|
||||
private long dataSent, dataReceived, clientLastDataSent, clientLastDataReceived = 0;
|
||||
|
||||
private Runnable handleClientRunner;
|
||||
private PacketHandler packetHandler;
|
||||
|
||||
private long lastHeartBeatSent = 0;
|
||||
private long heartBeatInterval = TACNET.HEARTBEAT_INTERVAL;
|
||||
|
||||
public Server(int port) throws IOException {
|
||||
this.server = new ServerSocket();
|
||||
this.port = port;
|
||||
this.packetHandler = new PacketHandler(false);
|
||||
this.handleClientRunner = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleClient();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int connectionAttempts = 0;
|
||||
|
||||
while(!server.isBound() && connectionAttempts < 10) {
|
||||
try {
|
||||
server.bind(new InetSocketAddress(port));
|
||||
Log.i(TAG, "Server bound and ready!");
|
||||
} catch (IOException e) {
|
||||
connectionAttempts++;
|
||||
Log.e(TAG, "Server failed to bind: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
while (!server.isClosed()) {
|
||||
try {
|
||||
runServer();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error running server: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void runServer() throws IOException {
|
||||
while (!isClosed()) {
|
||||
|
||||
final Client client = new Client();
|
||||
client.setSyncInterval(syncInterval);
|
||||
client.setSocket(this.server.accept());
|
||||
|
||||
if (activeClient != null && !activeClient.isClosed()) {
|
||||
Log.i(TAG, "Too many clients, already have one connected!");
|
||||
client.close("Too many clients!");
|
||||
|
||||
} else {
|
||||
this.activeClient = client;
|
||||
|
||||
activeClient.puts(PacketHandler.packetHandShake( activeClient.uuid() ).toString());
|
||||
activeClient.puts(PacketHandler.packetListConfigs().toString());
|
||||
|
||||
Log.i(TAG, "Client connected!");
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while(activeClient != null && !activeClient.isClosed()) {
|
||||
if (System.currentTimeMillis() > lastSyncTime + syncInterval) {
|
||||
lastSyncTime = System.currentTimeMillis();
|
||||
|
||||
activeClient.sync(handleClientRunner);
|
||||
updateNetStats();
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(syncInterval);
|
||||
} catch (InterruptedException e) {
|
||||
// Failed to sleep, i guess.
|
||||
}
|
||||
}
|
||||
|
||||
updateNetStats();
|
||||
activeClient = null;
|
||||
|
||||
clientLastPacketsSent = 0;
|
||||
clientLastPacketsReceived = 0;
|
||||
clientLastDataSent = 0;
|
||||
clientLastDataReceived = 0;
|
||||
|
||||
// AppSync.getMainActivity().clientDisconnected();
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleClient() {
|
||||
if (activeClient != null && !activeClient.isClosed()) {
|
||||
String message = activeClient.gets();
|
||||
|
||||
if (message != null) {
|
||||
packetHandler.handle(message);
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() > lastHeartBeatSent + heartBeatInterval) {
|
||||
lastHeartBeatSent = System.currentTimeMillis();
|
||||
|
||||
activeClient.puts(PacketHandler.packetHeartBeat().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() throws IOException {
|
||||
if (this.activeClient != null) {
|
||||
this.activeClient.close();
|
||||
this.activeClient = null;
|
||||
}
|
||||
|
||||
this.server.close();
|
||||
}
|
||||
|
||||
public boolean hasActiveClient() {
|
||||
return activeClient != null;
|
||||
}
|
||||
|
||||
public Client getActiveClient() {
|
||||
return activeClient;
|
||||
}
|
||||
|
||||
public int getPacketsSent() {
|
||||
return packetsSent;
|
||||
}
|
||||
|
||||
public int getPacketsReceived() {
|
||||
return packetsReceived;
|
||||
}
|
||||
|
||||
public long getDataSent() {
|
||||
return dataSent;
|
||||
}
|
||||
|
||||
public long getDataReceived() {
|
||||
return dataReceived;
|
||||
}
|
||||
|
||||
private void updateNetStats() {
|
||||
if (activeClient != null) {
|
||||
// NOTE: In and Out are reversed for Server stats
|
||||
|
||||
packetsSent += activeClient.getPacketsReceived() - clientLastPacketsReceived;
|
||||
packetsReceived += activeClient.getPacketsSent() - clientLastPacketsSent;
|
||||
|
||||
dataSent += activeClient.getDataReceived() - clientLastDataReceived;
|
||||
dataReceived += activeClient.getDataSent() - clientLastDataSent;
|
||||
|
||||
clientLastPacketsSent = activeClient.getPacketsSent();
|
||||
clientLastPacketsReceived = activeClient.getPacketsReceived();
|
||||
clientLastDataSent = activeClient.getDataSent();
|
||||
clientLastDataReceived = activeClient.getDataReceived();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBound() {
|
||||
return this.server.isBound();
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return this.server.isClosed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user