From 024e26e0419cd68412c67dd91c269a9d429495cc Mon Sep 17 00:00:00 2001 From: "DESKTOP-1A3P75F\\lee" Date: Wed, 21 Sep 2022 11:53:27 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8B=9C=EC=97=B0=EC=9A=A9=20=EC=9E=84?= =?UTF-8?q?=EC=8B=9C=20=EC=97=85=EB=A1=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/misc.xml | 2 + .../x86/generate_cxx_metadata_1215_timing.txt | 2 + .../x86/generate_cxx_metadata_1356_timing.txt | 2 + .../x86/generate_cxx_metadata_1836_timing.txt | 2 + .../x86/generate_cxx_metadata_2116_timing.txt | 2 + .../x86/generate_cxx_metadata_2399_timing.txt | 2 + .../x86/generate_cxx_metadata_418_timing.txt | 2 + .../x86/generate_cxx_metadata_854_timing.txt | 2 + .../meta/x86/metadata_generation_record.json | 6 +- .../cxx/create_cxx_tasks_1055_timing.txt | 5 + .../cxx/create_cxx_tasks_1356_timing.txt | 10 + .../cxx/create_cxx_tasks_1970_timing.txt | 10 + .../cxx/create_cxx_tasks_418_timing.txt | 5 + .../cxx/create_cxx_tasks_695_timing.txt | 5 + .../com/qtagger/Activity/AccountList.java | 12 +- .../com/qtagger/Activity/CameraActivity.java | 18 +- .../java/com/qtagger/Database/DBHelper.java | 2 + .../main/java/com/qtagger/MainActivity.java | 1 - app/src/main/java/com/qtagger/test.java | 256 +++++++++++++++++- .../main/java/com/qtagger/util/Constants.java | 16 ++ .../java/com/qtagger/util/PositionData.java | 166 ++++++++++++ .../java/com/qtagger/util/SerialListener.java | 8 + .../java/com/qtagger/util/SerialService.java | 251 +++++++++++++++++ .../java/com/qtagger/util/SerialSocket.java | 102 +++++++ .../main/java/com/qtagger/util/TextUtil.java | 157 +++++++++++ .../main/java/com/qtagger/util/UsbItem.java | 17 ++ .../res/drawable/tablelayout_background.xml | 2 +- .../main/res/layout/activity_account_list.xml | 1 + app/src/main/res/layout/activity_test.xml | 22 ++ app/src/main/res/values/colors.xml | 1 + 30 files changed, 1079 insertions(+), 10 deletions(-) create mode 100644 OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1215_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1356_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1836_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_2116_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_2399_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_418_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_854_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/create_cxx_tasks_1055_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/create_cxx_tasks_1356_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/create_cxx_tasks_1970_timing.txt create mode 100644 OpenCV/build/intermediates/cxx/create_cxx_tasks_695_timing.txt create mode 100644 app/src/main/java/com/qtagger/util/Constants.java create mode 100644 app/src/main/java/com/qtagger/util/PositionData.java create mode 100644 app/src/main/java/com/qtagger/util/SerialListener.java create mode 100644 app/src/main/java/com/qtagger/util/SerialService.java create mode 100644 app/src/main/java/com/qtagger/util/SerialSocket.java create mode 100644 app/src/main/java/com/qtagger/util/TextUtil.java create mode 100644 app/src/main/java/com/qtagger/util/UsbItem.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 3a6be18..9a451dd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -143,6 +143,8 @@ + + diff --git a/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1215_timing.txt b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1215_timing.txt new file mode 100644 index 0000000..9708df4 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1215_timing.txt @@ -0,0 +1,2 @@ +# C/C++ build system timings + diff --git a/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1356_timing.txt b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1356_timing.txt new file mode 100644 index 0000000..9708df4 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1356_timing.txt @@ -0,0 +1,2 @@ +# C/C++ build system timings + diff --git a/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1836_timing.txt b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1836_timing.txt new file mode 100644 index 0000000..9708df4 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_1836_timing.txt @@ -0,0 +1,2 @@ +# C/C++ build system timings + diff --git a/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_2116_timing.txt b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_2116_timing.txt new file mode 100644 index 0000000..9708df4 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_2116_timing.txt @@ -0,0 +1,2 @@ +# C/C++ build system timings + diff --git a/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_2399_timing.txt b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_2399_timing.txt new file mode 100644 index 0000000..9708df4 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_2399_timing.txt @@ -0,0 +1,2 @@ +# C/C++ build system timings + diff --git a/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_418_timing.txt b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_418_timing.txt new file mode 100644 index 0000000..9708df4 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_418_timing.txt @@ -0,0 +1,2 @@ +# C/C++ build system timings + diff --git a/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_854_timing.txt b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_854_timing.txt new file mode 100644 index 0000000..9708df4 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/generate_cxx_metadata_854_timing.txt @@ -0,0 +1,2 @@ +# C/C++ build system timings + diff --git a/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/metadata_generation_record.json b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/metadata_generation_record.json index 0dfbfff..274c5c4 100644 --- a/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/metadata_generation_record.json +++ b/OpenCV/build/intermediates/cxx/Debug/3w3p486t/meta/x86/metadata_generation_record.json @@ -11,7 +11,7 @@ "fieldsDescending": {} }, "memoizedSize": -1, - "memoizedHashCode": 163502203 + "memoizedHashCode": -1078900285 }, { "level_": 0, @@ -25,7 +25,7 @@ "fieldsDescending": {} }, "memoizedSize": -1, - "memoizedHashCode": -2106990726 + "memoizedHashCode": 945574082 }, { "level_": 0, @@ -39,6 +39,6 @@ "fieldsDescending": {} }, "memoizedSize": -1, - "memoizedHashCode": -44608632 + "memoizedHashCode": -1287011120 } ] \ No newline at end of file diff --git a/OpenCV/build/intermediates/cxx/create_cxx_tasks_1055_timing.txt b/OpenCV/build/intermediates/cxx/create_cxx_tasks_1055_timing.txt new file mode 100644 index 0000000..7a1061f --- /dev/null +++ b/OpenCV/build/intermediates/cxx/create_cxx_tasks_1055_timing.txt @@ -0,0 +1,5 @@ +# C/C++ build system timings +create_cxx_tasks + create-initial-cxx-model 25ms +create_cxx_tasks completed in 27ms + diff --git a/OpenCV/build/intermediates/cxx/create_cxx_tasks_1356_timing.txt b/OpenCV/build/intermediates/cxx/create_cxx_tasks_1356_timing.txt new file mode 100644 index 0000000..4187b32 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/create_cxx_tasks_1356_timing.txt @@ -0,0 +1,10 @@ +# C/C++ build system timings +create_cxx_tasks + create-initial-cxx-model 32ms +create_cxx_tasks completed in 32ms + +# C/C++ build system timings +create_cxx_tasks + create-initial-cxx-model 18ms +create_cxx_tasks completed in 19ms + diff --git a/OpenCV/build/intermediates/cxx/create_cxx_tasks_1970_timing.txt b/OpenCV/build/intermediates/cxx/create_cxx_tasks_1970_timing.txt new file mode 100644 index 0000000..cdbb923 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/create_cxx_tasks_1970_timing.txt @@ -0,0 +1,10 @@ +# C/C++ build system timings +create_cxx_tasks + create-initial-cxx-model 20ms +create_cxx_tasks completed in 21ms + +# C/C++ build system timings +create_cxx_tasks + create-initial-cxx-model 19ms +create_cxx_tasks completed in 19ms + diff --git a/OpenCV/build/intermediates/cxx/create_cxx_tasks_418_timing.txt b/OpenCV/build/intermediates/cxx/create_cxx_tasks_418_timing.txt index 3663ad8..79bcd7b 100644 --- a/OpenCV/build/intermediates/cxx/create_cxx_tasks_418_timing.txt +++ b/OpenCV/build/intermediates/cxx/create_cxx_tasks_418_timing.txt @@ -13,3 +13,8 @@ create_cxx_tasks create-initial-cxx-model 52ms create_cxx_tasks completed in 53ms +# C/C++ build system timings +create_cxx_tasks + create-initial-cxx-model 32ms +create_cxx_tasks completed in 35ms + diff --git a/OpenCV/build/intermediates/cxx/create_cxx_tasks_695_timing.txt b/OpenCV/build/intermediates/cxx/create_cxx_tasks_695_timing.txt new file mode 100644 index 0000000..05b71f4 --- /dev/null +++ b/OpenCV/build/intermediates/cxx/create_cxx_tasks_695_timing.txt @@ -0,0 +1,5 @@ +# C/C++ build system timings +create_cxx_tasks + create-initial-cxx-model 47ms +create_cxx_tasks completed in 48ms + diff --git a/app/src/main/java/com/qtagger/Activity/AccountList.java b/app/src/main/java/com/qtagger/Activity/AccountList.java index a32681f..5170947 100644 --- a/app/src/main/java/com/qtagger/Activity/AccountList.java +++ b/app/src/main/java/com/qtagger/Activity/AccountList.java @@ -4,6 +4,8 @@ import androidx.annotation.DrawableRes; import androidx.appcompat.app.AppCompatActivity; import androidx.constraintlayout.utils.widget.ImageFilterButton; +import android.app.AlertDialog; +import android.content.DialogInterface; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; @@ -16,6 +18,7 @@ import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; +import com.qtagger.Data.TestEnum; import com.qtagger.Database.AccountData; import com.qtagger.R; import com.qtagger.util.DataManager; @@ -105,8 +108,13 @@ public class AccountList extends AppCompatActivity { btnRemove.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - dataManager.dbHelper.deleteAccount(account); - linearLayout.removeView(tableLayout); + new AlertDialog.Builder(AccountList.this).setTitle("해당 계정을 삭제하시겠습니까?").setPositiveButton("확인", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dataManager.dbHelper.deleteAccount(account); + linearLayout.removeView(tableLayout); + } + }).setNegativeButton("취소", null).show(); } }); diff --git a/app/src/main/java/com/qtagger/Activity/CameraActivity.java b/app/src/main/java/com/qtagger/Activity/CameraActivity.java index 65df520..867ade4 100644 --- a/app/src/main/java/com/qtagger/Activity/CameraActivity.java +++ b/app/src/main/java/com/qtagger/Activity/CameraActivity.java @@ -33,6 +33,7 @@ import android.widget.Toast; import com.googlecode.tesseract.android.ResultIterator; import com.googlecode.tesseract.android.TessBaseAPI; +import com.hoho.android.usbserial.driver.UsbSerialPort; import com.qtagger.CustomPalette.CameraSurfaceView; import com.qtagger.Data.TestEnum; import com.qtagger.Database.ImageFile; @@ -80,6 +81,8 @@ public class CameraActivity extends AppCompatActivity { Bitmap resultBitmap,mOrigin; String filename, time,pkgData; String pkgNo = "K042022B000009"; + private UsbSerialPort usbSerialPort; + //private SerialService service; @@ -291,10 +294,21 @@ public class CameraActivity extends AppCompatActivity { String imgTitle = null; if(dbTestType.contains("Surface")) { - imgTitle = "표면방사선량 값 : 0.04 μSv/hr"; + if(pkgData.isEmpty()) { + imgTitle = "표면방사선량 값 : 0.04 μSv/hr"; + } + else + { + imgTitle = "표면방사선량 값 : " + pkgData + " μSv/hr"; + } } else if(dbTestType.contains("M1")){ - imgTitle = "1m방사선량 값 : 0.04 μSv/hr"; + if(pkgData.isEmpty()) { + imgTitle = "1m방사선량 값 : 0.04 μSv/hr"; + } + else{ + imgTitle = "1m방사선량 값 : "+pkgData+" μSv/hr"; + } } else if(dbTestType.compareTo(TestType.Weight.name()) == 0){ if(pkgData.isEmpty()) diff --git a/app/src/main/java/com/qtagger/Database/DBHelper.java b/app/src/main/java/com/qtagger/Database/DBHelper.java index b517c0d..df20f15 100644 --- a/app/src/main/java/com/qtagger/Database/DBHelper.java +++ b/app/src/main/java/com/qtagger/Database/DBHelper.java @@ -150,7 +150,9 @@ public class DBHelper extends SQLiteOpenHelper { } public void deleteAccount(AccountData ac){ + db = getWritableDatabase(); db.execSQL("Delete from Account where id = '"+ac.getId()+"'"); + db.close(); } public void insertFile(ImageFile f){ diff --git a/app/src/main/java/com/qtagger/MainActivity.java b/app/src/main/java/com/qtagger/MainActivity.java index e33b697..ac3b455 100644 --- a/app/src/main/java/com/qtagger/MainActivity.java +++ b/app/src/main/java/com/qtagger/MainActivity.java @@ -53,7 +53,6 @@ public class MainActivity extends AppCompatActivity { CommonHelper.CheckCameraPermission(this); CommonHelper.FolderClear(); dataManager.dbHelper = new DBHelper(getApplicationContext()); - //dataManager.nuclideDataMap = CommonHelper.getAllNuclideData(); //테스트용도 나중에 삭제 findViewById(R.id.imageView).setOnClickListener(v -> { diff --git a/app/src/main/java/com/qtagger/test.java b/app/src/main/java/com/qtagger/test.java index f21307c..5ccb5e5 100644 --- a/app/src/main/java/com/qtagger/test.java +++ b/app/src/main/java/com/qtagger/test.java @@ -8,8 +8,12 @@ import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import android.annotation.SuppressLint; +import android.app.PendingIntent; import android.app.ProgressDialog; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -17,26 +21,49 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbManager; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.text.Editable; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.style.ForegroundColorSpan; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; import android.widget.Toast; +import android.widget.ToggleButton; import com.googlecode.tesseract.android.ResultIterator; import com.googlecode.tesseract.android.TessBaseAPI; +import com.hoho.android.usbserial.driver.UsbSerialDriver; +import com.hoho.android.usbserial.driver.UsbSerialPort; +import com.hoho.android.usbserial.driver.UsbSerialProber; import com.qtagger.Activity.SyncActivity; +import com.qtagger.Data.CustomProber; import com.qtagger.Data.TestEnum; import com.qtagger.Database.PKG; +import com.qtagger.util.Constants; import com.qtagger.util.DataManager; import com.qtagger.util.OracleQueryStr; +import com.qtagger.util.PositionData; +import com.qtagger.util.SerialListener; +import com.qtagger.util.SerialService; +import com.qtagger.util.SerialSocket; +import com.qtagger.util.TextUtil; import org.json.JSONObject; import org.opencv.android.OpenCVLoader; @@ -68,14 +95,32 @@ import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.EnumSet; import java.util.List; import java.util.stream.Collectors; -public class test extends AppCompatActivity { +public class test extends AppCompatActivity implements SerialListener { static { System.loadLibrary("native-lib"); } + private enum Connected { False, Pending, True } + private Connected connected = Connected.False; + //private final BroadcastReceiver broadcastReceiver + private boolean controlLinesEnabled = false; + private boolean hexEnabled = true; + public boolean modeCheck = false; + public boolean bStart = false; + private boolean pendingNewline = false; + private int deviceId, portNum, baudRate; + private String newline = TextUtil.newline_crlf; + private UsbSerialPort usbSerialPort; + private SerialService service; + private ControlLines controlLines; + private PositionData positionData; + + public Context mContext; + TextView receiveText; ListView list1; ImageView imageView; private DataManager dataManager; @@ -92,6 +137,7 @@ public class test extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); + receiveText = findViewById(R.id.receiveText); mContext = this; dataManager = DataManager.getInstance(); list1 = findViewById(R.id.list_test); @@ -250,6 +296,214 @@ public class test extends AppCompatActivity { }); } + + private void connect() { + connect(null); + } + + private void connect(Boolean permissionGranted) { + UsbDevice device = null; + UsbManager usbManager = (UsbManager) this.getSystemService(Context.USB_SERVICE); + for(UsbDevice v : usbManager.getDeviceList().values()) + if(v.getDeviceId() == deviceId) + device = v; + if(device == null) { + status("connection failed: device not found"); + return; + } + UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(device); + if(driver == null) { + driver = CustomProber.getCustomProber().probeDevice(device); + } + if(driver == null) { + status("connection failed: no driver for device"); + return; + } + if(driver.getPorts().size() < portNum) { + status("connection failed: not enough ports at device"); + return; + } + usbSerialPort = driver.getPorts().get(portNum); + UsbDeviceConnection usbConnection = usbManager.openDevice(driver.getDevice()); + if(usbConnection == null && permissionGranted == null && !usbManager.hasPermission(driver.getDevice())) { + PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(test.this, 0, new Intent(Constants.INTENT_ACTION_GRANT_USB), 0); + usbManager.requestPermission(driver.getDevice(), usbPermissionIntent); + return; + } + if(usbConnection == null) { + if (!usbManager.hasPermission(driver.getDevice())) + status("connection failed: permission denied"); + else + status("connection failed: open failed"); + return; + } + + connected = Connected.Pending; + try { + usbSerialPort.open(usbConnection); + usbSerialPort.setParameters(baudRate, UsbSerialPort.DATABITS_8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); + SerialSocket socket = new SerialSocket(this.getApplicationContext(), usbConnection, usbSerialPort); + service.connect(socket); + // usb connect is not asynchronous. connect-success and connect-error are returned immediately from socket.connect + // for consistency to bluetooth/bluetooth-LE app use same SerialListener and SerialService classes + onSerialConnect(); + } catch (Exception e) { + onSerialConnectError(e); + } + } + + @Override + public void onSerialConnect() { + status("connected"); + connected = Connected.True; + if(controlLinesEnabled) { + controlLines.start(); + } + } + + @Override + public void onSerialConnectError(Exception e) { + status("connection failed: " + e.getMessage()); + disconnect(); + } + + @Override + public void onSerialRead(byte[] data) { + receive(data); + } + + @Override + public void onSerialIoError(Exception e) { + status("connection lost: " + e.getMessage()); + disconnect(); + } + + @Override + public void onPointerCaptureChanged(boolean hasCapture) { + super.onPointerCaptureChanged(hasCapture); + } + private void disconnect() { + connected = Connected.False; + controlLines.stop(); + service.disconnect(); + usbSerialPort = null; + } + + void status(String str) { + SpannableStringBuilder spn = new SpannableStringBuilder(str+'\n'); + spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.black)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + //receiveText.append(spn); + } + + private void receive(byte[] data) { + if(hexEnabled) { + if(resultStr.length() > 0) { + resultStr += " "; + } + resultStr += TextUtil.toHexString(data); + + if(modeCheck) { + String tempStr = resultStr.replaceAll(" ", ""); + + Log.i("jjhtest length", tempStr.length()+""); + if(tempStr.length() == 72) { + String value = tempStr.substring(42, 50); + Long i = Long.parseLong(value, 16); + Float f = Float.intBitsToFloat(i.intValue()); + double df = f; + df = df / 10; + + Log.i("jjhtest", String.format("%.5f", df) + " μSv/hr"); + receiveText.setText(String.format("%.5f", df) + " μSv/hr"); + positionData.setCurrentValue(String.format("%.5f", df)); + bStart = true; + + } + } + } else { + String msg = new String(data); + if(newline.equals(TextUtil.newline_crlf) && msg.length() > 0) { + // don't show CR as ^M if directly before LF + msg = msg.replace(TextUtil.newline_crlf, TextUtil.newline_lf); + // special handling if CR and LF come in separate fragments + if (pendingNewline && msg.charAt(0) == '\n') { + Editable edt = receiveText.getEditableText(); + if (edt != null && edt.length() > 1) + edt.replace(edt.length() - 2, edt.length(), ""); + } + pendingNewline = msg.charAt(msg.length() - 1) == '\r'; + } + receiveText.append(TextUtil.toCaretString(msg, newline.length() != 0)); + } + } + + class ControlLines { + private static final int refreshInterval = 200; // msec + + private final Handler mainLooper; + private final Runnable runnable; + //private final LinearLayout frame; + private final ToggleButton rtsBtn; + + ControlLines(View view) { + mainLooper = new Handler(Looper.getMainLooper()); + runnable = this::run; // w/o explicit Runnable, a new lambda would be created on each postDelayed, which would not be found again by removeCallbacks + + rtsBtn = view.findViewById(R.id.rtsBtn); + rtsBtn.setOnClickListener(this::toggle); + //dtrBtn.setOnClickListener(this::toggle); + } + + private void toggle(View v) { + ToggleButton btn = (ToggleButton) v; + if (connected != Connected.True) { + btn.setChecked(!btn.isChecked()); + Toast.makeText(test.this, "not connected", Toast.LENGTH_SHORT).show(); + return; + } + String ctrl = ""; + try { + if (btn.equals(rtsBtn)) { ctrl = "RTS"; usbSerialPort.setRTS(btn.isChecked()); } + if (btn.equals(rtsBtn)) { ctrl = "DTR"; usbSerialPort.setDTR(btn.isChecked()); } + } catch (IOException e) { + status("set" + ctrl + " failed: " + e.getMessage()); + } + } + + private void run() { + if (connected != Connected.True) + return; + try { + EnumSet controlLines = usbSerialPort.getControlLines(); + rtsBtn.setChecked(controlLines.contains(UsbSerialPort.ControlLine.RTS)); + mainLooper.postDelayed(runnable, refreshInterval); + //send("AA000E0040FF00000002015DFFAA"); + } catch (IOException e) { + status("getControlLines() failed: " + e.getMessage() + " -> stopped control line refresh"); + } + } + + void start() { + + if (connected != Connected.True) + return; + try { + EnumSet controlLines = usbSerialPort.getSupportedControlLines(); + if (!controlLines.contains(UsbSerialPort.ControlLine.RTS)) rtsBtn.setVisibility(View.INVISIBLE); + run(); + } catch (IOException e) { + Toast.makeText(test.this, "getSupportedControlLines() failed: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + } + } + + void stop() { + mainLooper.removeCallbacks(runnable); + rtsBtn.setChecked(false); + } + } + + + private class AsyncTess extends AsyncTask { @Override protected String doInBackground(Bitmap... mRelativeParams) { diff --git a/app/src/main/java/com/qtagger/util/Constants.java b/app/src/main/java/com/qtagger/util/Constants.java new file mode 100644 index 0000000..a200144 --- /dev/null +++ b/app/src/main/java/com/qtagger/util/Constants.java @@ -0,0 +1,16 @@ +package com.qtagger.util; + +public class Constants { + + // values have to be globally unique + public static final String INTENT_ACTION_GRANT_USB = com.qtagger.BuildConfig.APPLICATION_ID + ".GRANT_USB"; + static final String INTENT_ACTION_DISCONNECT = com.qtagger.BuildConfig.APPLICATION_ID + ".Disconnect"; + static final String NOTIFICATION_CHANNEL = com.qtagger.BuildConfig.APPLICATION_ID + ".Channel"; + static final String INTENT_CLASS_MAIN_ACTIVITY = com.qtagger.BuildConfig.APPLICATION_ID + ".MainActivity"; + + // values have to be unique within each app + static final int NOTIFY_MANAGER_START_FOREGROUND_SERVICE = 1001; + + private Constants() {} +} + diff --git a/app/src/main/java/com/qtagger/util/PositionData.java b/app/src/main/java/com/qtagger/util/PositionData.java new file mode 100644 index 0000000..42eee56 --- /dev/null +++ b/app/src/main/java/com/qtagger/util/PositionData.java @@ -0,0 +1,166 @@ +package com.qtagger.util; + +import java.io.Serializable; +import java.util.HashMap; + +public class PositionData implements Serializable { + + private String title; + + private HashMap resultBitmapPath; + private HashMap resultValue; + private HashMap resultDatetime; + + private String front; + private String back; + private String left; + private String right; + + private String currentTypeMain; + private String currentTypeSub; + private String + currentBitmapPath; + private String currentValue; + private String currentDatetime; + + public PositionData(){ + + resultBitmapPath = new HashMap(); + resultValue = new HashMap(); + resultDatetime = new HashMap(); + + title = null; + currentTypeMain = null; + currentTypeSub = null; + currentBitmapPath = null; + } + + public PositionData(String title){ + + resultBitmapPath = new HashMap(); + resultValue = new HashMap(); + resultDatetime = new HashMap(); + + this.title = title; + currentTypeMain = null; + currentTypeSub = null; + currentBitmapPath = null; + } + + public void init() { + resultBitmapPath = new HashMap(); + resultValue = new HashMap(); + resultDatetime = new HashMap(); + + currentTypeMain = null; + currentTypeSub = null; + currentBitmapPath = null; + + } + + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getFront() { + return front; + } + + public void setFront(String front) { + this.front = front; + } + + public String getBack() { + return back; + } + + public void setBack(String back) { + this.back = back; + } + + public String getLeft() { + return left; + } + + public void setLeft(String left) { + this.left = left; + } + + public String getRight() { + return right; + } + + public void setRight(String right) { + this.right = right; + } + + + public String getCurrentTypeMain() { + return currentTypeMain; + } + + public void setCurrentTypeMain(String currentTypeMain) { + this.currentTypeMain = currentTypeMain; + } + + public String getCurrentTypeSub() { + return currentTypeSub; + } + + public void setCurrentTypeSub(String currentTypeSub) { + this.currentTypeSub = currentTypeSub; + } + + public String getCurrentBitmapPath() { + return currentBitmapPath; + } + + public void setCurrentBitmapPath(String currentBitmapPath) { + this.currentBitmapPath = currentBitmapPath; + } + + public String getCurrentValue() { + return currentValue; + } + + public void setCurrentValue(String currentValue) { + this.currentValue = currentValue; + } + + public String getCurrentDatetime() { + return currentDatetime; + } + + public void setCurrentDatetime(String currentDatetime) { + this.currentDatetime = currentDatetime; + } + + public HashMap getResultValue() { + return resultValue; + } + + public void setResultValue(HashMap resultValue) { + this.resultValue = resultValue; + } + + public HashMap getResultBitmapPath() { + return resultBitmapPath; + } + + public void setResultBitmapPath(HashMap resultBitmapPath) { + this.resultBitmapPath = resultBitmapPath; + } + + public HashMap getResultDatetime() { + return resultDatetime; + } + + public void setResultDatetime(HashMap resultDatetime) { + this.resultDatetime = resultDatetime; + } +} diff --git a/app/src/main/java/com/qtagger/util/SerialListener.java b/app/src/main/java/com/qtagger/util/SerialListener.java new file mode 100644 index 0000000..667467e --- /dev/null +++ b/app/src/main/java/com/qtagger/util/SerialListener.java @@ -0,0 +1,8 @@ +package com.qtagger.util; + +public interface SerialListener { + void onSerialConnect (); + void onSerialConnectError (Exception e); + void onSerialRead (byte[] data); + void onSerialIoError (Exception e); +} diff --git a/app/src/main/java/com/qtagger/util/SerialService.java b/app/src/main/java/com/qtagger/util/SerialService.java new file mode 100644 index 0000000..95ba416 --- /dev/null +++ b/app/src/main/java/com/qtagger/util/SerialService.java @@ -0,0 +1,251 @@ +package com.qtagger.util; + +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.os.Binder; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; + +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; + +import com.qtagger.R; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.Queue; + +public class SerialService extends Service implements SerialListener { + + class SerialBinder extends Binder { + SerialService getService() { return SerialService.this; } + } + + private enum QueueType {Connect, ConnectError, Read, IoError} + + private class QueueItem { + QueueType type; + byte[] data; + Exception e; + + QueueItem(QueueType type, byte[] data, Exception e) { this.type=type; this.data=data; this.e=e; } + } + + private final Handler mainLooper; + private final IBinder binder; + private final Queue queue1, queue2; + + private SerialSocket socket; + private SerialListener listener; + private boolean connected; + + /** + * Lifecylce + */ + public SerialService() { + mainLooper = new Handler(Looper.getMainLooper()); + binder = new SerialBinder(); + queue1 = new LinkedList<>(); + queue2 = new LinkedList<>(); + } + + @Override + public void onDestroy() { + cancelNotification(); + disconnect(); + super.onDestroy(); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + /** + * Api + */ + public void connect(SerialSocket socket) throws IOException { + socket.connect(this); + this.socket = socket; + connected = true; + } + + public void disconnect() { + connected = false; // ignore data,errors while disconnecting + cancelNotification(); + if(socket != null) { + socket.disconnect(); + socket = null; + } + } + + public void write(byte[] data) throws IOException { + if(!connected) + throw new IOException("not connected"); + socket.write(data); + } + + public void attach(SerialListener listener) { + if(Looper.getMainLooper().getThread() != Thread.currentThread()) + throw new IllegalArgumentException("not in main thread"); + cancelNotification(); + // use synchronized() to prevent new items in queue2 + // new items will not be added to queue1 because mainLooper.post and attach() run in main thread + synchronized (this) { + this.listener = listener; + } + for(QueueItem item : queue1) { + switch(item.type) { + case Connect: listener.onSerialConnect (); break; + case ConnectError: listener.onSerialConnectError (item.e); break; + case Read: listener.onSerialRead (item.data); break; + case IoError: listener.onSerialIoError (item.e); break; + } + } + for(QueueItem item : queue2) { + switch(item.type) { + case Connect: listener.onSerialConnect (); break; + case ConnectError: listener.onSerialConnectError (item.e); break; + case Read: listener.onSerialRead (item.data); break; + case IoError: listener.onSerialIoError (item.e); break; + } + } + queue1.clear(); + queue2.clear(); + } + + public void detach() { + if(connected) + createNotification(); + // items already in event queue (posted before detach() to mainLooper) will end up in queue1 + // items occurring later, will be moved directly to queue2 + // detach() and mainLooper.post run in the main thread, so all items are caught + listener = null; + } + + private void createNotification() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel nc = new NotificationChannel(Constants.NOTIFICATION_CHANNEL, "Background service", NotificationManager.IMPORTANCE_LOW); + nc.setShowBadge(false); + NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + nm.createNotificationChannel(nc); + } + Intent disconnectIntent = new Intent() + .setAction(Constants.INTENT_ACTION_DISCONNECT); + Intent restartIntent = new Intent() + .setClassName(this, Constants.INTENT_CLASS_MAIN_ACTIVITY) + .setAction(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_LAUNCHER); + PendingIntent disconnectPendingIntent = PendingIntent.getBroadcast(this, 1, disconnectIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent restartPendingIntent = PendingIntent.getActivity(this, 1, restartIntent, PendingIntent.FLAG_UPDATE_CURRENT); + NotificationCompat.Builder builder = new NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNEL) + .setSmallIcon(R.drawable.logo) + .setColor(getResources().getColor(R.color.white)) + .setContentTitle(getResources().getString(R.string.app_name)) + .setContentText(socket != null ? "Connected to "+socket.getName() : "Background Service") + .setContentIntent(restartPendingIntent) + .setOngoing(true) + .addAction(new NotificationCompat.Action(android.R.drawable.ic_menu_close_clear_cancel, "Disconnect", disconnectPendingIntent)); + // @drawable/ic_notification created with Android Studio -> New -> Image Asset using @color/colorPrimaryDark as background color + // Android < API 21 does not support vectorDrawables in notifications, so both drawables used here, are created as .png instead of .xml + Notification notification = builder.build(); + startForeground(Constants.NOTIFY_MANAGER_START_FOREGROUND_SERVICE, notification); + } + + private void cancelNotification() { + stopForeground(true); + } + + /** + * SerialListener + */ + public void onSerialConnect() { + if(connected) { + synchronized (this) { + if (listener != null) { + mainLooper.post(() -> { + if (listener != null) { + listener.onSerialConnect(); + } else { + queue1.add(new QueueItem(QueueType.Connect, null, null)); + } + }); + } else { + queue2.add(new QueueItem(QueueType.Connect, null, null)); + } + } + } + } + + public void onSerialConnectError(Exception e) { + if(connected) { + synchronized (this) { + if (listener != null) { + mainLooper.post(() -> { + if (listener != null) { + listener.onSerialConnectError(e); + } else { + queue1.add(new QueueItem(QueueType.ConnectError, null, e)); + cancelNotification(); + disconnect(); + } + }); + } else { + queue2.add(new QueueItem(QueueType.ConnectError, null, e)); + cancelNotification(); + disconnect(); + } + } + } + } + + public void onSerialRead(byte[] data) { + if(connected) { + synchronized (this) { + if (listener != null) { + mainLooper.post(() -> { + if (listener != null) { + listener.onSerialRead(data); + } else { + queue1.add(new QueueItem(QueueType.Read, data, null)); + } + }); + } else { + queue2.add(new QueueItem(QueueType.Read, data, null)); + } + } + } + } + + public void onSerialIoError(Exception e) { + if(connected) { + synchronized (this) { + if (listener != null) { + mainLooper.post(() -> { + if (listener != null) { + listener.onSerialIoError(e); + } else { + queue1.add(new QueueItem(QueueType.IoError, null, e)); + cancelNotification(); + disconnect(); + } + }); + } else { + queue2.add(new QueueItem(QueueType.IoError, null, e)); + cancelNotification(); + disconnect(); + } + } + } + } + +} + diff --git a/app/src/main/java/com/qtagger/util/SerialSocket.java b/app/src/main/java/com/qtagger/util/SerialSocket.java new file mode 100644 index 0000000..7da553c --- /dev/null +++ b/app/src/main/java/com/qtagger/util/SerialSocket.java @@ -0,0 +1,102 @@ +package com.qtagger.util; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbDeviceConnection; + +import com.hoho.android.usbserial.driver.UsbSerialPort; +import com.hoho.android.usbserial.util.SerialInputOutputManager; + +import java.io.IOException; +import java.security.InvalidParameterException; +import java.util.concurrent.Executors; + +public class SerialSocket implements SerialInputOutputManager.Listener { + + private static final int WRITE_WAIT_MILLIS = 2000; // 0 blocked infinitely on unprogrammed arduino + + private final BroadcastReceiver disconnectBroadcastReceiver; + + private Context context; + private SerialListener listener; + private UsbDeviceConnection connection; + private UsbSerialPort serialPort; + private SerialInputOutputManager ioManager; + + public SerialSocket(Context context, UsbDeviceConnection connection, UsbSerialPort serialPort) { + if(context instanceof Activity) + throw new InvalidParameterException("expected non UI context"); + this.context = context; + this.connection = connection; + this.serialPort = serialPort; + disconnectBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (listener != null) + listener.onSerialIoError(new IOException("background disconnect")); + disconnect(); // disconnect now, else would be queued until UI re-attached + } + }; + } + + String getName() { return serialPort.getDriver().getClass().getSimpleName().replace("SerialDriver",""); } + + void connect(SerialListener listener) throws IOException { + this.listener = listener; + context.registerReceiver(disconnectBroadcastReceiver, new IntentFilter(Constants.INTENT_ACTION_DISCONNECT)); + serialPort.setDTR(true); // for arduino, ... + serialPort.setRTS(true); + ioManager = new SerialInputOutputManager(serialPort, this); + Executors.newSingleThreadExecutor().submit(ioManager); + } + + void disconnect() { + listener = null; // ignore remaining data and errors + if (ioManager != null) { + ioManager.setListener(null); + ioManager.stop(); + ioManager = null; + } + if (serialPort != null) { + try { + serialPort.setDTR(false); + serialPort.setRTS(false); + } catch (Exception ignored) { + } + try { + serialPort.close(); + } catch (Exception ignored) { + } + serialPort = null; + } + if(connection != null) { + connection.close(); + connection = null; + } + try { + context.unregisterReceiver(disconnectBroadcastReceiver); + } catch (Exception ignored) { + } + } + + void write(byte[] data) throws IOException { + if(serialPort == null) + throw new IOException("not connected"); + serialPort.write(data, WRITE_WAIT_MILLIS); + } + + @Override + public void onNewData(byte[] data) { + if(listener != null) + listener.onSerialRead(data); + } + + @Override + public void onRunError(Exception e) { + if (listener != null) + listener.onSerialIoError(e); + } +} diff --git a/app/src/main/java/com/qtagger/util/TextUtil.java b/app/src/main/java/com/qtagger/util/TextUtil.java new file mode 100644 index 0000000..2c3c68d --- /dev/null +++ b/app/src/main/java/com/qtagger/util/TextUtil.java @@ -0,0 +1,157 @@ +package com.qtagger.util; + +import android.text.Editable; +import android.text.InputType; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextWatcher; +import android.text.style.BackgroundColorSpan; +import android.widget.TextView; + +import androidx.annotation.ColorInt; + +import java.io.ByteArrayOutputStream; + +public final class TextUtil { + + @ColorInt + static int caretBackground = 0xff666666; + + public final static String newline_crlf = "\r\n"; + public final static String newline_lf = "\n"; + + static byte[] fromHexString(final CharSequence s) { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + byte b = 0; + int nibble = 0; + for(int pos = 0; pos='0' && c<='9') { nibble++; b *= 16; b += c-'0'; } + if(c>='A' && c<='F') { nibble++; b *= 16; b += c-'A'+10; } + if(c>='a' && c<='f') { nibble++; b *= 16; b += c-'a'+10; } + } + if(nibble>0) + buf.write(b); + return buf.toByteArray(); + } + + public static String toHexString(final byte[] buf) { + return toHexString(buf, 0, buf.length); + } + + static String toHexString(final byte[] buf, int begin, int end) { + StringBuilder sb = new StringBuilder(3*(end-begin)); + toHexString(sb, buf, begin, end); + return sb.toString(); + } + + static void toHexString(StringBuilder sb, final byte[] buf) { + toHexString(sb, buf, 0, buf.length); + } + + static void toHexString(StringBuilder sb, final byte[] buf, int begin, int end) { + for(int pos=begin; pos0) + sb.append(' '); + int c; + c = (buf[pos]&0xff) / 16; + if(c >= 10) c += 'A'-10; + else c += '0'; + sb.append((char)c); + c = (buf[pos]&0xff) % 16; + if(c >= 10) c += 'A'-10; + else c += '0'; + sb.append((char)c); + } + } + + /** + * use https://en.wikipedia.org/wiki/Caret_notation to avoid invisible control characters + */ + public static CharSequence toCaretString(CharSequence s, boolean keepNewline) { + return toCaretString(s, keepNewline, s.length()); + } + + static CharSequence toCaretString(CharSequence s, boolean keepNewline, int length) { + boolean found = false; + for (int pos = 0; pos < length; pos++) { + if (s.charAt(pos) < 32 && (!keepNewline ||s.charAt(pos)!='\n')) { + found = true; + break; + } + } + if(!found) + return s; + SpannableStringBuilder sb = new SpannableStringBuilder(); + for(int pos=0; pos= '0' && c <= '9') sb.append(c); + if(c >= 'A' && c <= 'F') sb.append(c); + if(c >= 'a' && c <= 'f') sb.append((char)(c+'A'-'a')); + } + for(i=2; i + android:color="@color/white"/> diff --git a/app/src/main/res/layout/activity_test.xml b/app/src/main/res/layout/activity_test.xml index 200e9e1..456b8cd 100644 --- a/app/src/main/res/layout/activity_test.xml +++ b/app/src/main/res/layout/activity_test.xml @@ -62,4 +62,26 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + +