Refactored server lifecycle so that it is hosted in a foreground service, removed ServerDialog and moved it's function into the TACNET fragment host, stubbed service for TACNET Connection

This commit is contained in:
2020-11-10 10:37:31 -06:00
parent e90478a117
commit a8ec2d0956
12 changed files with 479 additions and 285 deletions

View File

@@ -7,15 +7,25 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:requestLegacyExternalStorage="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".tacnet.TACNETServerService"
android:enabled="true"
android:exported="false"/>
<service
android:name=".tacnet.TACNETConnectionService"
android:enabled="true"
android:exported="false"/>
<activity
android:name=".LauncherActivity"
android:label="@string/app_name">
@@ -25,7 +35,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity"/>
<activity android:name=".MainActivity" />
</application>
</manifest>

View File

@@ -1,6 +1,11 @@
package org.timecrafters.TimeCraftersConfigurationTool;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import com.google.android.material.bottomnavigation.BottomNavigationView;
@@ -11,9 +16,12 @@ import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
import org.timecrafters.TimeCraftersConfigurationTool.backend.TAC;
import org.timecrafters.TimeCraftersConfigurationTool.tacnet.TACNETServerService;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final String AUTO_START_MODEL = "pixel"; // "rev hub" /* LOWERCASE */
private AppBarConfiguration appBarConfiguration;
@Override
@@ -37,6 +45,18 @@ public class MainActivity extends AppCompatActivity {
}
Backend.instance().applicationContext = getApplicationContext();
// Auto start TACNET server if allowed and device model contains AUTO_START_MODEL
if (!TAC.COMPETITION_MODE && Backend.instance().getServer() == null && Build.MODEL.toLowerCase().contains(AUTO_START_MODEL)) {
Log.i(TAG, "Detected REV Robotics Control Hub, attempting to auto-start TACNET Server Service...");
startService(new Intent(this, TACNETServerService.class));
}
if (getIntent().getBooleanExtra("navigate_to_tacnet", false)) {
Log.i(TAG, "Navigatingg to tacnet...");
Navigation.findNavController(this, R.id.nav_host_fragment).navigate(R.id.navigation_tacnet);
}
}
@Override

View File

@@ -11,4 +11,6 @@ public class TAC {
SETTINGS_PATH = ROOT_PATH + File.separator + "settings.json";
public static final int CONFIG_SPEC_VERSION = 2;
// Set COMPETITION_MODE to true to disable automatic TACNET server start
public static final boolean COMPETITION_MODE = false;
}

View File

@@ -1,48 +0,0 @@
package org.timecrafters.TimeCraftersConfigurationTool.dialogs;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
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);
final View root = super.onCreateView(inflater, container, savedInstanceState);
if (Backend.instance().getServer() == null) {
Backend.instance().startServer();
}
final TextView title = root.findViewById(R.id.dialog_title);
final ConstraintLayout titlebar = root.findViewById(R.id.titlebar);
final LinearLayout view = root.findViewById(R.id.dialog_content);
view.addView(getLayoutInflater().inflate(R.layout.dialog_server, null));
new ServerStatsSyncHandler(view, 1_000);
title.setText(getResources().getString(R.string.tacnet_server_status));
final Button stopServer = root.findViewById(R.id.stop_server);
stopServer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Backend.instance().stopServer();
dismiss();
}
});
return root;
}
}

View File

@@ -0,0 +1,84 @@
package org.timecrafters.TimeCraftersConfigurationTool.tacnet;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import org.timecrafters.TimeCraftersConfigurationTool.MainActivity;
import org.timecrafters.TimeCraftersConfigurationTool.R;
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
public class TACNETConnectionService extends Service {
private static final String CHANNEL_ID = "TACNET_CONNECTION_SERVICE";
private static final String TAG = "TACNETConnectionService";
private static final int ID = 8962_1;
public TACNETConnectionService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
foregroundify();
connect();
return START_REDELIVER_INTENT;
}
@Override
public void onDestroy() {
stopForeground(true);
}
private void connect() {
}
private void disconnect() {
Backend.instance().stopServer();
}
private void foregroundify() {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.putExtra("navigate_to_tacnet", true);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("TACNET Connection is running")
.setSmallIcon(R.drawable.tacnet)
.setContentIntent(pendingIntent);
Notification notification = builder.build();
startForeground(ID, notification);
}
private void createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "TACNET Connection Service";
String description = "TACNET Connection Service Description";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
}

View File

@@ -0,0 +1,93 @@
package org.timecrafters.TimeCraftersConfigurationTool.tacnet;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import org.timecrafters.TimeCraftersConfigurationTool.MainActivity;
import org.timecrafters.TimeCraftersConfigurationTool.R;
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
public class TACNETServerService extends Service {
private static final String CHANNEL_ID = "TACNET_SERVER_SERVICE";
private static final String TAG = "TACNETServerService";
private static final int ID = 8962_0;
public TACNETServerService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
foregroundify();
startServer();
return START_REDELIVER_INTENT;
}
@Override
public void onDestroy() {
Log.i(TAG, "Stopping server...");
stopServer();
stopForeground(true);
}
private void startServer() {
if (Backend.instance().getServer() == null) {
Log.i(TAG, "Starting server...");
Backend.instance().startServer();
}
}
private void stopServer() {
Backend.instance().stopServer();
}
private void foregroundify() {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.putExtra("navigate_to_tacnet", true);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("TACNET server is running")
.setSmallIcon(R.drawable.tacnet)
.setContentIntent(pendingIntent);
Notification notification = builder.build();
startForeground(ID, notification);
}
private void createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "TACNET Server Service";
String description = "TACNET Server Service Description";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
}

View File

@@ -17,6 +17,7 @@ public class ServerStatsSyncHandler {
private Handler handler;
private Runnable runner;
private long delay;
private boolean stopped = false;
private TextView clientStatus, totalPacketsIn, totalPacketsOut, totalDataIn, totalDataOut;
public ServerStatsSyncHandler(View view, long delay) {
@@ -42,7 +43,7 @@ public class ServerStatsSyncHandler {
public void run() {
Server server = Backend.instance().getServer();
if (server != null) {
if (!stopped && server != null) {
if (server.hasActiveClient()) {
clientStatus.setText("Connected");
} else {
@@ -57,4 +58,8 @@ public class ServerStatsSyncHandler {
handler.postDelayed(runner, delay);
}
}
public void stop() {
stopped = true;
}
}

View File

@@ -1,5 +1,6 @@
package org.timecrafters.TimeCraftersConfigurationTool.ui.tacnet;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
@@ -12,19 +13,14 @@ 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.TACNETServerService;
import org.timecrafters.TimeCraftersConfigurationTool.tacnet.support.ConnectionStatsSyncHandler;
import org.timecrafters.TimeCraftersConfigurationTool.tacnet.support.ServerStatsSyncHandler;
@@ -32,6 +28,7 @@ public class TACNETHostFragment extends TimeCraftersFragment {
private static final String TAG = "TACNETFragment";
private ConnectionStatsSyncHandler connectionStatsSyncHandler;
private ServerStatsSyncHandler serverStatsSyncHandler;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup viewGroup, Bundle savedInstanceState) {
@@ -41,6 +38,8 @@ public class TACNETHostFragment extends TimeCraftersFragment {
if (Backend.instance().tacnet().status() != TACNET.Status.NOT_CONNECTED) {
inflateTACNETConnectionStatus(container);
} else if (Backend.instance().getServer() != null) {
inflateTACNETServerStatus(container);
} else {
inflateTACNET(container);
}
@@ -106,8 +105,10 @@ public class TACNETHostFragment extends TimeCraftersFragment {
startServerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ServerDialog dialog = new ServerDialog();
dialog.show(getFragmentManager(), null);
getActivity().startService(new Intent(getContext(), TACNETServerService.class));
root.removeAllViews();
inflateTACNETServerStatus(container);
}
});
}
@@ -133,14 +134,41 @@ public class TACNETHostFragment extends TimeCraftersFragment {
});
}
private void inflateTACNETServerStatus(final LinearLayout container) {
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.title_tacnet_server_status);
final ConstraintLayout root = (ConstraintLayout) getLayoutInflater().inflate(R.layout.fragment_tacnet_server_status, null);
container.removeAllViews();
container.addView(root);
serverStatsSyncHandler = new ServerStatsSyncHandler(root, 1_000);
Button stopServer = root.findViewById(R.id.stop_server);
stopServer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getActivity().stopService(new Intent(getContext(), TACNETServerService.class));
Backend.instance().stopErrorSound();
serverStatsSyncHandler.stop();
inflateTACNET(container);
}
});
}
@Override
public void onDetach() {
super.onDetach();
if (connectionStatsSyncHandler != null) {
Log.d(TAG, "onDetach: stopping sync handler");
Log.d(TAG, "onDetach: stopping client sync handler");
connectionStatsSyncHandler.stop();
connectionStatsSyncHandler = null;
}
if (serverStatsSyncHandler != null) {
Log.d(TAG, "onDetach: stopping server sync handler");
serverStatsSyncHandler.stop();
serverStatsSyncHandler = null;
}
}
}

View File

@@ -1,127 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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="Client Status"
android:textColor="@color/dialog_label"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Total Packets In"
android:textColor="@color/dialog_label"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Total Packets Out"
android:textColor="@color/dialog_label"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Total Data In"
android:textColor="@color/dialog_label"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Total Data Out"
android:textColor="@color/dialog_label"
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/client_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0"
android:textColor="@color/dialog_label" />
<TextView
android:id="@+id/total_packets_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0"
android:textColor="@color/dialog_label" />
<TextView
android:id="@+id/total_packets_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0"
android:textColor="@color/dialog_label" />
<TextView
android:id="@+id/total_data_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0"
android:textColor="@color/dialog_label" />
<TextView
android:id="@+id/total_data_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0"
android:textColor="@color/dialog_label"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/dialog_button_margin"
android:orientation="horizontal">
<Button
android:id="@+id/stop_server"
style="@style/DangerousButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/tacnet_stop_server" />
</LinearLayout>
</LinearLayout>

View File

@@ -14,126 +14,120 @@
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/tacnet_connect_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
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: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">
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="match_parent"
android:layout_height="match_parent"
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:layout_marginRight="@dimen/button_margin_right"
android:orientation="horizontal">
android:orientation="vertical">
<!-- labels -->
<LinearLayout
<TextView
android:id="@+id/connection_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_weight="1"
android:text="0" />
<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
<TextView
android:id="@+id/total_packets_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/button_margin_left"
android:orientation="vertical">
android:layout_weight="1"
android:text="0" />
<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_out"
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_data_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>
<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>
<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>
<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>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,132 @@
<?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:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/tacnet_server_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:id="@+id/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="Client 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/client_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/stop_server"
style="@style/DangerousButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dialog_button_margin"
android:layout_weight="1"
android:text="@string/tacnet_stop_server" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -5,6 +5,7 @@
<string name="title_settings">Settings</string>
<string name="title_tacnet">TACNET</string>
<string name="title_tacnet_connection_status">TACNET Connection Status</string>
<string name="title_tacnet_server_status">TACNET Server Status</string>
<string name="tacnet_hostname">Hostname</string>
<string name="tacnet_port">Port</string>