diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/src/main/jniLibs/arm64-v8a/libserial_port.so b/app/src/main/jniLibs/arm64-v8a/libserial_port.so new file mode 100755 index 0000000..fe664b1 --- /dev/null +++ b/app/src/main/jniLibs/arm64-v8a/libserial_port.so Binary files differ diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/src/main/jniLibs/arm64-v8a/libserial_port.so b/app/src/main/jniLibs/arm64-v8a/libserial_port.so new file mode 100755 index 0000000..fe664b1 --- /dev/null +++ b/app/src/main/jniLibs/arm64-v8a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libserial_port.so b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so new file mode 100755 index 0000000..88819f8 --- /dev/null +++ b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so Binary files differ diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/src/main/jniLibs/arm64-v8a/libserial_port.so b/app/src/main/jniLibs/arm64-v8a/libserial_port.so new file mode 100755 index 0000000..fe664b1 --- /dev/null +++ b/app/src/main/jniLibs/arm64-v8a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libserial_port.so b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so new file mode 100755 index 0000000..88819f8 --- /dev/null +++ b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86/libserial_port.so b/app/src/main/jniLibs/x86/libserial_port.so new file mode 100755 index 0000000..f6eb9ba --- /dev/null +++ b/app/src/main/jniLibs/x86/libserial_port.so Binary files differ diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/src/main/jniLibs/arm64-v8a/libserial_port.so b/app/src/main/jniLibs/arm64-v8a/libserial_port.so new file mode 100755 index 0000000..fe664b1 --- /dev/null +++ b/app/src/main/jniLibs/arm64-v8a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libserial_port.so b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so new file mode 100755 index 0000000..88819f8 --- /dev/null +++ b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86/libserial_port.so b/app/src/main/jniLibs/x86/libserial_port.so new file mode 100755 index 0000000..f6eb9ba --- /dev/null +++ b/app/src/main/jniLibs/x86/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86_64/libserial_port.so b/app/src/main/jniLibs/x86_64/libserial_port.so new file mode 100755 index 0000000..14cbf6b --- /dev/null +++ b/app/src/main/jniLibs/x86_64/libserial_port.so Binary files differ diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/src/main/jniLibs/arm64-v8a/libserial_port.so b/app/src/main/jniLibs/arm64-v8a/libserial_port.so new file mode 100755 index 0000000..fe664b1 --- /dev/null +++ b/app/src/main/jniLibs/arm64-v8a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libserial_port.so b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so new file mode 100755 index 0000000..88819f8 --- /dev/null +++ b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86/libserial_port.so b/app/src/main/jniLibs/x86/libserial_port.so new file mode 100755 index 0000000..f6eb9ba --- /dev/null +++ b/app/src/main/jniLibs/x86/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86_64/libserial_port.so b/app/src/main/jniLibs/x86_64/libserial_port.so new file mode 100755 index 0000000..14cbf6b --- /dev/null +++ b/app/src/main/jniLibs/x86_64/libserial_port.so Binary files differ diff --git a/app/src/main/res/raw/ling.wav b/app/src/main/res/raw/ling.wav new file mode 100644 index 0000000..1b9f0e2 --- /dev/null +++ b/app/src/main/res/raw/ling.wav Binary files differ diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/src/main/jniLibs/arm64-v8a/libserial_port.so b/app/src/main/jniLibs/arm64-v8a/libserial_port.so new file mode 100755 index 0000000..fe664b1 --- /dev/null +++ b/app/src/main/jniLibs/arm64-v8a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libserial_port.so b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so new file mode 100755 index 0000000..88819f8 --- /dev/null +++ b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86/libserial_port.so b/app/src/main/jniLibs/x86/libserial_port.so new file mode 100755 index 0000000..f6eb9ba --- /dev/null +++ b/app/src/main/jniLibs/x86/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86_64/libserial_port.so b/app/src/main/jniLibs/x86_64/libserial_port.so new file mode 100755 index 0000000..14cbf6b --- /dev/null +++ b/app/src/main/jniLibs/x86_64/libserial_port.so Binary files differ diff --git a/app/src/main/res/raw/ling.wav b/app/src/main/res/raw/ling.wav new file mode 100644 index 0000000..1b9f0e2 --- /dev/null +++ b/app/src/main/res/raw/ling.wav Binary files differ diff --git a/app/src/main/res/raw/ring2.wav b/app/src/main/res/raw/ring2.wav new file mode 100644 index 0000000..38337e4 --- /dev/null +++ b/app/src/main/res/raw/ring2.wav Binary files differ diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/src/main/jniLibs/arm64-v8a/libserial_port.so b/app/src/main/jniLibs/arm64-v8a/libserial_port.so new file mode 100755 index 0000000..fe664b1 --- /dev/null +++ b/app/src/main/jniLibs/arm64-v8a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libserial_port.so b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so new file mode 100755 index 0000000..88819f8 --- /dev/null +++ b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86/libserial_port.so b/app/src/main/jniLibs/x86/libserial_port.so new file mode 100755 index 0000000..f6eb9ba --- /dev/null +++ b/app/src/main/jniLibs/x86/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86_64/libserial_port.so b/app/src/main/jniLibs/x86_64/libserial_port.so new file mode 100755 index 0000000..14cbf6b --- /dev/null +++ b/app/src/main/jniLibs/x86_64/libserial_port.so Binary files differ diff --git a/app/src/main/res/raw/ling.wav b/app/src/main/res/raw/ling.wav new file mode 100644 index 0000000..1b9f0e2 --- /dev/null +++ b/app/src/main/res/raw/ling.wav Binary files differ diff --git a/app/src/main/res/raw/ring2.wav b/app/src/main/res/raw/ring2.wav new file mode 100644 index 0000000..38337e4 --- /dev/null +++ b/app/src/main/res/raw/ring2.wav Binary files differ diff --git a/app/src/main/res/raw/ring3.wav b/app/src/main/res/raw/ring3.wav new file mode 100644 index 0000000..3dbcb17 --- /dev/null +++ b/app/src/main/res/raw/ring3.wav Binary files differ diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/src/main/jniLibs/arm64-v8a/libserial_port.so b/app/src/main/jniLibs/arm64-v8a/libserial_port.so new file mode 100755 index 0000000..fe664b1 --- /dev/null +++ b/app/src/main/jniLibs/arm64-v8a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libserial_port.so b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so new file mode 100755 index 0000000..88819f8 --- /dev/null +++ b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86/libserial_port.so b/app/src/main/jniLibs/x86/libserial_port.so new file mode 100755 index 0000000..f6eb9ba --- /dev/null +++ b/app/src/main/jniLibs/x86/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86_64/libserial_port.so b/app/src/main/jniLibs/x86_64/libserial_port.so new file mode 100755 index 0000000..14cbf6b --- /dev/null +++ b/app/src/main/jniLibs/x86_64/libserial_port.so Binary files differ diff --git a/app/src/main/res/raw/ling.wav b/app/src/main/res/raw/ling.wav new file mode 100644 index 0000000..1b9f0e2 --- /dev/null +++ b/app/src/main/res/raw/ling.wav Binary files differ diff --git a/app/src/main/res/raw/ring2.wav b/app/src/main/res/raw/ring2.wav new file mode 100644 index 0000000..38337e4 --- /dev/null +++ b/app/src/main/res/raw/ring2.wav Binary files differ diff --git a/app/src/main/res/raw/ring3.wav b/app/src/main/res/raw/ring3.wav new file mode 100644 index 0000000..3dbcb17 --- /dev/null +++ b/app/src/main/res/raw/ring3.wav Binary files differ diff --git a/app/src/main/res/raw/ring4.wav b/app/src/main/res/raw/ring4.wav new file mode 100644 index 0000000..3ecfb99 --- /dev/null +++ b/app/src/main/res/raw/ring4.wav Binary files differ diff --git a/app/build.gradle b/app/build.gradle index ef50c2a..b4ce9a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ targetSdkVersion 32 versionCode 1 versionName "1.0.0.0" + + ndk { + moduleName "serial_port" + //"log"表示加入Android Logcat日志,需要导入 #include "android/log.h" + ldLibs "log" + } } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c54bf2a..348eb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,12 @@ + + + + + + + + + + diff --git a/app/src/main/java/android_serialport_api/SerialPort.java b/app/src/main/java/android_serialport_api/SerialPort.java new file mode 100644 index 0000000..4b975d9 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPort.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SerialPort { + + private static final String TAG = "SerialPort"; + + /* + * Do not remove or rename the field mFd: it is used by native method close(); + */ + private FileDescriptor mFd; + private final FileInputStream mFileInputStream; + private final FileOutputStream mFileOutputStream; + + public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { + + /* Check access permission */ + if (!device.canRead() || !device.canWrite()) { + try { + /* Missing read/write permission, trying to chmod the file */ + Process su; + su = Runtime.getRuntime().exec("/system/bin/su"); + String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + + "exit\n"; + su.getOutputStream().write(cmd.getBytes()); + if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { + throw new SecurityException(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new SecurityException(); + } + } + + mFd = open(device.getAbsolutePath(), baudrate, flags); + if (mFd == null) { + Log.e(TAG, "native open returns null"); + throw new IOException(); + } + mFileInputStream = new FileInputStream(mFd); + mFileOutputStream = new FileOutputStream(mFd); + } + + // Getters and setters + public InputStream getInputStream() { + return mFileInputStream; + } + + public OutputStream getOutputStream() { + return mFileOutputStream; + } + + // JNI + private native static FileDescriptor open(String path, int baudrate, int flags); + + public native void close(); + + static { + System.loadLibrary("serial_port"); + } +} diff --git a/app/src/main/java/android_serialport_api/SerialPortFinder.java b/app/src/main/java/android_serialport_api/SerialPortFinder.java new file mode 100644 index 0000000..23e6b89 --- /dev/null +++ b/app/src/main/java/android_serialport_api/SerialPortFinder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2009 Cedric Priscal + * + * 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 + * + * http://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. + */ + +package android_serialport_api; + +import android.util.Log; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Iterator; +import java.util.Vector; + +public class SerialPortFinder { + + public class Driver { + public Driver(String name, String root) { + mDriverName = name; + mDeviceRoot = root; + } + + private String mDriverName; + private String mDeviceRoot; + Vector mDevices = null; + + public Vector getDevices() { + if (mDevices == null) { + mDevices = new Vector(); + File dev = new File("/dev"); + File[] files = dev.listFiles(); + int i; + for (i = 0; i < files.length; i++) { + if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { + Log.d(TAG, "Found new device: " + files[i]); + mDevices.add(files[i]); + } + } + } + return mDevices; + } + + public String getName() { + return mDriverName; + } + } + + private static final String TAG = "SerialPort"; + + private Vector mDrivers = null; + + Vector getDrivers() throws IOException { + if (mDrivers == null) { + mDrivers = new Vector(); + LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers")); + String l; + while ((l = r.readLine()) != null) { + // Issue 3: + // Since driver name may contain spaces, we do not extract driver name with split() + String drivername = l.substring(0, 0x15).trim(); + String[] w = l.split(" +"); + if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { + Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); + mDrivers.add(new Driver(drivername, w[w.length - 4])); + } + } + r.close(); + } + return mDrivers; + } + + public String[] getAllDevices() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getName(); + String value = String.format("%s (%s)", device, driver.getName()); + devices.add(value); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } + + public String[] getAllDevicesPath() { + Vector devices = new Vector(); + // Parse each driver + Iterator itdriv; + try { + itdriv = getDrivers().iterator(); + while (itdriv.hasNext()) { + Driver driver = itdriv.next(); + Iterator itdev = driver.getDevices().iterator(); + while (itdev.hasNext()) { + String device = itdev.next().getAbsolutePath(); + devices.add(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return devices.toArray(new String[devices.size()]); + } +} diff --git a/app/src/main/java/com/casic/detector/base/BaseApplication.kt b/app/src/main/java/com/casic/detector/base/BaseApplication.kt index f5f784a..c36fcea 100644 --- a/app/src/main/java/com/casic/detector/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/detector/base/BaseApplication.kt @@ -1,14 +1,32 @@ package com.casic.detector.base import android.app.Application +import android_serialport_api.SerialPort import com.casic.detector.greendao.DaoMaster import com.casic.detector.greendao.DaoSession import com.pengxh.kt.lite.utils.SaveKeyValues +import java.io.File +import java.io.IOException +import java.security.InvalidParameterException import kotlin.properties.Delegates + class BaseApplication : Application() { private val kTag = "BaseApplication" + private var serialPort: SerialPort? = null + + @Throws(SecurityException::class, IOException::class, InvalidParameterException::class) + fun getSerialPort(): SerialPort? { + if (serialPort == null) { + /** + * Open the serial port + * */ + serialPort = + SerialPort(File("/dev/ttysWK1"), 9600, 0) + } + return serialPort + } companion object { private var application: BaseApplication by Delegates.notNull() @@ -30,4 +48,11 @@ fun getDaoSession(): DaoSession { return daoSession } + + fun closeSerialPort() { + if (serialPort != null) { + serialPort?.close() + serialPort = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt new file mode 100644 index 0000000..c7229ad --- /dev/null +++ b/app/src/main/java/com/casic/detector/base/SerialPortActivity.kt @@ -0,0 +1,100 @@ +package com.casic.detector.base + +import android.os.Bundle +import android_serialport_api.SerialPort +import androidx.appcompat.app.AppCompatActivity +import com.pengxh.kt.lite.extensions.show +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.security.InvalidParameterException + + +abstract class SerialPortActivity : AppCompatActivity() { + + private var serialPort: SerialPort? = null + var outputStream: OutputStream? = null + private var inputStream: InputStream? = null + private var readThread: ReadThread? = null + + inner class ReadThread : Thread() { + override fun run() { + super.run() + while (!isInterrupted) { + var size: Int + try { + val buffer = ByteArray(64) + if (inputStream == null) return + size = inputStream!!.read(buffer) + if (size > 0) { + onDataReceived(buffer, size) + } + } catch (e: IOException) { + e.printStackTrace() + return + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + setupTopBarLayout() + initData(savedInstanceState) + observeRequestState() + initEvent() + + try { + serialPort = BaseApplication.get().getSerialPort() + outputStream = serialPort?.outputStream + inputStream = serialPort?.inputStream + + readThread = ReadThread() + readThread?.start() + } catch (e: SecurityException) { + "您没有串口的读写权限!".show(this) + } catch (e: IOException) { + "因为不明原因,串口无法打开!".show(this) + } catch (e: InvalidParameterException) { + "请检查串口!".show(this) + } + } + + /** + * 初始化xml布局 + */ + abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData(savedInstanceState: Bundle?) + + /** + * 数据请求状态监听 + */ + abstract fun observeRequestState() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 串口读数 + * */ + abstract fun onDataReceived(buffer: ByteArray?, size: Int) + + override fun onDestroy() { + readThread?.interrupt() + BaseApplication.get().closeSerialPort() + serialPort = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/utils/GpioManager.java b/app/src/main/java/com/casic/detector/utils/GpioManager.java new file mode 100644 index 0000000..0f54f1a --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/GpioManager.java @@ -0,0 +1,121 @@ +package com.casic.detector.utils; + + +public class GpioManager { + + private static final int MT6761 = 0; + private int selected; + private final MT6761Gpio mt6761; + + public GpioManager(int selected) { + this.selected = selected; + mt6761 = new MT6761Gpio(); + } + + public GpioManager() { + mt6761 = new MT6761Gpio(); + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + this.selected = selected; + } + + /************************************************************************ + * 璁剧疆GPIO杈撳叆 + * *********************************************************************/ + public void setGpioInput(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioInput(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭楂� + * *********************************************************************/ + public void setGpioHigh(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioHigh(); + } + } + + /************************************************************************ + * 璁剧疆GPIO杈撳嚭浣� + * *********************************************************************/ + public void setGpioLow(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setGpioLow(); + } + } + + /************************************************************************ + * 璁剧疆GPIO鏃犱笂涓嬫媺 + * *********************************************************************/ + public void setNotPen(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setNotPen(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓嬫媺 + * *********************************************************************/ + public void setPullDown(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullDown(); + } + } + + /************************************************************************ + * 璁剧疆GPIO涓婃媺 + * *********************************************************************/ + public void setPullUp(String gpio) { + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + mt6761.setPullUp(); + } + } + + /************************************************************************ + * 璇诲彇鍗曚釜GPIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireSingleGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioSingle(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬� + * *********************************************************************/ + public String inquireAllGpio(String gpio) { + String status = ""; + if (this.getSelected() == MT6761) { + mt6761.setCtrlPin(gpio); + status = mt6761.readGpioAll(); + } + return status; + } + + /************************************************************************ + * 璇诲彇鎵�鏈塆PIO鐨勭姸鎬佹爣棰� + * *********************************************************************/ + public String readGpioTitle() { + String status = ""; + if (this.getSelected() == MT6761) { + status = mt6761.readGpioTitle(); + } + return status; + } +} diff --git a/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java new file mode 100644 index 0000000..d9d394f --- /dev/null +++ b/app/src/main/java/com/casic/detector/utils/MT6761Gpio.java @@ -0,0 +1,157 @@ +package com.casic.detector.utils; + +import java.io.BufferedReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class MT6761Gpio { + + private final String filePath = "/sys/devices/platform/1000b000.pinctrl/mt_gpio"; + private String pin; + + public void setGpioHigh() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "1"); + } + + public void setGpioLow() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "1"); + setGpioOut(pin, "0"); + } + + public void setGpioInput() { + if (pin.isEmpty()) { + return; + } + setGpioMode(pin, "0"); + setGpioDir(pin, "0"); + } + + public void setNotPen() { + setGpioPen(pin, "0"); + } + + public void setPullUp() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "1"); + } + + public void setPullDown() { + setGpioPen(pin, "1"); + setGpioPsel(pin, "0"); + } + + public void setCtrlPin(String gpio) { + pin = gpio; + } + + public String readGpioTitle() { + return read(filePath, "PIN:"); + } + + public String readGpioSingle() { + int gpio = 0; + if (pin.isEmpty()) { + return ""; + } + try { + gpio = Integer.parseInt(pin); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + String str = String.format("%3d", gpio).replace(" ", "0"); + return read(filePath, pin + ":"); + } + + public String readGpioAll() { + return read_all(filePath); + } + + private void setGpioMode(String pin, String value) { + nm_write("mode " + pin + " " + value); + } + + private void setGpioDir(String pin, String value) { + nm_write("dir " + pin + " " + value); + } + + private void setGpioOut(String pin, String value) { + nm_write("out " + pin + " " + value); + } + + private void setGpioPen(String pin, String value) { //1:浣胯兘涓婁笅鎷� 0:绂佹涓婁笅鎷� + nm_write("pullen " + pin + " " + value); + } + + private void setGpioPsel(String pin, String value) { //1:璁剧疆涓婃媺 0:璁剧疆涓嬫媺 + nm_write("pullsel " + pin + " " + value); + } + + private void nm_write(String value) { + if (value.isEmpty()) { + return; + } + FileWriter fw; + try { + fw = new FileWriter(filePath); + fw.write(value); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public String read(String sys_path, String value) { + if (value.isEmpty()) { + return null; + } + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + String line; + while (null != (line = br.readLine())) { + if (line.contains(value)) { + return line; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + + public String read_all(String sys_path) { + StringBuilder value = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("cat " + sys_path + "\n"); // 姝ゅ杩涜璇绘搷浣� + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line; + + while (null != (line = br.readLine())) { + value.append(line).append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return value.toString(); + } +} diff --git a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt index ee56adf..09c5ae1 100644 --- a/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/InstallLabelActivity.kt @@ -4,28 +4,30 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.SoundPool +import android.os.Bundle +import android.os.Handler import android.provider.Settings +import android.util.Log import android.view.View import androidx.lifecycle.ViewModelProvider import com.amap.api.location.AMapLocation import com.casic.detector.R import com.casic.detector.adapter.EditableImageAdapter +import com.casic.detector.base.SerialPortActivity import com.casic.detector.bean.LabelBean import com.casic.detector.callback.ILocationListener import com.casic.detector.callback.OnImageCompressListener import com.casic.detector.callback.OnItemSelectedListener import com.casic.detector.extensions.* -import com.casic.detector.utils.DataBaseManager -import com.casic.detector.utils.LoadingDialogHub -import com.casic.detector.utils.LocaleConstant -import com.casic.detector.utils.LocationHub +import com.casic.detector.utils.* import com.casic.detector.vm.TaskViewModel import com.gyf.immersionbar.ImmersionBar import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnResultCallbackListener -import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.vm.LoadState @@ -36,10 +38,11 @@ import kotlinx.android.synthetic.main.include_install_label_object.* import kotlinx.android.synthetic.main.include_install_label_open_camera.* import java.io.File +import java.io.IOException import java.util.* @SuppressLint("SetTextI18n") -class InstallLabelActivity : KotlinBaseActivity() { +class InstallLabelActivity : SerialPortActivity() { private val kTag = "InstallLabelActivity" private val context: Context = this@InstallLabelActivity @@ -54,8 +57,10 @@ private var identifierTypeSelectedItem = "" private var colorSelectedItem = "" private lateinit var taskViewModel: TaskViewModel + private lateinit var soundPool: SoundPool + private val gpioManager by lazy { GpioManager() } - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { imageAdapter = EditableImageAdapter(this, 3, 3f) addImageRecyclerView.adapter = imageAdapter @@ -73,6 +78,9 @@ ownerView.setText("ownerView".getDefaultValue()) identifierDeepView.setText("identifierDeepView".getDefaultValue()) personDeptView.setText("personDeptView".getDefaultValue()) + + soundPool = SoundPool(10, AudioManager.STREAM_SYSTEM, 5) + soundPool.load(this, R.raw.ring4, 1) } override fun initEvent() { @@ -331,12 +339,64 @@ //TODO 搜索标识器 readLabelButton.setOnClickListener { - LoadingDialogHub.show(this, "正在搜索标识器,请稍后...") - identifierIdView.text = "123456789" - LoadingDialogHub.dismiss() + reception = "0" + gpioManager.setGpioHigh("18") + + // 1000ms后发送读标识器或搜索信号 + Handler().postDelayed({ // 发送读标识器或搜索信号 + try { + outputStream?.write(0x02) + outputStream?.flush() + } catch (e: IOException) { + e.printStackTrace() + } + }, 1000) } } + private var reception = "" + + + override fun onDataReceived(buffer: ByteArray?, size: Int) { + Log.d(kTag, "onDataReceived => " + buffer.contentToString()) + if (buffer != null) { + reception = String.format( + "%010d", hex2Str( + byte2hex(buffer).substring(6, 22) + ).toLong(16) + ) + Log.d(kTag, "onDataReceived => $reception") + } + } + + private fun byte2hex(buffer: ByteArray): String { + var h = "" + for (i in buffer.indices) { + var temp = Integer.toHexString(buffer[i].toInt() and 0xFF) + if (temp.length == 1) { + temp = "0$temp" + } + h += temp + } + return h + } + + private fun hex2Str(hex: String): String { + val sb = StringBuilder() + val temp = StringBuilder() + run { + var i = 0 + while (i < hex.length - 1) { + val output = hex.substring(i, i + 2) + val decimal = output.toInt(16) + sb.append(decimal.toChar()) + temp.append(decimal) + i += 2 + } + } + return sb.toString() + } + private fun takePicture() { PictureSelector.create(this).openCamera(SelectMimeType.ofImage()) .forResult(object : OnResultCallbackListener { @@ -429,4 +489,9 @@ .init() initLayoutImmersionBar(rootView) } + + override fun onDestroy() { + gpioManager.setGpioLow("18") + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/detector/view/LoginActivity.kt b/app/src/main/java/com/casic/detector/view/LoginActivity.kt index 3297c76..ae56b26 100644 --- a/app/src/main/java/com/casic/detector/view/LoginActivity.kt +++ b/app/src/main/java/com/casic/detector/view/LoginActivity.kt @@ -4,6 +4,7 @@ import com.amap.api.navi.NaviSetting import com.casic.detector.R import com.casic.detector.extensions.initLayoutImmersionBar +import com.casic.detector.utils.GpioManager import com.casic.detector.utils.LoadingDialogHub import com.casic.detector.utils.LocaleConstant import com.casic.detector.vm.UserViewModel @@ -18,8 +19,10 @@ class LoginActivity : KotlinBaseActivity() { private lateinit var userViewModel: UserViewModel + private val gpioManager by lazy { GpioManager() } override fun initData() { + gpioManager.setGpioLow("18") //先把导航隐私政策声明,后面导航会用到 NaviSetting.updatePrivacyShow(this, true, true) NaviSetting.updatePrivacyAgree(this, true) diff --git a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt index 8bca233..3140bc3 100644 --- a/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt +++ b/app/src/main/java/com/casic/detector/view/SearchLabelActivity.kt @@ -1,14 +1,15 @@ package com.casic.detector.view +import android.os.Bundle import com.casic.detector.R +import com.casic.detector.base.SerialPortActivity import com.casic.detector.extensions.initLayoutImmersionBar import com.gyf.immersionbar.ImmersionBar -import com.pengxh.kt.lite.base.KotlinBaseActivity import kotlinx.android.synthetic.main.activity_search_label.* -class SearchLabelActivity : KotlinBaseActivity() { +class SearchLabelActivity : SerialPortActivity() { - override fun initData() { + override fun initData(savedInstanceState: Bundle?) { } @@ -16,6 +17,10 @@ leftBackView.setOnClickListener { finish() } } + override fun onDataReceived(buffer: ByteArray?, size: Int) { + + } + override fun initLayoutView(): Int = R.layout.activity_search_label override fun observeRequestState() { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 0000000..f4ce1c3 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright 2009 Cedric Priscal +# +# 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 +# +# http://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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +TARGET_PLATFORM := android-3 +LOCAL_MODULE := serial_port +LOCAL_SRC_FILES := SerialPort.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a252a72 --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all diff --git a/app/src/main/jni/SerialPort.c b/app/src/main/jni/SerialPort.c new file mode 100644 index 0000000..84748a7 --- /dev/null +++ b/app/src/main/jni/SerialPort.c @@ -0,0 +1,167 @@ +/* + * Copyright 2009-2011 Cedric Priscal + * + * 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 + * + * http://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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "SerialPort.h" + +#include "android/log.h" +static const char *TAG="serial_port"; +#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) +#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) +#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) + +static speed_t getBaudrate(jint baudrate) +{ + switch(baudrate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; + default: return -1; + } +} + +/* + * Class: android_serialport_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) +{ + int fd; + speed_t speed; + jobject mFileDescriptor; + + /* Check arguments */ + { + speed = getBaudrate(baudrate); + if (speed == -1) { + /* TODO: throw an exception */ + LOGE("Invalid baudrate"); + return NULL; + } + } + + /* Opening device */ + { + jboolean iscopy; + const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); + LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); + fd = open(path_utf, O_RDWR | flags); + LOGD("open() fd = %d", fd); + (*env)->ReleaseStringUTFChars(env, path, path_utf); + if (fd == -1) + { + /* Throw an exception */ + LOGE("Cannot open port"); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Configure device */ + { + struct termios cfg; + LOGD("Configuring serial port"); + if (tcgetattr(fd, &cfg)) + { + LOGE("tcgetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + + cfmakeraw(&cfg); + cfsetispeed(&cfg, speed); + cfsetospeed(&cfg, speed); + + if (tcsetattr(fd, TCSANOW, &cfg)) + { + LOGE("tcsetattr() failed"); + close(fd); + /* TODO: throw an exception */ + return NULL; + } + } + + /* Create a corresponding file descriptor */ + { + jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); + jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); + jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); + mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); + (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); + } + + return mFileDescriptor; +} + +/* + * Class: cedric_serial_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *env, jobject thiz) +{ + jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); + jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); + + jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); + jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); + + jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); + jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); + + LOGD("close(fd = %d)", descriptor); + close(descriptor); +} + diff --git a/app/src/main/jni/SerialPort.h b/app/src/main/jni/SerialPort.h new file mode 100644 index 0000000..61f1fb2 --- /dev/null +++ b/app/src/main/jni/SerialPort.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_serialport_api_SerialPort */ + +#ifndef _Included_android_serialport_api_SerialPort +#define _Included_android_serialport_api_SerialPort +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_serialport_api_SerialPort + * Method: open + * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; + */ +JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open + (JNIEnv *, jclass, jstring, jint, jint); + +/* + * Class: android_serialport_api_SerialPort + * Method: close + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/app/src/main/jni/gen_SerialPort_h.sh b/app/src/main/jni/gen_SerialPort_h.sh new file mode 100755 index 0000000..5414670 --- /dev/null +++ b/app/src/main/jni/gen_SerialPort_h.sh @@ -0,0 +1,3 @@ +#!/bin/sh +javah -o SerialPort.h -jni -classpath ../java android_serialport_api.SerialPort + diff --git a/app/src/main/jniLibs/arm64-v8a/libserial_port.so b/app/src/main/jniLibs/arm64-v8a/libserial_port.so new file mode 100755 index 0000000..fe664b1 --- /dev/null +++ b/app/src/main/jniLibs/arm64-v8a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libserial_port.so b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so new file mode 100755 index 0000000..88819f8 --- /dev/null +++ b/app/src/main/jniLibs/armeabi-v7a/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86/libserial_port.so b/app/src/main/jniLibs/x86/libserial_port.so new file mode 100755 index 0000000..f6eb9ba --- /dev/null +++ b/app/src/main/jniLibs/x86/libserial_port.so Binary files differ diff --git a/app/src/main/jniLibs/x86_64/libserial_port.so b/app/src/main/jniLibs/x86_64/libserial_port.so new file mode 100755 index 0000000..14cbf6b --- /dev/null +++ b/app/src/main/jniLibs/x86_64/libserial_port.so Binary files differ diff --git a/app/src/main/res/raw/ling.wav b/app/src/main/res/raw/ling.wav new file mode 100644 index 0000000..1b9f0e2 --- /dev/null +++ b/app/src/main/res/raw/ling.wav Binary files differ diff --git a/app/src/main/res/raw/ring2.wav b/app/src/main/res/raw/ring2.wav new file mode 100644 index 0000000..38337e4 --- /dev/null +++ b/app/src/main/res/raw/ring2.wav Binary files differ diff --git a/app/src/main/res/raw/ring3.wav b/app/src/main/res/raw/ring3.wav new file mode 100644 index 0000000..3dbcb17 --- /dev/null +++ b/app/src/main/res/raw/ring3.wav Binary files differ diff --git a/app/src/main/res/raw/ring4.wav b/app/src/main/res/raw/ring4.wav new file mode 100644 index 0000000..3ecfb99 --- /dev/null +++ b/app/src/main/res/raw/ring4.wav Binary files differ diff --git a/app/src/main/res/xml/accessory_filter.xml b/app/src/main/res/xml/accessory_filter.xml new file mode 100644 index 0000000..e8206e0 --- /dev/null +++ b/app/src/main/res/xml/accessory_filter.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file