From e944332b682dd627f136cb41a517856e89a6f9cb Mon Sep 17 00:00:00 2001 From: cyberarm Date: Tue, 25 Aug 2020 20:19:17 -0500 Subject: [PATCH] Added TACNET Connection Status 'fragment', misc. --- .../backend/Backend.java | 1 + .../backend/TACNET.java | 28 ++-- .../dialogs/ServerDialog.java | 1 - .../tacnet/Client.java | 18 ++- .../tacnet/Connection.java | 8 +- .../tacnet/Packet.java | 4 + .../support/ConnectionStatsSyncHandler.java | 94 ++++++++++++ ...TFragment.java => TACNETHostFragment.java} | 72 +++++++-- .../ui/tacnet/TACNETViewModel.java | 19 --- app/src/main/res/layout/fragment_tacnet.xml | 4 +- .../fragment_tacnet_connection_status.xml | 139 ++++++++++++++++++ .../main/res/layout/fragment_tacnet_host.xml | 8 + .../main/res/navigation/mobile_navigation.xml | 16 +- app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/strings.xml | 8 +- app/src/main/res/values/styles.xml | 2 +- 16 files changed, 362 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/support/ConnectionStatsSyncHandler.java rename app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/{TACNETFragment.java => TACNETHostFragment.java} (54%) delete mode 100644 app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETViewModel.java create mode 100644 app/src/main/res/layout/fragment_tacnet_connection_status.xml create mode 100644 app/src/main/res/layout/fragment_tacnet_host.xml 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 6432e0a..b81142a 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Backend.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Backend.java @@ -111,6 +111,7 @@ public class Backend { if (server != null) { try { server.stop(); + server = null; } catch (IOException error) { lastServerError = error; } diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TACNET.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TACNET.java index 333ca60..f1356ee 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TACNET.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TACNET.java @@ -31,29 +31,23 @@ public class TACNET { } connection = new Connection(hostname, port); - Backend.instance().stopErrorSound(); connection.connect(new Runnable() { @Override public void run() { - Log.d(TAG, "run: " + connection.lastError()); + Log.d(TAG, "run: " + connection.lastSocketError()); Backend.instance().startErrorSound(Backend.instance().applicationContext); - try { - connection.close(); - } catch (IOException e) { - e.printStackTrace(); - } } }); } public Status status() { - if (isConnected()) { - return Status.CONNECTED; - } else if (connection != null && !connection.socketError()) { + if (isConnecting()) { return Status.CONNECTING; - } else if (connection != null && connection.socketError()) { + } else if (isConnectionError()) { return Status.CONNECTION_ERROR; + } else if (isConnected()) { + return Status.CONNECTED; } else { return Status.NOT_CONNECTED; } @@ -63,6 +57,14 @@ public class TACNET { 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 { @@ -81,6 +83,10 @@ public class TACNET { } } + public Connection getConnection() { + return connection; + } + public void puts(String message) { if (isConnected()) { connection.puts(message); 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 9a21764..6acbb21 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java @@ -38,7 +38,6 @@ public class ServerDialog extends TimeCraftersDialog { stopServer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - // TODO: Halt server Backend.instance().stopServer(); dismiss(); } diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Client.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Client.java index bc8e538..7dea793 100755 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Client.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Client.java @@ -36,6 +36,9 @@ public class Client { private String TAG = "TACNET|Client"; + private boolean socketError = false; + private String lastSocketError; + public Client() { this.uuid = (UUID.randomUUID()).toString(); @@ -90,7 +93,7 @@ public class Client { } } catch (IOException e) { - Log.e(TAG, "Read error: " + e.getMessage()); + Log.e(TAG, "Read error: " + e.getLocalizedMessage()); } try { @@ -123,11 +126,13 @@ public class Client { itr.remove(); } catch (IOException e) { - Log.e(TAG, "Write error: " + e.getMessage()); + 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.getMessage()); + Log.e(TAG, "Failed to close socket: " + e.getLocalizedMessage()); } } } @@ -223,6 +228,13 @@ public class Client { 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(); } diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Connection.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Connection.java index dd66b23..11f0742 100755 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Connection.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Connection.java @@ -60,7 +60,7 @@ public class Connection { } } catch (IOException e) { socketError = true; - lastSocketError = e.getMessage(); + lastSocketError = e.getLocalizedMessage(); errorCallback.run(); @@ -114,10 +114,10 @@ public class Connection { return this.client != null && this.client.isConnected(); } public boolean socketError() { - return socketError; + return socketError ? socketError : client.socketError(); } - public String lastError() { - return lastSocketError; + public String lastSocketError() { + return lastSocketError != null ? lastSocketError : client.lastSocketError(); } public void close() throws IOException { 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 511e0c3..c07c2e9 100755 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Packet.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/Packet.java @@ -20,6 +20,10 @@ public class Packet { DOWNLOAD_CONFIG(10), UPLOAD_CONFIG(11), + LIST_CONFIGS(12), + ADD_CONFIG(13), + UPDATE_CONFIG(14), + DELETE_CONFIG(15), ADD_GROUP(20), UPDATE_GROUP(21), diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/support/ConnectionStatsSyncHandler.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/support/ConnectionStatsSyncHandler.java new file mode 100644 index 0000000..be7ecfa --- /dev/null +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/support/ConnectionStatsSyncHandler.java @@ -0,0 +1,94 @@ +package org.timecrafters.TimeCraftersConfigurationTool.tacnet.support; + +import android.os.Handler; +import android.os.Looper; +import android.view.View; +import android.widget.TextView; + +import org.timecrafters.TimeCraftersConfigurationTool.R; +import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend; +import org.timecrafters.TimeCraftersConfigurationTool.backend.TACNET; +import org.timecrafters.TimeCraftersConfigurationTool.tacnet.Client; + +public class ConnectionStatsSyncHandler { + private View view; + private Handler handler; + private Runnable runner; + private long delay; + private boolean stopped = false; + private TextView connectionStatus, totalPacketsIn, totalPacketsOut, totalDataIn, totalDataOut; + + public ConnectionStatsSyncHandler(View view, long delay) { + this.view = view; + this.delay = delay; + connectionStatus= view.findViewById(R.id.connection_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() { + ConnectionStatsSyncHandler.this.run(); + } + }; + + handler.postDelayed(runner, 0); + } + + public void run() { + Client client = Backend.instance().tacnet().getClient(); + + if (!stopped && client != null) { + TACNET.Status status = Backend.instance().tacnet().status(); + switch(status) { + case CONNECTED: { + connectionStatus.setText(R.string.tacnet_connected); + break; + } + case CONNECTING: { + connectionStatus.setText(R.string.tacnet_connecting); + break; + } + case NOT_CONNECTED: { + connectionStatus.setText(R.string.tacnet_not_connected); + break; + } + case CONNECTION_ERROR: { + connectionStatus.setText(Backend.instance().tacnet().getConnection().lastSocketError()); + break; + } + } + + if (Backend.instance().tacnet().isConnectionError()) { + connectionStatus.setTextColor(view.getResources().getColor(R.color.buttonDangerActive)); + } else { + connectionStatus.setTextColor(view.getResources().getColor(R.color.text_color)); + } + + totalPacketsIn.setText("" + client.getPacketsSent()); + totalPacketsOut.setText("" + client.getPacketsReceived()); + totalDataIn.setText("" + client.getDataSent() + " bytes"); + totalDataOut.setText("" + client.getDataReceived() + " bytes"); + + handler.postDelayed(runner, delay); + + } else if (!stopped && client == null) { + if (Backend.instance().tacnet().isConnectionError()) { + connectionStatus.setTextColor(view.getResources().getColor(R.color.buttonDangerActive)); + } else { + connectionStatus.setTextColor(view.getResources().getColor(R.color.text_color)); + } + + connectionStatus.setText(Backend.instance().tacnet().getConnection().lastSocketError()); + + handler.postDelayed(runner, delay); + } + } + + public void stop() { + stopped = true; + } +} diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETFragment.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETHostFragment.java similarity index 54% rename from app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETFragment.java rename to app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETHostFragment.java index 66beb7c..ad57919 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETFragment.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETHostFragment.java @@ -3,32 +3,57 @@ package org.timecrafters.TimeCraftersConfigurationTool.ui.tacnet; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; +import android.widget.LinearLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; import org.timecrafters.TimeCraftersConfigurationTool.R; import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend; +import org.timecrafters.TimeCraftersConfigurationTool.backend.TACNET; import org.timecrafters.TimeCraftersConfigurationTool.dialogs.ServerDialog; import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersFragment; +import org.timecrafters.TimeCraftersConfigurationTool.tacnet.support.ConnectionStatsSyncHandler; +import org.timecrafters.TimeCraftersConfigurationTool.tacnet.support.ServerStatsSyncHandler; -public class TACNETFragment extends TimeCraftersFragment { +public class TACNETHostFragment extends TimeCraftersFragment { private static final String TAG = "TACNETFragment"; - private TACNETViewModel TACNETViewModel; + private ConnectionStatsSyncHandler connectionStatsSyncHandler; public View onCreateView(@NonNull LayoutInflater inflater, - ViewGroup container, Bundle savedInstanceState) { - TACNETViewModel = - ViewModelProviders.of(this).get(TACNETViewModel.class); - View root = inflater.inflate(R.layout.fragment_tacnet, container, false); + ViewGroup viewGroup, Bundle savedInstanceState) { + final View root = inflater.inflate(R.layout.fragment_tacnet_host, viewGroup, false); + final LinearLayout container = (LinearLayout) root; + + + if (Backend.instance().tacnet().status() != TACNET.Status.NOT_CONNECTED) { + inflateTACNETConnectionStatus(container); + } else { + inflateTACNET(container); + } + + return root; + } + + private void inflateTACNET(final LinearLayout container) { + ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.title_tacnet); + final ConstraintLayout root = (ConstraintLayout) getLayoutInflater().inflate(R.layout.fragment_tacnet, null); + container.removeAllViews(); + container.addView(root); + final EditText hostname = root.findViewById(R.id.hostname); final EditText port = root.findViewById(R.id.port); @@ -69,11 +94,12 @@ public class TACNETFragment extends TimeCraftersFragment { connectButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { -// ConnectDialog dialog = new ConnectDialog(); -// dialog.show(getFragmentManager(), null); Backend.instance().saveSettings(); Backend.instance().tacnet().connect(hostname.getText().toString(), Integer.parseInt(port.getText().toString())); + + root.removeAllViews(); + inflateTACNETConnectionStatus(container); } }); @@ -84,13 +110,37 @@ public class TACNETFragment extends TimeCraftersFragment { dialog.show(getFragmentManager(), null); } }); + } - TACNETViewModel.getText().observe(getViewLifecycleOwner(), new Observer() { + private void inflateTACNETConnectionStatus(final LinearLayout container) { + ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.title_tacnet_connection_status); + final ConstraintLayout root = (ConstraintLayout) getLayoutInflater().inflate(R.layout.fragment_tacnet_connection_status, null); + container.removeAllViews(); + container.addView(root); + + connectionStatsSyncHandler = new ConnectionStatsSyncHandler(root, 1_000); + + Button disconnect = root.findViewById(R.id.tacnet_disconnect); + disconnect.setOnClickListener(new View.OnClickListener() { @Override - public void onChanged(@Nullable String s) { + public void onClick(View v) { + Backend.instance().tacnet().close(); + Backend.instance().stopErrorSound(); + connectionStatsSyncHandler.stop(); + + inflateTACNET(container); } }); + } - return root; + @Override + public void onDetach() { + super.onDetach(); + + if (connectionStatsSyncHandler != null) { + Log.d(TAG, "onDetach: stopping sync handler"); + connectionStatsSyncHandler.stop(); + connectionStatsSyncHandler = null; + } } } \ No newline at end of file diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETViewModel.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETViewModel.java deleted file mode 100644 index 5f721e6..0000000 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETViewModel.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.timecrafters.TimeCraftersConfigurationTool.ui.tacnet; - -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; - -public class TACNETViewModel extends ViewModel { - - private MutableLiveData mText; - - public TACNETViewModel() { - mText = new MutableLiveData<>(); - mText.setValue("This is TACNET fragment"); - } - - public LiveData getText() { - return mText; - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_tacnet.xml b/app/src/main/res/layout/fragment_tacnet.xml index bc0a278..2e5cb3c 100644 --- a/app/src/main/res/layout/fragment_tacnet.xml +++ b/app/src/main/res/layout/fragment_tacnet.xml @@ -4,8 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#FFFFFF" - tools:context=".ui.tacnet.TACNETFragment"> + tools:context=".ui.tacnet.TACNETHostFragment"> diff --git a/app/src/main/res/layout/fragment_tacnet_connection_status.xml b/app/src/main/res/layout/fragment_tacnet_connection_status.xml new file mode 100644 index 0000000..66c0a5e --- /dev/null +++ b/app/src/main/res/layout/fragment_tacnet_connection_status.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +