diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java index faf3f9a..5bf9e77 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java @@ -41,7 +41,9 @@ public class MainActivity extends AppCompatActivity { if (!havePermissions()) { new PermissionsRequestDialog().show(getSupportFragmentManager(), null); } else { - new Backend(); + if (Backend.instance() == null) { + new Backend(); + } } } diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Backend.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Backend.java index 449f5c8..828cc96 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Backend.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Backend.java @@ -11,6 +11,7 @@ import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Preset; import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsDeserializer; import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsSerializer; +import org.timecrafters.TimeCraftersConfigurationTool.tacnet.Server; import java.io.BufferedInputStream; import java.io.BufferedReader; @@ -32,18 +33,24 @@ public class Backend { private static final String TAG = "Backend"; static private Backend instance; private TACNET tacnet; + private Server server; + private Exception lastServerError; private Config config; private Settings settings; private boolean configChanged, settingsChanged; public Backend() { - instance = this; + if (Backend.instance() != null) { + throw(new RuntimeException("Backend instance already exists!")); + } else { + instance = this; + } loadSettings(); if (!settings.config.isEmpty()) { loadConfig(settings.config); } else { - config = new Config(); + config = new Config("DEBUG DEBUG DEBUG"); } tacnet = new TACNET(); @@ -59,6 +66,33 @@ public class Backend { 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(); + } catch (IOException error) { + lastServerError = error; + } + } + } + + public Exception getLastServerError() { + return lastServerError; + } + public Config getConfig() { return config; } @@ -76,7 +110,7 @@ public class Backend { public boolean isConfigChanged() { return configChanged; } public void loadConfig(String name) { - String path = "" + TAC.CONFIGS_PATH + File.separator + name; + String path = "" + TAC.CONFIGS_PATH + File.separator + name + ".json"; File file = new File(path); if (file.exists() && file.isFile()) { @@ -104,13 +138,10 @@ public class Backend { } public void writeNewConfig(String name) { - String path = "" + TAC.CONFIGS_PATH + File.separator + name; + String path = TAC.CONFIGS_PATH + File.separator + name + ".json"; File file = new File(path); - Configuration configuration = new Configuration(new Date(), new Date(), TAC.CONFIG_SPEC_VERSION, 0); - ArrayList groups = new ArrayList<>(); - ArrayList presets = new ArrayList<>(); - Config config = new Config(configuration, groups, presets); + Config config = new Config(name); Gson gson = new Gson(); @@ -136,7 +167,6 @@ public class Backend { }; File fileList[] = directory.listFiles(filter); for (File file : fileList) { - Log.d(TAG, "configsList: " + file.getName()); list.add(file.getName()); } @@ -144,7 +174,7 @@ public class Backend { } // TODO: Write De/serializers for config - private Gson gsonForConfig() { + public Gson gsonForConfig() { // return new GsonBuilder() // .registerTypeAdapter(Config.class, new ConfigSerializer()) // .registerTypeAdapter(COnfig.class, new ConfigDeserializer()) @@ -186,14 +216,14 @@ public class Backend { saveSettings(); } - private Gson gsonForSettings() { + public Gson gsonForSettings() { return new GsonBuilder() .registerTypeAdapter(Settings.class, new SettingsSerializer()) .registerTypeAdapter(Settings.class, new SettingsDeserializer()) .create(); } - protected String readFromFile(String path) { + public String readFromFile(String path) { StringBuilder text = new StringBuilder(); try { @@ -213,7 +243,7 @@ public class Backend { return text.toString(); } - protected boolean writeToFile(String filePath, String content) { + public boolean writeToFile(String filePath, String content) { try { if (filePath.startsWith(TAC.ROOT_PATH)) { createFolders(filePath); diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Config.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Config.java index 5fabe53..d8898e1 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Config.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Config.java @@ -17,10 +17,9 @@ public class Config { private ArrayList groups; private ArrayList presets; - // TODO: DELETE ME - public Config() { - this.name = "DEBUG ONLY"; - this.configuration = new Configuration(new Date(), new Date(), 0, 32); + public Config(String name) { + this.name = name; + this.configuration = new Configuration(new Date(), new Date(), TAC.CONFIG_SPEC_VERSION, 32); groups = new ArrayList<>(); presets = new ArrayList<>(); } diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java index 48975f8..0052add 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java @@ -13,18 +13,24 @@ import androidx.constraintlayout.widget.ConstraintLayout; import org.timecrafters.TimeCraftersConfigurationTool.R; import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend; import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersDialog; +import org.timecrafters.TimeCraftersConfigurationTool.tacnet.support.ServerStatsSyncHandler; public class ServerDialog extends TimeCraftersDialog { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setCancelable(false); - View root = super.onCreateView(inflater, container, savedInstanceState); + final View root = super.onCreateView(inflater, container, savedInstanceState); + if (Backend.instance().getServer() == null) { + Backend.instance().startServer(); + } + final TextView title = root.findViewById(R.id.dialogTitle); final ConstraintLayout titlebar = root.findViewById(R.id.titlebar); final LinearLayout view = root.findViewById(R.id.dialogContent); view.addView(getLayoutInflater().inflate(R.layout.dialog_server, null)); + new ServerStatsSyncHandler(view, 1_000); title.setText(getResources().getString(R.string.tacnet_server_status)); @@ -33,6 +39,7 @@ public class ServerDialog extends TimeCraftersDialog { @Override public void onClick(View v) { // TODO: Halt server + Backend.instance().stopServer(); dismiss(); } }); diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Packet.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Packet.java index 413d021..511e0c3 100755 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Packet.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Packet.java @@ -5,7 +5,7 @@ import android.util.Log; import java.util.Arrays; public class Packet { - final static public String PROTOCOL_VERSION = "0"; + final static public String PROTOCOL_VERSION = "1"; final static public String PROTOCOL_HEADER_SEPERATOR = "|"; final static public String PROTOCOL_HEARTBEAT = "heartbeat"; private static final String TAG = "TACNET|Packet"; @@ -14,11 +14,33 @@ public class Packet { // NOTE: PacketType is cast to a char, no more than 255 packet types can exist unless // header is updated. public enum PacketType { - HANDSHAKE, - HEARTBEAT, - DUMP_CONFIG, - CHANGE_ACTION, - CHANGE_VARIABLE, + HANDSHAKE(0), + HEARTBEAT(1), + ERROR(2), + + DOWNLOAD_CONFIG(10), + UPLOAD_CONFIG(11), + + 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; @@ -37,7 +59,7 @@ public class Packet { static public Packet fromStream(String message) { String version; - PacketType type; + PacketType type = null; int length; String body; @@ -54,10 +76,22 @@ public class Packet { } version = slice[0]; - type = PacketType.values()[Integer.parseInt(slice[1])]; +// 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); } @@ -84,7 +118,7 @@ public class Packet { String string = ""; string += PROTOCOL_VERSION; string += PROTOCOL_HEADER_SEPERATOR; - string += packetType.ordinal(); + string += packetType.getId(); string += PROTOCOL_HEADER_SEPERATOR; string += contentLength; string += PROTOCOL_HEADER_SEPERATOR; diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/PacketHandler.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/PacketHandler.java index bd331f6..a0cfe9e 100755 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/PacketHandler.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/PacketHandler.java @@ -3,6 +3,9 @@ package org.timecrafters.TimeCraftersConfigurationTool.tacnet; import android.util.Log; import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend; +import org.timecrafters.TimeCraftersConfigurationTool.backend.TAC; + +import java.io.File; import java.lang.reflect.Array; import java.util.Arrays; @@ -41,16 +44,26 @@ public class PacketHandler { return; } - case DUMP_CONFIG: { - handleDumpConfig(packet); + case ERROR: { +// handleHeartBeat(packet); +// return; + } + + case DOWNLOAD_CONFIG: { + handleDownloadConfig(packet); return; } - case CHANGE_ACTION: { - handleChangeAction(packet); + case UPLOAD_CONFIG: { + handleUploadConfig(packet); return; } +// case CHANGE_ACTION: { +// handleChangeAction(packet); +// return; +// } + default: { return; } @@ -62,22 +75,42 @@ public class PacketHandler { // NO-OP private void handleHeartBeat(Packet packet) {} - private void handleDumpConfig(Packet packet) { - if ( - packet.getContent().length() > 4 && packet.getContent().charAt(0) == "[".toCharArray()[0] && - packet.getContent().charAt(packet.getContent().length() - 1) == "]".toCharArray()[0] - ) { /* "unless" keyword anyone? */ } else { return; } + private void handleUploadConfig(Packet packet) { + String[] split = packet.getContent().split("\\" + Packet.PROTOCOL_HEADER_SEPERATOR, 2); + final String configName = split[0]; + final String json = split[1]; + if (configName.length() == 0 && false) { //!Backend.instance().configIsValid(json)) { + return; + } + if (configName.length() == 0) { + return; + } + final String path = TAC.CONFIGS_PATH + File.separator + configName + ".json"; Log.i(TAG, "Got valid json: " + packet.getContent()); - if (hostIsAConnection) { - // save and reload menu -// Writer.overwriteConfigFile(packet.getContent()); + Backend.instance().writeToFile(path, json); + } -// Backend.instance().loadConfig(); + 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 + ".json")) { + final String path = TAC.CONFIGS_PATH + File.separator + configName + ".json"; + + String content = Backend.instance().readFromFile(path); + Packet.create(Packet.PacketType.UPLOAD_CONFIG, content); + } else { // Errored + final String content = "ERROR"; + Packet.create(Packet.PacketType.ERROR, content); + } + + if (hostIsAConnection) { + Backend.instance().tacnet().puts(packet.toString()); } else { - // save -// Writer.overwriteConfigFile(packet.getContent()); + Backend.instance().getServer().getActiveClient().puts(packet.toString()); } } @@ -97,9 +130,9 @@ public class PacketHandler { return Packet.create(Packet.PacketType.HEARTBEAT, Packet.PROTOCOL_HEARTBEAT); } - static public Packet packetDumpConfig(String string) { - string = string.replace("\n", " "); - - return Packet.create(Packet.PacketType.DUMP_CONFIG, string); - } +// static public Packet packetDumpConfig(String string) { +// string = string.replace("\n", " "); +// +// return Packet.create(Packet.PacketType.DUMP_CONFIG, string); +// } } diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/support/ServerStatsSyncHandler.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/support/ServerStatsSyncHandler.java new file mode 100644 index 0000000..869b2f0 --- /dev/null +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/support/ServerStatsSyncHandler.java @@ -0,0 +1,60 @@ +package org.timecrafters.TimeCraftersConfigurationTool.tacnet.support; + +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import org.timecrafters.TimeCraftersConfigurationTool.R; +import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend; +import org.timecrafters.TimeCraftersConfigurationTool.tacnet.Server; + +import java.util.TimerTask; + +public class ServerStatsSyncHandler { + private View view; + private Handler handler; + private Runnable runner; + private long delay; + private TextView clientStatus, totalPacketsIn, totalPacketsOut, totalDataIn, totalDataOut; + + public ServerStatsSyncHandler(View view, long delay) { + this.view = view; + this.delay = delay; + clientStatus = view.findViewById(R.id.client_status); + totalPacketsIn = view.findViewById(R.id.total_packets_in); + totalPacketsOut = view.findViewById(R.id.total_packets_out); + totalDataIn = view.findViewById(R.id.total_data_in); + totalDataOut = view.findViewById(R.id.total_data_out); + + handler = new Handler(Looper.getMainLooper()); + runner = new Runnable() { + @Override + public void run() { + ServerStatsSyncHandler.this.run(); + } + }; + + handler.postDelayed(runner, 0); + } + + public void run() { + Server server = Backend.instance().getServer(); + + if (server != null) { + if (server.hasActiveClient()) { + clientStatus.setText("Connected"); + } else { + clientStatus.setText("Disconnected"); + } + + totalPacketsIn.setText("" + server.getPacketsSent()); + totalPacketsOut.setText("" + server.getPacketsReceived()); + totalDataIn.setText("" + server.getDataSent() + " bytes"); + totalDataOut.setText("" + server.getDataReceived() + " bytes"); + + handler.postDelayed(runner, delay); + } + } +} diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/settings/configurations/ConfigurationsFragment.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/settings/configurations/ConfigurationsFragment.java index af065df..858b728 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/settings/configurations/ConfigurationsFragment.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/settings/configurations/ConfigurationsFragment.java @@ -34,11 +34,29 @@ public class ConfigurationsFragment extends TimeCraftersFragment { } }); - for (String config : Backend.instance().configsList()) { + int i = 0; + for (String configFile : Backend.instance().configsList()) { + final String config = configFile.replace(".json", ""); View view = inflater.inflate(R.layout.fragment_part_configuration, null); + + if (i % 2 == 0) { // even + view.setBackgroundColor(getResources().getColor(R.color.list_even)); + } else { + view.setBackgroundColor(getResources().getColor(R.color.list_odd)); + } + Button configName = view.findViewById(R.id.name); configName.setText(config); + configName.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Backend.instance().getSettings().config = config; + Backend.instance().loadConfig(config); + Backend.instance().saveSettings(); + } + }); + i++; configsContainer.addView(view); } diff --git a/app/src/main/res/layout/dialog_base.xml b/app/src/main/res/layout/dialog_base.xml index f2d976c..f6d94f1 100644 --- a/app/src/main/res/layout/dialog_base.xml +++ b/app/src/main/res/layout/dialog_base.xml @@ -40,12 +40,16 @@ app:layout_constraintTop_toTopOf="parent" /> - + android:layout_height="match_parent"> + + + - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_server.xml b/app/src/main/res/layout/dialog_server.xml index 9789c56..dd36e53 100644 --- a/app/src/main/res/layout/dialog_server.xml +++ b/app/src/main/res/layout/dialog_server.xml @@ -66,12 +66,12 @@ android:orientation="vertical"> + android:textColor="@color/dialogLabel" /> - - - - + android:textColor="@color/dialogLabel" /> + + + + +