From a8ec2d0956b8e5621d7c14acbc1c6b7e33795ef4 Mon Sep 17 00:00:00 2001 From: cyberarm Date: Tue, 10 Nov 2020 10:37:31 -0600 Subject: [PATCH] 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 --- app/src/main/AndroidManifest.xml | 14 +- .../MainActivity.java | 20 ++ .../backend/TAC.java | 2 + .../dialogs/ServerDialog.java | 48 ----- .../tacnet/TACNETConnectionService.java | 84 ++++++++ .../tacnet/TACNETServerService.java | 93 +++++++++ .../support/ServerStatsSyncHandler.java | 7 +- .../ui/tacnet/TACNETHostFragment.java | 46 ++++- app/src/main/res/layout/dialog_server.xml | 127 ------------ .../fragment_tacnet_connection_status.xml | 190 +++++++++--------- .../layout/fragment_tacnet_server_status.xml | 132 ++++++++++++ app/src/main/res/values/strings.xml | 1 + 12 files changed, 479 insertions(+), 285 deletions(-) delete mode 100644 app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/ServerDialog.java create mode 100644 app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/TACNETConnectionService.java create mode 100644 app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/tacnet/TACNETServerService.java delete mode 100644 app/src/main/res/layout/dialog_server.xml create mode 100644 app/src/main/res/layout/fragment_tacnet_server_status.xml 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -