시연용 임시 업로드

This commit is contained in:
DESKTOP-1A3P75F\lee
2022-09-21 11:53:27 +09:00
parent 5dadb6b7c2
commit 024e26e041
30 changed files with 1079 additions and 10 deletions

2
.idea/misc.xml generated
View File

@@ -143,6 +143,8 @@
<entry key="..\:/QTagger/a/QTagger_Android9/app/src/main/res/drawable/image_squrebutton_background.xml" value="0.4055" />
<entry key="..\:/QTagger/a/QTagger_Android9/app/src/main/res/drawable/nuclide_item_background.xml" value="0.4045" />
<entry key="..\:/QTagger/a/QTagger_Android9/app/src/main/res/drawable/squrebutton_background.xml" value="0.4055" />
<entry key="..\:/QTagger/a/QTagger_Android9/app/src/main/res/drawable/tablelayout_background.xml" value="0.367" />
<entry key="..\:/QTagger/a/QTagger_Android9/app/src/main/res/layout/activity_account_list.xml" value="0.40625" />
<entry key="..\:/QTagger/a/QTagger_Android9/app/src/main/res/layout/activity_camera.xml" value="0.40625" />
<entry key="..\:/QTagger/a/QTagger_Android9/app/src/main/res/layout/activity_more.xml" value="0.40625" />
<entry key="..\:/QTagger/a/QTagger_Android9/app/src/main/res/layout/activity_search.xml" value="0.40625" />

View File

@@ -0,0 +1,2 @@
# C/C++ build system timings

View File

@@ -0,0 +1,2 @@
# C/C++ build system timings

View File

@@ -0,0 +1,2 @@
# C/C++ build system timings

View File

@@ -0,0 +1,2 @@
# C/C++ build system timings

View File

@@ -0,0 +1,2 @@
# C/C++ build system timings

View File

@@ -0,0 +1,2 @@
# C/C++ build system timings

View File

@@ -0,0 +1,2 @@
# C/C++ build system timings

View File

@@ -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
}
]

View File

@@ -0,0 +1,5 @@
# C/C++ build system timings
create_cxx_tasks
create-initial-cxx-model 25ms
create_cxx_tasks completed in 27ms

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,5 @@
# C/C++ build system timings
create_cxx_tasks
create-initial-cxx-model 47ms
create_cxx_tasks completed in 48ms

View File

@@ -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();
}
});

View File

@@ -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())

View File

@@ -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){

View File

@@ -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 -> {

View File

@@ -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<UsbSerialPort.ControlLine> 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<UsbSerialPort.ControlLine> 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<Bitmap, Integer, String> {
@Override
protected String doInBackground(Bitmap... mRelativeParams) {

View File

@@ -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() {}
}

View File

@@ -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<String, String> resultBitmapPath;
private HashMap<String, String> resultValue;
private HashMap<String, String> 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<String, String>();
resultValue = new HashMap<String, String>();
resultDatetime = new HashMap<String, String>();
title = null;
currentTypeMain = null;
currentTypeSub = null;
currentBitmapPath = null;
}
public PositionData(String title){
resultBitmapPath = new HashMap<String, String>();
resultValue = new HashMap<String, String>();
resultDatetime = new HashMap<String, String>();
this.title = title;
currentTypeMain = null;
currentTypeSub = null;
currentBitmapPath = null;
}
public void init() {
resultBitmapPath = new HashMap<String, String>();
resultValue = new HashMap<String, String>();
resultDatetime = new HashMap<String, String>();
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<String, String> getResultValue() {
return resultValue;
}
public void setResultValue(HashMap<String, String> resultValue) {
this.resultValue = resultValue;
}
public HashMap<String, String> getResultBitmapPath() {
return resultBitmapPath;
}
public void setResultBitmapPath(HashMap<String, String> resultBitmapPath) {
this.resultBitmapPath = resultBitmapPath;
}
public HashMap<String, String> getResultDatetime() {
return resultDatetime;
}
public void setResultDatetime(HashMap<String, String> resultDatetime) {
this.resultDatetime = resultDatetime;
}
}

View File

@@ -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);
}

View File

@@ -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<QueueItem> 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();
}
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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<s.length(); pos++) {
if(nibble==2) {
buf.write(b);
nibble = 0;
b = 0;
}
int c = s.charAt(pos);
if(c>='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; pos<end; pos++) {
if(sb.length()>0)
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<length; pos++)
if (s.charAt(pos) < 32 && (!keepNewline ||s.charAt(pos)!='\n')) {
sb.append('^');
sb.append((char)(s.charAt(pos) + 64));
sb.setSpan(new BackgroundColorSpan(caretBackground), sb.length()-2, sb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
sb.append(s.charAt(pos));
}
return sb;
}
static class HexWatcher implements TextWatcher {
private final TextView view;
private final StringBuilder sb = new StringBuilder();
private boolean self = false;
private boolean enabled = false;
HexWatcher(TextView view) {
this.view = view;
}
void enable(boolean enable) {
if(enable) {
view.setInputType(InputType.TYPE_CLASS_TEXT + InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
} else {
view.setInputType(InputType.TYPE_CLASS_TEXT + InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
}
enabled = enable;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if(!enabled || self)
return;
sb.delete(0,sb.length());
int i;
for(i=0; i<s.length(); i++) {
char c = s.charAt(i);
if(c >= '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<sb.length(); i+=3)
sb.insert(i,' ');
final String s2 = sb.toString();
if(!s2.equals(s.toString())) {
self = true;
s.replace(0, s.length(), s2);
self = false;
}
}
}
}

View File

@@ -0,0 +1,17 @@
package com.qtagger.util;
import android.hardware.usb.UsbDevice;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
public class UsbItem {
UsbDevice device;
int port;
UsbSerialDriver driver;
UsbItem(UsbDevice device, int port, UsbSerialDriver driver) {
this.device = device;
this.port = port;
this.driver = driver;
}
}

View File

@@ -4,7 +4,7 @@
<!-- #f5f5f5" 색으로 채움 -->
<solid
android:color="#CFD9FB"/>
android:color="@color/white"/>
<corners
android:bottomLeftRadius="12dp"
android:bottomRightRadius="12dp"

View File

@@ -47,6 +47,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="64dp"
android:background="#F5F5F5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view"
tools:ignore="SpeakableTextPresentCheck">

View File

@@ -62,4 +62,26 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/receiveText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="receiveText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/button3"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/rtsBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="15dp"
android:text="Button"
app:layout_constraintEnd_toStartOf="@+id/receiveText"
app:layout_constraintStart_toEndOf="@+id/button3"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -7,4 +7,5 @@
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="colorPrimary">#d84315</color>
</resources>