diff options
author | Alexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com> | 2022-05-29 16:35:29 +0300 |
---|---|---|
committer | Alexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com> | 2022-05-29 16:35:29 +0300 |
commit | f91f04c193a0038e9988db101429ee5481c83016 (patch) | |
tree | 671bbc0e9aaa4fb34359dab383db4525697d0e6f | |
parent | b79eae37fcf0f5e27dea66b98ca1468c7fc62211 (diff) |
First commit
60 files changed, 1611 insertions, 0 deletions
diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..d18fb5d --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +USB Serial Telnet Server
\ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..fb7f4a8 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CompilerConfiguration"> + <bytecodeTargetLevel target="11" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..8f6e4e0 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="deploymentTargetDropDown"> + <targetSelectedWithDropDown> + <Target> + <type value="QUICK_BOOT_TARGET" /> + <deviceKey> + <Key> + <type value="VIRTUAL_DEVICE_PATH" /> + <value value="C:\Users\clust\.android\avd\Nexus_5X_API_32.avd" /> + </Key> + </deviceKey> + </Target> + </targetSelectedWithDropDown> + <timeTargetWasSelectedWithDropDown value="2022-05-27T10:08:07.332831600Z" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..a0f7380 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="DesignSurface"> + <option name="filePathToZoomLevelMap"> + <map> + <entry key="..\:/Projects/AndroidStudio/USBSerialTelnetServer/app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.5475" /> + <entry key="..\:/Projects/AndroidStudio/USBSerialTelnetServer/app/src/main/res/drawable/ic_launcher_background.xml" value="0.5475" /> + <entry key="..\:/Projects/AndroidStudio/USBSerialTelnetServer/app/src/main/res/layout/activity_main.xml" value="0.31354166666666666" /> + <entry key="..\:/Projects/AndroidStudio/USBSerialTelnetServer/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.5475" /> + </map> + </option> + </component> + <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK"> + <output url="file://$PROJECT_DIR$/build/classes" /> + </component> + <component name="ProjectType"> + <option name="id" value="Android" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/render.experimental.xml b/.idea/render.experimental.xml new file mode 100644 index 0000000..8ec256a --- /dev/null +++ b/.idea/render.experimental.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="RenderSettings"> + <option name="showDecorations" value="true" /> + </component> +</project>
\ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build
\ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..89583e7 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,41 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.clusterrr.usbserialtelnetserver" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + applicationVariants.all { variant -> + variant.outputs.all { output -> + def formattedDate = new Date().format('yyMMdd-HHmmss') + outputFileName = "UsbSerialTelnetServer-v${defaultConfig.versionCode}-${formattedDate}.apk" + } + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'com.google.android.material:material:1.6.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'com.github.mik3y:usb-serial-for-android:3.4.3' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile
\ No newline at end of file diff --git a/app/src/androidTest/java/com/clusterrr/usbserialtelnetserver/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/clusterrr/usbserialtelnetserver/ExampleInstrumentedTest.java new file mode 100644 index 0000000..72b7ade --- /dev/null +++ b/app/src/androidTest/java/com/clusterrr/usbserialtelnetserver/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.clusterrr.usbserialtelnetserver; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.clusterrr.usbserialtelnetserver", appContext.getPackageName()); + } +}
\ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3dc5fa2 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.clusterrr.usbserialtelnetserver" > + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> + <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> + + <application + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/Theme.USBSerialTelnetServer" > + + <service + android:name=".UsbSerialTelnetService" + android:enabled="true" + android:exported="true"/> + + <activity + android:name=".MainActivity" + android:exported="true" + android:launchMode="singleTop" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + <intent-filter> + <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> + </intent-filter> + <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/usb_device_filter"/> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png Binary files differnew file mode 100644 index 0000000..1ca72ee --- /dev/null +++ b/app/src/main/ic_launcher-playstore.png diff --git a/app/src/main/java/com/clusterrr/usbserialtelnetserver/MainActivity.java b/app/src/main/java/com/clusterrr/usbserialtelnetserver/MainActivity.java new file mode 100644 index 0000000..0206003 --- /dev/null +++ b/app/src/main/java/com/clusterrr/usbserialtelnetserver/MainActivity.java @@ -0,0 +1,258 @@ +package com.clusterrr.usbserialtelnetserver; + +import static android.hardware.usb.UsbManager.EXTRA_PERMISSION_GRANTED; +import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; + +import android.Manifest; +import android.annotation.TargetApi; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.os.PowerManager; +import android.provider.Settings; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; + +import com.hoho.android.usbserial.driver.UsbSerialDriver; +import com.hoho.android.usbserial.driver.UsbSerialPort; +import com.hoho.android.usbserial.driver.UsbSerialProber; + +import java.text.ParseException; +import java.util.List; + +public class MainActivity extends AppCompatActivity implements View.OnClickListener, UsbSerialTelnetService.IOnStopListener { + final static String SETTING_TCP_PORT = "tcp_port"; + final static String SETTING_BAUD_RATE = "baud_rate"; + final static String SETTING_DATA_BITS = "data_bits"; + final static String SETTING_STOP_BITS = "stop_bits"; + final static String SETTING_PARITY = "parity"; + + UsbSerialTelnetService.ServiceBinder mServiceBinder = null; + Button mStartButton; + Button mStopButton; + EditText mTcpPort; + EditText mBaudRate; + Spinner mDataBits; + Spinner mStopBits; + Spinner mParity; + TextView mStatus; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + mStartButton = findViewById(R.id.buttonStart); + mStopButton = findViewById(R.id.buttonStop); + mTcpPort = findViewById(R.id.editTextTcpPort); + mBaudRate = findViewById(R.id.editTextNumberBaudRate); + mDataBits = findViewById(R.id.spinnerDataBits); + mStopBits = findViewById(R.id.spinnerStopBits); + mParity = findViewById(R.id.spinnerParity); + mStatus = findViewById(R.id.textViewStatus); + + mStartButton.setOnClickListener(this); + mStopButton.setOnClickListener(this); + + Intent serviceIntent = new Intent(this, UsbSerialTelnetService.class); + bindService(serviceIntent, serviceConnection, 0); // in case it's service already started + Intent intent = getIntent(); + //if (intent != null) onNewIntent(intent); + + updateSettings(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if (intent != null) { + if (intent.getBooleanExtra(UsbSerialTelnetService.KEY_NEED_TO_START, false)) { + // Test that permission is granted + UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); + List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager); + if (!availableDrivers.isEmpty()) { + UsbSerialDriver driver = availableDrivers.get(0); + UsbDeviceConnection connection = manager.openDevice(driver.getDevice()); + if (connection != null) start(); + } + } + } + } + + @Override + protected void onPause() { + super.onPause(); + saveSettings(); + } + + @Override + public void onClick(View view) + { + switch(view.getId()) + { + case R.id.buttonStart: + start(); + break; + case R.id.buttonStop: + stop(); + break; + } + } + + private void start() { + saveSettings(); + + Intent ignoreOptimization = prepareIntentForWhiteListingOfBatteryOptimization( + this, getPackageName(), false); + if (ignoreOptimization != null) startActivity(ignoreOptimization); + + Intent serviceIntent = new Intent(this, UsbSerialTelnetService.class); + SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); + serviceIntent.putExtra(UsbSerialTelnetService.KEY_TCP_PORT, prefs.getInt(SETTING_TCP_PORT, 2323)); + serviceIntent.putExtra(UsbSerialTelnetService.KEY_BAUD_RATE, prefs.getInt(SETTING_BAUD_RATE, 115200)); + serviceIntent.putExtra(UsbSerialTelnetService.KEY_DATA_BITS, prefs.getInt(SETTING_DATA_BITS, 3) + 5); + switch (prefs.getInt(SETTING_STOP_BITS, 0)) { + case 0: + serviceIntent.putExtra(UsbSerialTelnetService.KEY_STOP_BITS, UsbSerialPort.STOPBITS_1); + break; + case 1: + serviceIntent.putExtra(UsbSerialTelnetService.KEY_STOP_BITS, UsbSerialPort.STOPBITS_1_5); + break; + case 2: + serviceIntent.putExtra(UsbSerialTelnetService.KEY_STOP_BITS, UsbSerialPort.STOPBITS_2); + break; + } + serviceIntent.putExtra(UsbSerialTelnetService.KEY_PARITY, prefs.getInt(SETTING_PARITY, 0)); + startForegroundService(serviceIntent); + bindService(serviceIntent, serviceConnection, 0); + } + + private void stop() { + Intent serviceIntent = new Intent(this, UsbSerialTelnetService.class); + stopService(serviceIntent); + mServiceBinder = null; + updateSettings(); + } + + private ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + mServiceBinder = (UsbSerialTelnetService.ServiceBinder) service; + mServiceBinder.setOnStopListener(MainActivity.this); + updateSettings(); + Log.d(UsbSerialTelnetService.TAG, "Service connected"); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + mServiceBinder = null; + Log.d(UsbSerialTelnetService.TAG, "Service disconnected"); + } + }; + + @Override + public void usbSerialServiceStopped() { + updateSettings(); + } + private void saveSettings() { + SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); + int tcpPort; + try { + tcpPort = Integer.parseInt(mTcpPort.getText().toString()); + } + catch (NumberFormatException e) { + tcpPort = 2323; + } + int baudRate; + try { + baudRate = Integer.parseInt(mBaudRate.getText().toString()); + } + catch (NumberFormatException e) { + baudRate = 115200; + } + prefs.edit() + .putInt(SETTING_TCP_PORT, tcpPort) + .putInt(SETTING_BAUD_RATE, baudRate) + .putInt(SETTING_DATA_BITS, mDataBits.getSelectedItemPosition()) + .putInt(SETTING_STOP_BITS, mStopBits.getSelectedItemPosition()) + .putInt(SETTING_PARITY, mParity.getSelectedItemPosition()) + .commit(); + } + + private void updateSettings() { + SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); + boolean started = mServiceBinder != null && mServiceBinder.isStarted(); + mStartButton.setEnabled(!started); + mStopButton.setEnabled(started); + mTcpPort.setEnabled(!started); + mBaudRate.setEnabled(!started); + mDataBits.setEnabled(!started); + mStopBits.setEnabled(!started); + mParity.setEnabled(!started); + mTcpPort.setText(String.valueOf(prefs.getInt(SETTING_TCP_PORT, 2323))); + mBaudRate.setText(String.valueOf(prefs.getInt(SETTING_BAUD_RATE, 115200))); + mDataBits.setSelection(prefs.getInt(SETTING_DATA_BITS, 3)); + mStopBits.setSelection(prefs.getInt(SETTING_STOP_BITS, 0)); + mParity.setSelection(prefs.getInt(SETTING_PARITY, 0)); + if (started) + mStatus.setText("Started, please connect to: telnet://" + UsbSerialTelnetService.getIPAddress() + ": "+ mTcpPort.getText()); + else + mStatus.setText("Not started"); + } + + public static Intent prepareIntentForWhiteListingOfBatteryOptimization(Context context, String packageName, boolean alsoWhenWhiteListed) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + return null; + if (ContextCompat.checkSelfPermission(context, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) == PackageManager.PERMISSION_DENIED) + return null; + final WhiteListedInBatteryOptimizations appIsWhiteListedFromPowerSave = getIfAppIsWhiteListedFromBatteryOptimizations(context, packageName); + Intent intent = null; + switch (appIsWhiteListedFromPowerSave) { + case WHITE_LISTED: + if (alsoWhenWhiteListed) + intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); + break; + case NOT_WHITE_LISTED: + intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).setData(Uri.parse("package:" + packageName)); + break; + case ERROR_GETTING_STATE: + case UNKNOWN_TOO_OLD_ANDROID_API_FOR_CHECKING: + case IRRELEVANT_OLD_ANDROID_API: + default: + break; + } + return intent; + } + + public enum WhiteListedInBatteryOptimizations { + WHITE_LISTED, NOT_WHITE_LISTED, ERROR_GETTING_STATE, UNKNOWN_TOO_OLD_ANDROID_API_FOR_CHECKING, IRRELEVANT_OLD_ANDROID_API + } + + public static WhiteListedInBatteryOptimizations getIfAppIsWhiteListedFromBatteryOptimizations(Context context, String packageName) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + return WhiteListedInBatteryOptimizations.IRRELEVANT_OLD_ANDROID_API; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) + return WhiteListedInBatteryOptimizations.UNKNOWN_TOO_OLD_ANDROID_API_FOR_CHECKING; + final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + if (pm == null) + return WhiteListedInBatteryOptimizations.ERROR_GETTING_STATE; + return pm.isIgnoringBatteryOptimizations(packageName) ? WhiteListedInBatteryOptimizations.WHITE_LISTED : WhiteListedInBatteryOptimizations.NOT_WHITE_LISTED; + } + +}
\ No newline at end of file diff --git a/app/src/main/java/com/clusterrr/usbserialtelnetserver/TcpClientThread.java b/app/src/main/java/com/clusterrr/usbserialtelnetserver/TcpClientThread.java new file mode 100644 index 0000000..3e793ab --- /dev/null +++ b/app/src/main/java/com/clusterrr/usbserialtelnetserver/TcpClientThread.java @@ -0,0 +1,144 @@ +package com.clusterrr.usbserialtelnetserver; + +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; + +public class TcpClientThread extends Thread { + private UsbSerialTelnetService mUsbSerialTelnetService; + private TcpServerThread mTcpServerThread; + private Socket mSocket; + private InputStream mDataInputStream; + private OutputStream mDataOutputStream; + private String mAddress; + private List<Byte> mBuffer; + + public TcpClientThread(UsbSerialTelnetService usbSerialTelnetService, TcpServerThread tcpServerThread, Socket socket) throws IOException { + mUsbSerialTelnetService = usbSerialTelnetService; + mTcpServerThread = tcpServerThread; + mSocket = socket; + mDataInputStream = mSocket.getInputStream(); + mDataOutputStream = mSocket.getOutputStream(); + mAddress = mSocket.getRemoteSocketAddress().toString(); + mBuffer = new ArrayList<>(); + } + + @Override + public void run() { + byte buffer[] = new byte[1024]; + + try { + mDataOutputStream.write(new byte[]{(byte) 0xFF, (byte) 0xFD, (byte) 0x03}); // Do Suppress Go Ahead + mDataOutputStream.write(new byte[]{(byte) 0xFF, (byte) 0xFB, (byte) 0x03}); // Will Suppress Go Ahead + mDataOutputStream.write(new byte[]{(byte) 0xFF, (byte) 0xFB, (byte) 0x01}); // Will Echo + while (true) { + if (mDataInputStream == null) break; + int l = mDataInputStream.read(buffer); + if (l <= 0) break; // disconnect + for (int i = 0; i < l; i++) + mBuffer.add(buffer[i]); + proceedBuffer(); + } + } + catch (SocketException e) { + Log.i(UsbSerialTelnetService.TAG, mAddress + ": " + e.getMessage()); + } + catch (IOException e) { + e.printStackTrace(); + } + + try { + if (mSocket != null) + mSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + mSocket = null; + mDataInputStream = null; + mDataOutputStream = null; + mTcpServerThread.removeClient(this); + Log.i(UsbSerialTelnetService.TAG, mAddress + ": stopped"); + } + + private void proceedBuffer() throws IOException { + int len = mBuffer.size(); + int i = 0; + byte[] output = new byte[len]; + int outputSize = 0; + for (; i < len; i++) { + byte b = mBuffer.get(i); + if (b == 0) continue; + if (b == '\n') continue; + /* + if ((b == '\r') && ((i >= len) || (mBuffer.get(i + 1) == '\n'))) { + // skip \r\n + if (true) + continue; + } + */ + if (b == (byte)0xFF) { + if (i >= len) break; + byte next = mBuffer.get(i + 1); + if (next == (byte)0xFF) { + // just 0xFF + //mUsbSerialTelnetService.writePort((byte) 0xFF); + output[outputSize++] = (byte)0xFF; + i++; + continue; + } + // Command + if (i + 1 >= len) break; + byte cmd = next; + byte opt = mBuffer.get(i + 2); + Log.d(UsbSerialTelnetService.TAG, "Telnet command: CMD=" + Integer.toHexString(cmd >= 0 ? cmd : cmd + 256) + " ARG=" + Integer.toHexString(opt >= 0 ? opt : opt + 256)); + i += 2; + continue; + } + // just data + //mUsbSerialTelnetService.writePort(b); + output[outputSize++] = b; + } + + // Remove proceeded + for (int j = 0; j < i; j++) + mBuffer.remove(0); + + mUsbSerialTelnetService.writeSerialPort(output, 0, outputSize); + } + + public void write(byte[] data) throws IOException { + write(data, 0, data.length); + } + + public void write(byte[] data, int offset, int len) throws IOException { + if (mDataOutputStream != null) + mDataOutputStream.write(data, offset, len); + //Log.d(UsbSerialTelnetService.TAG, "Writing " + len + " bytes to TCP"); + } + + public void close() { + try { + if (mSocket != null) { + mSocket.close(); + mSocket = null; + } + if (mDataOutputStream != null) { + mDataOutputStream.close(); + mDataOutputStream = null; + } + if (mDataInputStream != null) + { + mDataInputStream.close(); + mDataInputStream = null; + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/main/java/com/clusterrr/usbserialtelnetserver/TcpServerThread.java b/app/src/main/java/com/clusterrr/usbserialtelnetserver/TcpServerThread.java new file mode 100644 index 0000000..b18c516 --- /dev/null +++ b/app/src/main/java/com/clusterrr/usbserialtelnetserver/TcpServerThread.java @@ -0,0 +1,91 @@ +package com.clusterrr.usbserialtelnetserver; + +import android.util.Log; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; + +public class TcpServerThread extends Thread { + private UsbSerialTelnetService mUsbSerialTelnetService; + private ServerSocket mTcpServer; + private List<TcpClientThread> mClients; + + public TcpServerThread(UsbSerialTelnetService usbSerialTelnetService, ServerSocket tcpServer) + { + mUsbSerialTelnetService = usbSerialTelnetService; + mTcpServer = tcpServer; + mClients = new ArrayList<>(); + } + + @Override + public void run() { + try { + while (true) { + if (mTcpServer == null) break; + Socket socket = mTcpServer.accept(); + Log.i(UsbSerialTelnetService.TAG, "Connected: " + socket.getRemoteSocketAddress()); + TcpClientThread client = new TcpClientThread(mUsbSerialTelnetService, this, socket); + client.start(); + mClients.add(client); + } + } + catch (SocketException e) { + Log.i(UsbSerialTelnetService.TAG, "Server: " + e.getMessage()); + } + catch (Exception e) { + e.printStackTrace(); + } + close(); + Log.i(UsbSerialTelnetService.TAG, "Server: stopped"); + } + + public void removeClient(TcpClientThread tcpClientThread) { + mClients.remove(tcpClientThread); + } + + public void write(byte[] data) throws IOException { + write(data, 0, data.length); + } + + public void write(byte[] data, int offset, int len) throws IOException { + List<TcpClientThread> toRemove = new ArrayList<>(); + for (TcpClientThread client : mClients) { + try { + client.write(data, offset, len); + } + catch (Exception ex) { + ex.printStackTrace(); + toRemove.add(client); + } + } + for (TcpClientThread client : toRemove) { + client.close(); + mClients.remove(client); + } + } + + public void close() + { + try { + if (mTcpServer != null) { + mTcpServer.close(); + mTcpServer = null; + } + } catch (IOException e) { + e.printStackTrace(); + } + for (TcpClientThread client : mClients) { + try { + client.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + mClients.clear(); + } +} diff --git a/app/src/main/java/com/clusterrr/usbserialtelnetserver/UsbSerialTelnetService.java b/app/src/main/java/com/clusterrr/usbserialtelnetserver/UsbSerialTelnetService.java new file mode 100644 index 0000000..eb0f292 --- /dev/null +++ b/app/src/main/java/com/clusterrr/usbserialtelnetserver/UsbSerialTelnetService.java @@ -0,0 +1,241 @@ +package com.clusterrr.usbserialtelnetserver; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbManager; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.util.Log; +import android.widget.Toast; + +import androidx.core.app.NotificationCompat; + +import com.hoho.android.usbserial.driver.UsbSerialDriver; +import com.hoho.android.usbserial.driver.UsbSerialPort; +import com.hoho.android.usbserial.driver.UsbSerialProber; +import com.hoho.android.usbserial.util.SerialInputOutputManager; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.ServerSocket; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Enumeration; +import java.util.List; + +public class UsbSerialTelnetService extends Service { + final static String TAG = "UsbSerialTelnet"; + final static String KEY_NEED_TO_START = "need_to_start"; +// final static String START_INTENT = "com.clusterrr.usbserialtelnetserver.START"; +// final static String STOP_INTENT = "com.clusterrr.usbserialtelnetserver.STOP"; +// final static String STARTED_INTENT = "com.clusterrr.usbserialtelnetserver.STARTED"; +// final static String STOPPED_INTENT = "com.clusterrr.usbserialtelnetserver.STOPPED"; + final static String KEY_TCP_PORT = "tcp_port"; + final static String KEY_BAUD_RATE = "baud_rate"; + final static String KEY_DATA_BITS = "data_bits"; + final static String KEY_STOP_BITS = "stop_bits"; + final static String KEY_PARITY = "parity"; + + boolean mStarted = false; + //UsbSerialPort mSerialPort = null; + UsbSerialThread mUsbSerialThread = null; + TcpServerThread mTcpServerThread = null; + + int mTcpPort = 2323; + + public UsbSerialTelnetService() { + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + if (mStarted) { + // Already started + new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(UsbSerialTelnetService.this.getApplicationContext(), "Already started", Toast.LENGTH_LONG).show()); + return START_STICKY; + } + + String message = getString(R.string.app_name) + " started"; + boolean success = false; + + try { + // Find all available drivers from attached devices. + UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); + List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager); + if (availableDrivers.isEmpty()) { + message = "Error: USB serial device not found"; + } else { + // Open a connection to the first available driver. + UsbSerialDriver driver = availableDrivers.get(0); + UsbDeviceConnection connection = manager.openDevice(driver.getDevice()); + if (connection == null) { + message = null; // "Please grant permission and try again"; + Intent mainActivityStartIntent = new Intent(this, MainActivity.class); + mainActivityStartIntent.putExtra(KEY_NEED_TO_START, true); + mainActivityStartIntent.setAction(KEY_NEED_TO_START); + PendingIntent mainActivityStartPendingIntent = PendingIntent.getActivity(this, 0, mainActivityStartIntent, PendingIntent.FLAG_IMMUTABLE); + manager.requestPermission(driver.getDevice(), mainActivityStartPendingIntent); + } else { + UsbSerialPort serialPort = driver.getPorts().get(0); // Most devices have just one port (port 0) + serialPort.open(connection); + serialPort.setParameters( + intent.getIntExtra(KEY_BAUD_RATE, 115200), + intent.getIntExtra(KEY_DATA_BITS, 8), + intent.getIntExtra(KEY_STOP_BITS, UsbSerialPort.STOPBITS_1), + intent.getIntExtra(KEY_PARITY, UsbSerialPort.PARITY_NONE)); + ServerSocket serverSocket = new ServerSocket(intent.getIntExtra(KEY_TCP_PORT,2323)); + mUsbSerialThread = new UsbSerialThread(this, serialPort); + mTcpServerThread = new TcpServerThread(this, serverSocket); + mUsbSerialThread.start(); + mTcpServerThread.start(); + success = true; + } + } + } + catch (Exception ex) { + message = "Error: " + ex.getMessage(); + ex.printStackTrace(); + } + + Intent mainActivityIntent = new Intent(this, MainActivity.class); + PendingIntent mainActivityPendingIntent = PendingIntent.getActivity(this, 0, mainActivityIntent, PendingIntent.FLAG_IMMUTABLE); + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel(TAG, + getString(R.string.app_name), + NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(getString(R.string.app_name)); + nm.createNotificationChannel(channel); + } + Notification notification = new NotificationCompat.Builder(this, TAG) + .setOngoing(true) + .setSmallIcon(R.mipmap.ic_notification) + .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher)) + .setContentTitle(message) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setCategory(Notification.CATEGORY_SERVICE) + .setShowWhen(false) + .setContentIntent(mainActivityPendingIntent) + .build(); + + if (message != null) { + final String msg = message; + new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(UsbSerialTelnetService.this.getApplicationContext(), msg, Toast.LENGTH_SHORT).show()); + } + + startForeground(1, notification); + + if (success) { + if (message != null) + Log.i(TAG, message); + Log.d(TAG, String.format("Local IP Address: %s", getIPAddress())); + mStarted = true; + } else { + if (message != null) + Log.e(TAG, message); + stopSelf(); + mStarted = false; + } + return START_STICKY; + } + + public static String getIPAddress() { + try { + Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + if (networkInterface.isUp()) { + String name = networkInterface.getName(); + if (name.toLowerCase().equals("wlan0") || name.toLowerCase().equals("rmnet0")) { + List<InterfaceAddress> interfaceAddresses = networkInterface.getInterfaceAddresses(); + for (InterfaceAddress interfaceAddress : interfaceAddresses) { + InetAddress address = interfaceAddress.getAddress(); + if (address instanceof Inet4Address){ + return address.getHostAddress(); + } + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return "localhost"; + } + + @Override + public void onDestroy() + { + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + nm.cancel(1); + if (mTcpServerThread != null) { + mTcpServerThread.close(); + mTcpServerThread = null; + } + if (mUsbSerialThread != null) { + mUsbSerialThread.close(); + mUsbSerialThread = null; + } + if (mStarted) + new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(UsbSerialTelnetService.this.getApplicationContext(), getString(R.string.app_name) + " stopped", Toast.LENGTH_SHORT).show()); + Log.i(TAG, "Service stopped"); + mStarted = false; + mBinder.stopped(); + } + + private final ServiceBinder mBinder = new ServiceBinder(); + public class ServiceBinder extends Binder { + private IOnStopListener onStopListener = null; + public boolean isStarted() + { + return mStarted; + } + public void setOnStopListener(IOnStopListener listener) { onStopListener = listener; } + public void stopped() { if (onStopListener != null) onStopListener.usbSerialServiceStopped(); } + } + public interface IOnStopListener + { + public void usbSerialServiceStopped(); + } + + public void writeSerialPort(byte[] buffer) throws IOException { + if (mUsbSerialThread == null) return; + mUsbSerialThread.write(buffer); + } + + public void writeSerialPort(byte[] buffer, int pos, int len) throws IOException { + if (mUsbSerialThread == null) return; + byte[] writeBuffer = new byte[len]; + System.arraycopy(buffer, pos, writeBuffer, 0, len); + mUsbSerialThread.write(writeBuffer); + } + + public void writeClients(byte[] buffer) throws IOException { + if (mTcpServerThread == null) return; + mTcpServerThread.write(buffer); + } + + public void writeClients(byte[] buffer, int pos, int len) throws IOException { + if (mTcpServerThread == null) return; + mTcpServerThread.write(buffer, pos, len); + } +}
\ No newline at end of file diff --git a/app/src/main/java/com/clusterrr/usbserialtelnetserver/UsbSerialThread.java b/app/src/main/java/com/clusterrr/usbserialtelnetserver/UsbSerialThread.java new file mode 100644 index 0000000..6bfe4e6 --- /dev/null +++ b/app/src/main/java/com/clusterrr/usbserialtelnetserver/UsbSerialThread.java @@ -0,0 +1,60 @@ +package com.clusterrr.usbserialtelnetserver; + +import android.util.Log; + +import com.hoho.android.usbserial.driver.UsbSerialPort; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.SocketException; + +public class UsbSerialThread extends Thread { + final static int WRITE_TIMEOUT = 1000; + + private UsbSerialTelnetService mUsbSerialTelnetService; + private UsbSerialPort mSerialPort; + + public UsbSerialThread(UsbSerialTelnetService usbSerialTelnetService, UsbSerialPort serialPort) { + mUsbSerialTelnetService = usbSerialTelnetService; + mSerialPort = serialPort; + } + + @Override + public void run() { + byte buffer[] = new byte[1024]; + + try { + while (true) { + if (mSerialPort == null) break; + int l = mSerialPort.read(buffer, 0); + if (l <= 0) break; // disconnect + mUsbSerialTelnetService.writeClients(buffer, 0, l); + } + } + catch (IOException e) { + Log.i(UsbSerialTelnetService.TAG, "Serial port: " + e.getMessage()); + } + catch (Exception e) { + e.printStackTrace(); + } + close(); + Log.i(UsbSerialTelnetService.TAG, "Serial port closed"); + mUsbSerialTelnetService.stopSelf(); + } + + public void write(byte[] data) throws IOException { + if (mSerialPort != null) + mSerialPort.write(data, WRITE_TIMEOUT); + } + + public void close() { + try { + if (mSerialPort != null) { + mSerialPort.close(); + mSerialPort = null; + } + } catch (Exception e) { + e.printStackTrace(); + } + } +}
\ No newline at end of file diff --git a/app/src/main/res/color/text_color.xml b/app/src/main/res/color/text_color.xml new file mode 100644 index 0000000..1cf3b79 --- /dev/null +++ b/app/src/main/res/color/text_color.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android" > + <item android:state_enabled="false" android:color="#80000000"/> + <item android:color="#FF000000"/> +</selector> diff --git a/app/src/main/res/color/text_color_dark.xml b/app/src/main/res/color/text_color_dark.xml new file mode 100644 index 0000000..922c96a --- /dev/null +++ b/app/src/main/res/color/text_color_dark.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android" > + <item android:state_enabled="false" android:color="#80FFFFFF"/> + <item android:color="#FFFFFFFF"/> +</selector> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..20ffbb6 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,178 @@ +<?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" + android:layout_gravity="center_horizontal" + tools:context=".MainActivity"> + + <Button + android:id="@+id/buttonStart" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="16dp" + android:text="Start" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/buttonStop" + app:layout_constraintStart_toStartOf="parent" /> + + <Button + android:id="@+id/buttonStop" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginEnd="24dp" + android:layout_marginBottom="16dp" + android:text="Stop" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/buttonStart" /> + + <TextView + android:id="@+id/textViewStatus" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="24dp" + android:layout_marginBottom="16dp" + android:gravity="center" + android:text="status" + app:layout_constraintBottom_toTopOf="@+id/buttonStart" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/scrollView2" /> + + <ScrollView + android:id="@+id/scrollView2" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginTop="16dp" + app:layout_constraintBottom_toTopOf="@+id/textViewStatus" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="400dp" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal"> + + <TextView + android:id="@+id/textViewTcpPort" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:text="TCP port to listen" + app:layout_constraintBottom_toBottomOf="@+id/editTextTcpPort" + app:layout_constraintEnd_toStartOf="@+id/editTextTcpPort" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/editTextTcpPort" /> + + <EditText + android:id="@+id/editTextTcpPort" + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_marginEnd="24dp" + android:ems="10" + android:inputType="number" + android:maxLength="5" + android:text="0" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/textViewTcpPort" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/textViewBaudRate" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:text="Baud rate" + app:layout_constraintBottom_toBottomOf="@+id/editTextNumberBaudRate" + app:layout_constraintEnd_toStartOf="@+id/editTextNumberBaudRate" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/editTextNumberBaudRate" /> + + <EditText + android:id="@+id/editTextNumberBaudRate" + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_marginEnd="24dp" + android:ems="10" + android:inputType="number" + android:maxLength="6" + android:text="0" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/textViewBaudRate" + app:layout_constraintTop_toBottomOf="@+id/editTextTcpPort" /> + + <TextView + android:id="@+id/textViewDataBits" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:text="Data bits" + app:layout_constraintBottom_toBottomOf="@+id/spinnerDataBits" + app:layout_constraintEnd_toStartOf="@+id/spinnerDataBits" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/spinnerDataBits" /> + + <Spinner + android:id="@+id/spinnerDataBits" + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_marginEnd="24dp" + android:entries="@array/data_bits" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/textViewDataBits" + app:layout_constraintTop_toBottomOf="@+id/editTextNumberBaudRate" /> + + <TextView + android:id="@+id/textViewStopBits" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:text="Stop bits" + app:layout_constraintBottom_toBottomOf="@+id/spinnerStopBits" + app:layout_constraintEnd_toStartOf="@+id/spinnerStopBits" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/spinnerStopBits" /> + + <Spinner + android:id="@+id/spinnerStopBits" + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_marginEnd="24dp" + android:entries="@array/stop_bits" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/textViewStopBits" + app:layout_constraintTop_toBottomOf="@+id/spinnerDataBits" /> + + <TextView + android:id="@+id/textViewParity" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:text="Parity" + app:layout_constraintBottom_toBottomOf="@+id/spinnerParity" + app:layout_constraintEnd_toStartOf="@+id/spinnerParity" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/spinnerParity" /> + + <Spinner + android:id="@+id/spinnerParity" + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_marginEnd="24dp" + android:entries="@array/parity" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/textViewParity" + app:layout_constraintTop_toBottomOf="@+id/spinnerStopBits" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + </ScrollView> + +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..4ae7d12 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@mipmap/ic_launcher_background"/> + <foreground android:drawable="@mipmap/ic_launcher_foreground"/> +</adaptive-icon>
\ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..4ae7d12 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@mipmap/ic_launcher_background"/> + <foreground android:drawable="@mipmap/ic_launcher_foreground"/> +</adaptive-icon>
\ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..657a790 --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/app/src/main/res/mipmap-hdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 0000000..2222ba6 --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..01f6633 --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 0000000..17c3435 --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-hdpi/ic_notification.png b/app/src/main/res/mipmap-hdpi/ic_notification.png Binary files differnew file mode 100644 index 0000000..052830e --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_notification.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..0fb2b44 --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/app/src/main/res/mipmap-mdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 0000000..c99ed4f --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..c0092be --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 0000000..ebe7895 --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-mdpi/ic_notification.png b/app/src/main/res/mipmap-mdpi/ic_notification.png Binary files differnew file mode 100644 index 0000000..5a7430c --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_notification.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..5e4b8c5 --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 0000000..8a2820b --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..b7278e5 --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 0000000..59fd94f --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_notification.png b/app/src/main/res/mipmap-xhdpi/ic_notification.png Binary files differnew file mode 100644 index 0000000..caa5017 --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_notification.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..6d6c2b2 --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 0000000..5d2d608 --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..9823908 --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 0000000..387efaa --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_notification.png b/app/src/main/res/mipmap-xxhdpi/ic_notification.png Binary files differnew file mode 100644 index 0000000..6407767 --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_notification.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..d20b769 --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png Binary files differnew file mode 100644 index 0000000..d56fc50 --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..d1e872f --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 0000000..a67f028 --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_notification.png b/app/src/main/res/mipmap-xxxhdpi/ic_notification.png Binary files differnew file mode 100644 index 0000000..f116fa9 --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_notification.png diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..6033bd9 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,18 @@ +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Base application theme. --> + <style name="Theme.USBSerialTelnetServer" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> + <!-- Primary brand color. --> + <item name="colorPrimary">@color/blue_light</item> + <item name="colorPrimaryVariant">@color/blue</item> + <item name="colorOnPrimary">@color/black</item> + <!-- Secondary brand color. --> + <item name="colorSecondary">@color/teal_200</item> + <item name="colorSecondaryVariant">@color/teal_200</item> + <item name="colorOnSecondary">@color/black</item> + <!-- Status bar color. --> + <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> + <!-- Customize your theme here. --> + <item name="android:textColor">@color/text_color_dark</item> + <item name="android:textColorPrimary">@color/text_color_dark</item> + </style> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..6733ad4 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="purple_200">#FFBB86FC</color> + <color name="purple_500">#FF6200EE</color> + <color name="purple_700">#FF3700B3</color> + <color name="teal_200">#FF03DAC5</color> + <color name="teal_700">#FF018786</color> + <color name="black">#FF000000</color> + <color name="white">#FFFFFFFF</color> + <color name="blue">#FF0000FF</color> + <color name="blue_dark">#FF000080</color> + <color name="blue_light">#FFC0C0FF</color> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..72528ed --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,22 @@ +<resources> + <string name="app_name">USB Serial Telnet Server</string> + <string-array name="data_bits"> + <item>5</item> + <item>6</item> + <item>7</item> + <item>8</item> + <item>9</item> + </string-array> + <string-array name="stop_bits"> + <item>1 stop bit</item> + <item>1.5 stop bits</item> + <item>2 stop bits</item> + </string-array> + <string-array name="parity"> + <item>None</item> + <item>Odd</item> + <item>Even</item> + <item>Mark</item> + <item>Space</item> + </string-array> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..21e2869 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,18 @@ +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Base application theme. --> + <style name="Theme.USBSerialTelnetServer" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> + <!-- Primary brand color. --> + <item name="colorPrimary">@color/blue_dark</item> + <item name="colorPrimaryVariant">@color/blue</item> + <item name="colorOnPrimary">@color/white</item> + <!-- Secondary brand color. --> + <item name="colorSecondary">@color/teal_200</item> + <item name="colorSecondaryVariant">@color/teal_700</item> + <item name="colorOnSecondary">@color/black</item> + <!-- Status bar color. --> + <item name="android:statusBarColor" tools:targetApi="l">@color/blue_dark</item> + <!-- Customize your theme here. --> + <item name="android:textColor">@color/text_color</item> + <item name="android:textColorPrimary">@color/text_color</item> + </style> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/xml/usb_device_filter.xml b/app/src/main/res/xml/usb_device_filter.xml new file mode 100644 index 0000000..f0608aa --- /dev/null +++ b/app/src/main/res/xml/usb_device_filter.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <usb-device product-id="24577" vendor-id="1027" /> + <usb-device product-id="24592" vendor-id="1027" /> + <usb-device product-id="24593" vendor-id="1027" /> + <usb-device product-id="24596" vendor-id="1027" /> + <usb-device product-id="24597" vendor-id="1027" /> + <usb-device product-id="60000" vendor-id="4292" /> + <usb-device product-id="60016" vendor-id="4292" /> + <usb-device product-id="60017" vendor-id="4292" /> + <usb-device product-id="8963" vendor-id="1659" /> + <usb-device product-id="9123" vendor-id="1659" /> + <usb-device product-id="9139" vendor-id="1659" /> + <usb-device product-id="9155" vendor-id="1659" /> + <usb-device product-id="9171" vendor-id="1659" /> + <usb-device product-id="9187" vendor-id="1659" /> + <usb-device product-id="9203" vendor-id="1659" /> + <usb-device product-id="21795" vendor-id="6790" /> + <usb-device product-id="29987" vendor-id="6790" /> + <usb-device vendor-id="9025" /> + <usb-device product-id="1155" vendor-id="5824" /> + <usb-device product-id="8260" vendor-id="1003" /> + <usb-device product-id="4" vendor-id="7855" /> + <usb-device product-id="516" vendor-id="3368" /> + <usb-device product-id="22336" vendor-id="1155" /> +</resources> diff --git a/app/src/test/java/com/clusterrr/usbserialtelnetserver/ExampleUnitTest.java b/app/src/test/java/com/clusterrr/usbserialtelnetserver/ExampleUnitTest.java new file mode 100644 index 0000000..ca21106 --- /dev/null +++ b/app/src/test/java/com/clusterrr/usbserialtelnetserver/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.clusterrr.usbserialtelnetserver; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +}
\ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..2fcc7ef --- /dev/null +++ b/build.gradle @@ -0,0 +1,9 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '7.1.2' apply false + id 'com.android.library' version '7.1.2' apply false +} + +task clean(type: Delete) { + delete rootProject.buildDir +}
\ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..dab7c28 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true
\ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..e708b1c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..38fefc9 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri May 27 11:38:17 MSK 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..cd354ff --- /dev/null +++ b/settings.gradle @@ -0,0 +1,17 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven { url 'https://jitpack.io' } + } +} +rootProject.name = "USB Serial Telnet Server" +include ':app' |