diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a4cac16..40db34c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,15 +7,25 @@
+
+
+
+
@@ -25,7 +35,7 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java
index eada882..b19ca54 100644
--- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java
+++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java
@@ -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
diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TAC.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TAC.java
index ed0bb14..52efa2a 100644
--- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TAC.java
+++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TAC.java
@@ -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;
}
diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java
deleted file mode 100644
index 6acbb21..0000000
--- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/TACNETConnectionService.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/TACNETConnectionService.java
new file mode 100644
index 0000000..4a50962
--- /dev/null
+++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/TACNETConnectionService.java
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/TACNETServerService.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/TACNETServerService.java
new file mode 100644
index 0000000..675e82c
--- /dev/null
+++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/TACNETServerService.java
@@ -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);
+ }
+ }
+}
\ No newline at end of file
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
index 869b2f0..7ed9d3a 100644
--- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/support/ServerStatsSyncHandler.java
+++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/support/ServerStatsSyncHandler.java
@@ -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;
+ }
}
diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETHostFragment.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETHostFragment.java
index ad57919..a96df8f 100644
--- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETHostFragment.java
+++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/tacnet/TACNETHostFragment.java
@@ -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;
+ }
}
}
\ 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
deleted file mode 100644
index c09ff09..0000000
--- a/app/src/main/res/layout/dialog_server.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_tacnet_connection_status.xml b/app/src/main/res/layout/fragment_tacnet_connection_status.xml
index 66c0a5e..97d7823 100644
--- a/app/src/main/res/layout/fragment_tacnet_connection_status.xml
+++ b/app/src/main/res/layout/fragment_tacnet_connection_status.xml
@@ -14,126 +14,120 @@
app:layout_constraintTop_toTopOf="parent">
+ 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">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:orientation="vertical">
-
-
+ android:layout_weight="1"
+ android:text="0" />
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_weight="1"
+ android:text="0" />
-
+
-
+
-
-
-
-
-
-
-
+
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_tacnet_server_status.xml b/app/src/main/res/layout/fragment_tacnet_server_status.xml
new file mode 100644
index 0000000..95001ef
--- /dev/null
+++ b/app/src/main/res/layout/fragment_tacnet_server_status.xml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a8700f7..12732a0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5,6 +5,7 @@
Settings
TACNET
TACNET Connection Status
+ TACNET Server Status
Hostname
Port