Added TACNET Connection Status 'fragment', misc.

This commit is contained in:
2020-08-25 20:19:17 -05:00
parent 1a2ee767c2
commit e944332b68
16 changed files with 362 additions and 62 deletions

View File

@@ -111,6 +111,7 @@ public class Backend {
if (server != null) {
try {
server.stop();
server = null;
} catch (IOException error) {
lastServerError = error;
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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 {

View File

@@ -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),

View File

@@ -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;
}
}

View File

@@ -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<String>() {
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;
}
}
}

View File

@@ -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<String> mText;
public TACNETViewModel() {
mText = new MutableLiveData<>();
mText.setValue("This is TACNET fragment");
}
public LiveData<String> getText() {
return mText;
}
}

View File

@@ -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">
<ScrollView
android:layout_width="match_parent"
@@ -74,6 +73,7 @@
android:id="@+id/tacnet_connect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dialog_button_margin"
android:text="@string/tacnet_connect" />
</LinearLayout>

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/tacnet_connect_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="48dp"
android:layout_marginTop="24dp"
android:layout_marginRight="48dp"
android:layout_marginBottom="24dp"
android:background="@color/list_even"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/button_margin_left"
android:layout_marginRight="@dimen/button_margin_right"
android:orientation="horizontal">
<!-- labels -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Connection Status"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Total Packets In"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Total Packets Out"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Total Data In"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Total Data Out"
android:textStyle="bold" />
</LinearLayout>
<!-- values -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/button_margin_left"
android:orientation="vertical">
<TextView
android:id="@+id/connection_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0" />
<TextView
android:id="@+id/total_packets_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0" />
<TextView
android:id="@+id/total_packets_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0" />
<TextView
android:id="@+id/total_data_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0" />
<TextView
android:id="@+id/total_data_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0" />
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/tacnet_disconnect"
style="@style/DangerousButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dialog_button_margin"
android:text="@string/tacnet_disconnect" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>

View File

@@ -3,11 +3,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mobile_navigation"
app:startDestination="@+id/navigation_editor">
app:startDestination="@id/navigation_editor">
<fragment
android:id="@+id/navigation_tacnet"
android:name="org.timecrafters.TimeCraftersConfigurationTool.ui.tacnet.TACNETFragment"
android:name="org.timecrafters.TimeCraftersConfigurationTool.ui.tacnet.TACNETHostFragment"
android:label="@string/title_tacnet"
tools:layout="@layout/fragment_tacnet" />
@@ -28,10 +28,10 @@
tools:layout="@layout/fragment_settings" >
<action
android:id="@+id/action_navigation_settings_to_configurationsFragment"
app:destination="@id/configurationsFragment" />
app:destination="@id/configurations_fragment" />
<action
android:id="@+id/action_navigation_settings_to_presetsFragment"
app:destination="@id/presetsFragment" />
app:destination="@id/presets_fragment" />
</fragment>
<fragment
android:id="@+id/navigation_search"
@@ -51,11 +51,11 @@
android:name="org.timecrafters.TimeCraftersConfigurationTool.ui.editor.VariablesFragment"
android:label="Variables" />
<fragment
android:id="@+id/configurationsFragment"
android:id="@+id/configurations_fragment"
android:name="org.timecrafters.TimeCraftersConfigurationTool.ui.settings.configurations.ConfigurationsFragment"
android:label="Manage Configurations" />
android:label="@string/settings_manage_configurations" />
<fragment
android:id="@+id/presetsFragment"
android:id="@+id/presets_fragment"
android:name="org.timecrafters.TimeCraftersConfigurationTool.ui.settings.presets.PresetsFragment"
android:label="Manage Presets" />
android:label="@string/settings_manage_presets" />
</navigation>

View File

@@ -6,6 +6,8 @@
<color name="colorSecondary">#006000</color>
<color name="colorTertiary">#00d000</color>
<color name="text_color">#fff</color>
<color name="colorDanger">#800</color>
<color name="buttonDangerHover">#600</color>
<color name="buttonDangerActive">#c00</color>

View File

@@ -1,10 +1,10 @@
<resources>
<string name="app_name">TimeCraftersConfigurationTool</string>
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="app_name">TimeCrafters Configuration Tool</string>
<string name="title_editor">Editor</string>
<string name="title_search">Search</string>
<string name="title_settings">Settings</string>
<string name="title_tacnet">TACNET</string>
<string name="title_tacnet_connection_status">TACNET Connection Status</string>
<string name="tacnet_hostname">Hostname</string>
<string name="tacnet_port">Port</string>
@@ -36,4 +36,8 @@
<string name="warning_no_config_active">No active configuration set</string>
<string name="tacnet_stop_server">Stop Server</string>
<string name="tacnet_server_status">Server Status</string>
<string name="tacnet_disconnect">Disconnect</string>
<string name="tacnet_connected">Connected</string>
<string name="tacnet_connecting">Connecting...</string>
<string name="tacnet_not_connected">Not Connected</string>
</resources>

View File

@@ -6,7 +6,7 @@
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColor">#fff</item>
<item name="android:textColor">@color/text_color</item>
<item name="android:fontFamily">@font/dejavusans_condensed_fontfamily</item>
<item name="buttonStyle">@style/Button</item>