From 16e007b8fa92132429967d48ffbeb1b3e2607d26 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Wed, 4 Dec 2024 09:32:07 +0700 Subject: [PATCH] first commit --- bin/SBC/Amlogic/T982.class | Bin 0 -> 1147 bytes bin/SBC/AndroidNTP.class | Bin 0 -> 2444 bytes bin/SBC/BasicSBCInfo$1.class | Bin 0 -> 585 bytes bin/SBC/BasicSBCInfo$2.class | Bin 0 -> 3439 bytes bin/SBC/BasicSBCInfo$3.class | Bin 0 -> 3404 bytes bin/SBC/BasicSBCInfo$4.class | Bin 0 -> 3411 bytes bin/SBC/BasicSBCInfo$5.class | Bin 0 -> 3425 bytes bin/SBC/BasicSBCInfo$CheckIPReachable.class | Bin 0 -> 2115 bytes bin/SBC/BasicSBCInfo$CheckIPValid.class | Bin 0 -> 1441 bytes bin/SBC/BasicSBCInfo$cpumonitorrunnable.class | Bin 0 -> 2398 bytes .../BasicSBCInfo$networkmeterrunnable.class | Bin 0 -> 3214 bytes bin/SBC/BasicSBCInfo.class | Bin 0 -> 34763 bytes bin/SBC/CPU_Info.class | Bin 0 -> 3277 bytes bin/SBC/GeneralAndroid$1.class | Bin 0 -> 1381 bytes bin/SBC/GeneralAndroid$2.class | Bin 0 -> 3533 bytes bin/SBC/GeneralAndroid.class | Bin 0 -> 17093 bytes bin/SBC/GetProp_Info.class | Bin 0 -> 5573 bytes bin/SBC/Hwmon_Info.class | Bin 0 -> 3279 bytes bin/SBC/Ifconfig_Info.class | Bin 0 -> 7058 bytes bin/SBC/NetworkInterface_info.class | Bin 0 -> 11740 bytes bin/SBC/NetworkMeterInfo.class | Bin 0 -> 7800 bytes bin/SBC/ProcStat_Info.class | Bin 0 -> 5002 bytes bin/SBC/RAM_Info.class | Bin 0 -> 6373 bytes bin/SBC/RaspberryPi/Pi4.class | Bin 0 -> 5391 bytes bin/SBC/Size_Info.class | Bin 0 -> 3003 bytes bin/SBC/Storage_Info.class | Bin 0 -> 3122 bytes bin/SBC/WifiInfo.class | Bin 0 -> 557 bytes bin/SBC/cpudata.class | Bin 0 -> 3134 bytes bin/androgpio/Closer.class | Bin 0 -> 1225 bytes bin/androgpio/DigitalInput/DigitalInput.class | Bin 0 -> 4552 bytes .../DigitalInput/DigitalInputEvent.class | Bin 0 -> 208 bytes .../DigitalOutput/DigitalOutput.class | Bin 0 -> 4448 bytes .../DigitalOutput/DigitalOutputEvent.class | Bin 0 -> 212 bytes .../DigitalOutput/GtcAndroidOutput.class | Bin 0 -> 5123 bytes .../DigitalOutput/GtcAndroidOutputEvent.class | Bin 0 -> 355 bytes .../DigitalOutput/OutputDevice.class | Bin 0 -> 1963 bytes bin/androgpio/I2C/I2CException.class | Bin 0 -> 1739 bytes bin/androgpio/I2C/I2C_BUS.class | Bin 0 -> 1932 bytes bin/androgpio/I2C/I2C_BUS_Event.class | Bin 0 -> 159 bytes bin/androgpio/I2C/I2C_Device.class | Bin 0 -> 5168 bytes bin/androgpio/I2C/I2C_Device_Event.class | Bin 0 -> 165 bytes .../I2C/libc$Linux_C_lib$timeval.class | Bin 0 -> 1242 bytes bin/androgpio/I2C/libc$Linux_C_lib.class | Bin 0 -> 716 bytes .../I2C/libc$Linux_C_lib_DirectMapping.class | Bin 0 -> 1203 bytes bin/androgpio/I2C/libc.class | Bin 0 -> 908 bytes bin/androgpio/ModbusWrapper/jModbusCoil.class | Bin 0 -> 655 bytes .../jModbusHoldingRegister.class | Bin 0 -> 710 bytes .../ModbusWrapper/jModbusMaster$1.class | Bin 0 -> 1548 bytes .../ModbusWrapper/jModbusMaster$2.class | Bin 0 -> 721 bytes .../ModbusWrapper/jModbusMaster.class | Bin 0 -> 11375 bytes .../ModbusWrapper/jModbusSlave$1.class | Bin 0 -> 1539 bytes .../ModbusWrapper/jModbusSlave$2.class | Bin 0 -> 715 bytes .../ModbusWrapper/jModbusSlave.class | Bin 0 -> 10220 bytes bin/androgpio/ScreenRotater$1.class | Bin 0 -> 1054 bytes bin/androgpio/ScreenRotater.class | Bin 0 -> 4632 bytes bin/androgpio/SerialPort/SerialComm$1.class | Bin 0 -> 685 bytes bin/androgpio/SerialPort/SerialComm$2.class | Bin 0 -> 1514 bytes bin/androgpio/SerialPort/SerialComm.class | Bin 0 -> 8646 bytes .../SerialPort/jSerialPort_Event.class | Bin 0 -> 200 bytes bin/androgpio/androgpio.class | Bin 0 -> 3313 bytes .../customsocket/AndroFTPClient$1.class | Bin 0 -> 1449 bytes .../customsocket/AndroFTPClient$10.class | Bin 0 -> 2989 bytes .../customsocket/AndroFTPClient$11.class | Bin 0 -> 5457 bytes .../customsocket/AndroFTPClient$2.class | Bin 0 -> 3100 bytes .../customsocket/AndroFTPClient$3.class | Bin 0 -> 2673 bytes .../customsocket/AndroFTPClient$4.class | Bin 0 -> 2664 bytes .../customsocket/AndroFTPClient$5.class | Bin 0 -> 2679 bytes .../customsocket/AndroFTPClient$6$1.class | Bin 0 -> 2436 bytes .../customsocket/AndroFTPClient$6.class | Bin 0 -> 5432 bytes .../customsocket/AndroFTPClient$7$1.class | Bin 0 -> 2589 bytes .../customsocket/AndroFTPClient$7.class | Bin 0 -> 6493 bytes .../customsocket/AndroFTPClient$8.class | Bin 0 -> 3059 bytes .../customsocket/AndroFTPClient$9$1.class | Bin 0 -> 2074 bytes .../customsocket/AndroFTPClient$9.class | Bin 0 -> 5166 bytes .../customsocket/AndroFTPClient.class | Bin 0 -> 14705 bytes .../customsocket/AndroFTPEntry.class | Bin 0 -> 2949 bytes bin/androgpio/customsocket/JsonArray.class | Bin 0 -> 13429 bytes bin/androgpio/customsocket/JsonObject.class | Bin 0 -> 16100 bytes bin/androgpio/customsocket/NTPClient$1.class | Bin 0 -> 2094 bytes bin/androgpio/customsocket/NTPClient$2.class | Bin 0 -> 2213 bytes bin/androgpio/customsocket/NTPClient.class | Bin 0 -> 3270 bytes bin/androgpio/customsocket/NTPInfo.class | Bin 0 -> 1421 bytes .../customsocket/SocketIoObject.class | Bin 0 -> 6257 bytes bin/androgpio/customsocket/TCPSocket$1.class | Bin 0 -> 1064 bytes .../TCPSocket$tcpsocketrun$1.class | Bin 0 -> 1276 bytes .../customsocket/TCPSocket$tcpsocketrun.class | Bin 0 -> 7582 bytes bin/androgpio/customsocket/TCPSocket.class | Bin 0 -> 16272 bytes .../customsocket/TCPSocketServer$1.class | Bin 0 -> 1190 bytes .../TCPSocketServer$tcpsocketserverrun.class | Bin 0 -> 2435 bytes .../customsocket/TCPSocketServer.class | Bin 0 -> 10050 bytes .../customsocket/TcpSocketJavaEvent.class | Bin 0 -> 341 bytes .../customsocket/jSocketIOClientV1$1.class | Bin 0 -> 910 bytes .../customsocket/jSocketIOClientV1$2.class | Bin 0 -> 3842 bytes .../customsocket/jSocketIOClientV1$3.class | Bin 0 -> 2652 bytes .../customsocket/jSocketIOClientV1$4.class | Bin 0 -> 2822 bytes .../customsocket/jSocketIOClientV1$5.class | Bin 0 -> 3412 bytes .../customsocket/jSocketIOClientV1$6.class | Bin 0 -> 3417 bytes .../customsocket/jSocketIOClientV1$7.class | Bin 0 -> 3355 bytes .../customsocket/jSocketIOClientV1$8.class | Bin 0 -> 3351 bytes .../customsocket/jSocketIOClientV1.class | Bin 0 -> 19521 bytes .../customsocket/jUDPSocket$udprun.class | Bin 0 -> 2441 bytes bin/androgpio/customsocket/jUDPSocket.class | Bin 0 -> 9244 bytes .../customsocket/jUDPSocket2$1.class | Bin 0 -> 720 bytes .../customsocket/jUDPSocket2$2.class | Bin 0 -> 2467 bytes .../customsocket/jUDPSocket2$3.class | Bin 0 -> 3571 bytes .../customsocket/jUDPSocket2$4.class | Bin 0 -> 1859 bytes .../customsocket/jUDPSocket2$5.class | Bin 0 -> 3468 bytes .../jUDPSocket2$udpreceiver.class | Bin 0 -> 3072 bytes bin/androgpio/customsocket/jUDPSocket2.class | Bin 0 -> 11620 bytes bin/androgpio/gps/AdzanCalculator.class | Bin 0 -> 2465 bytes bin/androgpio/gps/DataKota.class | Bin 0 -> 1107 bytes bin/androgpio/gps/GpsGPGGA.class | Bin 0 -> 4187 bytes bin/androgpio/gps/GpsGPGLL.class | Bin 0 -> 3144 bytes bin/androgpio/gps/GpsGPGSA.class | Bin 0 -> 2794 bytes bin/androgpio/gps/GpsGPRMC.class | Bin 0 -> 4531 bytes bin/androgpio/gps/GpsGPVTG.class | Bin 0 -> 2535 bytes bin/androgpio/gps/GpsReceiver$1.class | Bin 0 -> 656 bytes bin/androgpio/gps/GpsReceiver$2.class | Bin 0 -> 1842 bytes bin/androgpio/gps/GpsReceiver.class | Bin 0 -> 11331 bytes bin/androgpio/gps/KotaIndonesia.class | Bin 0 -> 3752 bytes bin/androgpio/gps/PrayTime.class | Bin 0 -> 19413 bytes bin/androgpio/gps/kotaindonesiaminified.json | 545 +++++ bin/androgpio/mycodes.class | Bin 0 -> 24858 bytes .../networkrelated/IfConfigResult.class | Bin 0 -> 3890 bytes .../networkrelated/ethernetmanager$1.class | Bin 0 -> 6252 bytes .../networkrelated/ethernetmanager$2.class | Bin 0 -> 1517 bytes .../networkrelated/ethernetmanager.class | Bin 0 -> 16219 bytes bin/androgpio/openweather/OpenWeather.class | Bin 0 -> 5966 bytes .../openweather/OpenWeatherData.class | Bin 0 -> 2517 bytes .../openweather/OpenWeatherEvent.class | Bin 0 -> 239 bytes .../openweather/jsondata/clouds.class | Bin 0 -> 654 bytes .../openweather/jsondata/coord.class | Bin 0 -> 698 bytes bin/androgpio/openweather/jsondata/main.class | Bin 0 -> 1121 bytes bin/androgpio/openweather/jsondata/rain.class | Bin 0 -> 694 bytes bin/androgpio/openweather/jsondata/snow.class | Bin 0 -> 694 bytes bin/androgpio/openweather/jsondata/sys.class | Bin 0 -> 986 bytes .../openweather/jsondata/weather.class | Bin 0 -> 841 bytes bin/androgpio/openweather/jsondata/wind.class | Bin 0 -> 793 bytes bin/devices/Button$1.class | Bin 0 -> 595 bytes bin/devices/Button$2.class | Bin 0 -> 1444 bytes bin/devices/Button.class | Bin 0 -> 5637 bytes bin/devices/Buzzer$1.class | Bin 0 -> 564 bytes bin/devices/Buzzer$2.class | Bin 0 -> 1171 bytes bin/devices/Buzzer$3.class | Bin 0 -> 1218 bytes bin/devices/Buzzer$4.class | Bin 0 -> 1211 bytes bin/devices/Buzzer.class | Bin 0 -> 2814 bytes bin/devices/PCF8574$1.class | Bin 0 -> 598 bytes bin/devices/PCF8574.class | Bin 0 -> 4035 bytes bin/devices/PublicIPChecker$1.class | Bin 0 -> 2598 bytes bin/devices/PublicIPChecker.class | Bin 0 -> 3325 bytes bin/devices/Relay$1.class | Bin 0 -> 552 bytes bin/devices/Relay.class | Bin 0 -> 3200 bytes lib/arm64-v8a/libjnidispatch.so | Bin 0 -> 168176 bytes lib/armeabi-v7a/libjnidispatch.so | Bin 0 -> 126384 bytes lib/armeabi/libjnidispatch.so | Bin 0 -> 130964 bytes lib/x86/libjnidispatch.so | Bin 0 -> 124268 bytes lib/x86_64/libjnidispatch.so | Bin 0 -> 126768 bytes libs/classes.jar | Bin 0 -> 41611 bytes src/SBC/Amlogic/T982.java | 27 + src/SBC/AndroidNTP.java | 48 + src/SBC/BasicSBCInfo.java | 1918 +++++++++++++++++ src/SBC/CPU_Info.java | 95 + src/SBC/GeneralAndroid.java | 728 +++++++ src/SBC/GetProp_Info.java | 130 ++ src/SBC/Hwmon_Info.java | 90 + src/SBC/Ifconfig_Info.java | 201 ++ src/SBC/NetworkInterface_info.java | 522 +++++ src/SBC/NetworkMeterInfo.java | 330 +++ src/SBC/ProcStat_Info.java | 162 ++ src/SBC/RAM_Info.java | 195 ++ src/SBC/RaspberryPi/Pi4.java | 369 ++++ src/SBC/Size_Info.java | 195 ++ src/SBC/Storage_Info.java | 116 + src/SBC/WifiInfo.java | 11 + src/SBC/cpudata.java | 153 ++ src/androgpio/Closer.java | 33 + src/androgpio/DigitalInput/DigitalInput.java | 192 ++ .../DigitalInput/DigitalInputEvent.java | 6 + .../DigitalOutput/DigitalOutput.java | 169 ++ .../DigitalOutput/DigitalOutputEvent.java | 6 + .../DigitalOutput/GtcAndroidOutput.java | 313 +++ .../DigitalOutput/GtcAndroidOutputEvent.java | 12 + src/androgpio/DigitalOutput/OutputDevice.java | 96 + src/androgpio/I2C/I2CException.java | 49 + src/androgpio/I2C/I2C_BUS.java | 84 + src/androgpio/I2C/I2C_BUS_Event.java | 5 + src/androgpio/I2C/I2C_Device.java | 271 +++ src/androgpio/I2C/I2C_Device_Event.java | 5 + src/androgpio/I2C/libc.java | 143 ++ src/androgpio/ModbusWrapper/jModbusCoil.java | 22 + .../ModbusWrapper/jModbusHoldingRegister.java | 22 + .../ModbusWrapper/jModbusMaster.java | 447 ++++ src/androgpio/ModbusWrapper/jModbusSlave.java | 345 +++ src/androgpio/ScreenRotater.java | 145 ++ src/androgpio/SerialPort/SerialComm.java | 505 +++++ .../SerialPort/jSerialPort_Event.java | 6 + src/androgpio/androgpio.java | 113 + .../customsocket/AndroFTPClient.java | 1036 +++++++++ src/androgpio/customsocket/AndroFTPEntry.java | 109 + src/androgpio/customsocket/JsonArray.java | 806 +++++++ src/androgpio/customsocket/JsonObject.java | 858 ++++++++ src/androgpio/customsocket/NTPClient.java | 132 ++ src/androgpio/customsocket/NTPInfo.java | 74 + .../customsocket/SocketIoObject.java | 364 ++++ src/androgpio/customsocket/TCPSocket.java | 773 +++++++ .../customsocket/TCPSocketServer.java | 380 ++++ .../customsocket/TcpSocketJavaEvent.java | 8 + .../customsocket/jSocketIOClientV1.java | 1227 +++++++++++ src/androgpio/customsocket/jUDPSocket.java | 329 +++ src/androgpio/customsocket/jUDPSocket2.java | 547 +++++ src/androgpio/gps/AdzanCalculator.java | 72 + src/androgpio/gps/DataKota.java | 39 + src/androgpio/gps/GpsGPGGA.java | 163 ++ src/androgpio/gps/GpsGPGLL.java | 121 ++ src/androgpio/gps/GpsGPGSA.java | 98 + src/androgpio/gps/GpsGPRMC.java | 194 ++ src/androgpio/gps/GpsGPVTG.java | 83 + src/androgpio/gps/GpsReceiver.java | 489 +++++ src/androgpio/gps/KotaIndonesia.java | 103 + src/androgpio/gps/PrayTime.java | 939 ++++++++ src/androgpio/gps/kotaindonesiaminified.json | 545 +++++ src/androgpio/mycodes.java | 1159 ++++++++++ .../networkrelated/IfConfigResult.java | 120 ++ .../networkrelated/ethernetmanager.java | 643 ++++++ src/androgpio/openweather/OpenWeather.java | 227 ++ .../openweather/OpenWeatherData.java | 39 + .../openweather/OpenWeatherEvent.java | 6 + .../openweather/jsondata/clouds.java | 10 + src/androgpio/openweather/jsondata/coord.java | 11 + src/androgpio/openweather/jsondata/main.java | 19 + src/androgpio/openweather/jsondata/rain.java | 11 + src/androgpio/openweather/jsondata/snow.java | 11 + src/androgpio/openweather/jsondata/sys.java | 16 + .../openweather/jsondata/weather.java | 13 + src/androgpio/openweather/jsondata/wind.java | 12 + src/devices/Button.java | 219 ++ src/devices/Buzzer.java | 216 ++ src/devices/PCF8574.java | 170 ++ src/devices/PublicIPChecker.java | 115 + src/devices/Relay.java | 147 ++ 240 files changed, 21477 insertions(+) create mode 100644 bin/SBC/Amlogic/T982.class create mode 100644 bin/SBC/AndroidNTP.class create mode 100644 bin/SBC/BasicSBCInfo$1.class create mode 100644 bin/SBC/BasicSBCInfo$2.class create mode 100644 bin/SBC/BasicSBCInfo$3.class create mode 100644 bin/SBC/BasicSBCInfo$4.class create mode 100644 bin/SBC/BasicSBCInfo$5.class create mode 100644 bin/SBC/BasicSBCInfo$CheckIPReachable.class create mode 100644 bin/SBC/BasicSBCInfo$CheckIPValid.class create mode 100644 bin/SBC/BasicSBCInfo$cpumonitorrunnable.class create mode 100644 bin/SBC/BasicSBCInfo$networkmeterrunnable.class create mode 100644 bin/SBC/BasicSBCInfo.class create mode 100644 bin/SBC/CPU_Info.class create mode 100644 bin/SBC/GeneralAndroid$1.class create mode 100644 bin/SBC/GeneralAndroid$2.class create mode 100644 bin/SBC/GeneralAndroid.class create mode 100644 bin/SBC/GetProp_Info.class create mode 100644 bin/SBC/Hwmon_Info.class create mode 100644 bin/SBC/Ifconfig_Info.class create mode 100644 bin/SBC/NetworkInterface_info.class create mode 100644 bin/SBC/NetworkMeterInfo.class create mode 100644 bin/SBC/ProcStat_Info.class create mode 100644 bin/SBC/RAM_Info.class create mode 100644 bin/SBC/RaspberryPi/Pi4.class create mode 100644 bin/SBC/Size_Info.class create mode 100644 bin/SBC/Storage_Info.class create mode 100644 bin/SBC/WifiInfo.class create mode 100644 bin/SBC/cpudata.class create mode 100644 bin/androgpio/Closer.class create mode 100644 bin/androgpio/DigitalInput/DigitalInput.class create mode 100644 bin/androgpio/DigitalInput/DigitalInputEvent.class create mode 100644 bin/androgpio/DigitalOutput/DigitalOutput.class create mode 100644 bin/androgpio/DigitalOutput/DigitalOutputEvent.class create mode 100644 bin/androgpio/DigitalOutput/GtcAndroidOutput.class create mode 100644 bin/androgpio/DigitalOutput/GtcAndroidOutputEvent.class create mode 100644 bin/androgpio/DigitalOutput/OutputDevice.class create mode 100644 bin/androgpio/I2C/I2CException.class create mode 100644 bin/androgpio/I2C/I2C_BUS.class create mode 100644 bin/androgpio/I2C/I2C_BUS_Event.class create mode 100644 bin/androgpio/I2C/I2C_Device.class create mode 100644 bin/androgpio/I2C/I2C_Device_Event.class create mode 100644 bin/androgpio/I2C/libc$Linux_C_lib$timeval.class create mode 100644 bin/androgpio/I2C/libc$Linux_C_lib.class create mode 100644 bin/androgpio/I2C/libc$Linux_C_lib_DirectMapping.class create mode 100644 bin/androgpio/I2C/libc.class create mode 100644 bin/androgpio/ModbusWrapper/jModbusCoil.class create mode 100644 bin/androgpio/ModbusWrapper/jModbusHoldingRegister.class create mode 100644 bin/androgpio/ModbusWrapper/jModbusMaster$1.class create mode 100644 bin/androgpio/ModbusWrapper/jModbusMaster$2.class create mode 100644 bin/androgpio/ModbusWrapper/jModbusMaster.class create mode 100644 bin/androgpio/ModbusWrapper/jModbusSlave$1.class create mode 100644 bin/androgpio/ModbusWrapper/jModbusSlave$2.class create mode 100644 bin/androgpio/ModbusWrapper/jModbusSlave.class create mode 100644 bin/androgpio/ScreenRotater$1.class create mode 100644 bin/androgpio/ScreenRotater.class create mode 100644 bin/androgpio/SerialPort/SerialComm$1.class create mode 100644 bin/androgpio/SerialPort/SerialComm$2.class create mode 100644 bin/androgpio/SerialPort/SerialComm.class create mode 100644 bin/androgpio/SerialPort/jSerialPort_Event.class create mode 100644 bin/androgpio/androgpio.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$1.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$10.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$11.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$2.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$3.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$4.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$5.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$6$1.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$6.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$7$1.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$7.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$8.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$9$1.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient$9.class create mode 100644 bin/androgpio/customsocket/AndroFTPClient.class create mode 100644 bin/androgpio/customsocket/AndroFTPEntry.class create mode 100644 bin/androgpio/customsocket/JsonArray.class create mode 100644 bin/androgpio/customsocket/JsonObject.class create mode 100644 bin/androgpio/customsocket/NTPClient$1.class create mode 100644 bin/androgpio/customsocket/NTPClient$2.class create mode 100644 bin/androgpio/customsocket/NTPClient.class create mode 100644 bin/androgpio/customsocket/NTPInfo.class create mode 100644 bin/androgpio/customsocket/SocketIoObject.class create mode 100644 bin/androgpio/customsocket/TCPSocket$1.class create mode 100644 bin/androgpio/customsocket/TCPSocket$tcpsocketrun$1.class create mode 100644 bin/androgpio/customsocket/TCPSocket$tcpsocketrun.class create mode 100644 bin/androgpio/customsocket/TCPSocket.class create mode 100644 bin/androgpio/customsocket/TCPSocketServer$1.class create mode 100644 bin/androgpio/customsocket/TCPSocketServer$tcpsocketserverrun.class create mode 100644 bin/androgpio/customsocket/TCPSocketServer.class create mode 100644 bin/androgpio/customsocket/TcpSocketJavaEvent.class create mode 100644 bin/androgpio/customsocket/jSocketIOClientV1$1.class create mode 100644 bin/androgpio/customsocket/jSocketIOClientV1$2.class create mode 100644 bin/androgpio/customsocket/jSocketIOClientV1$3.class create mode 100644 bin/androgpio/customsocket/jSocketIOClientV1$4.class create mode 100644 bin/androgpio/customsocket/jSocketIOClientV1$5.class create mode 100644 bin/androgpio/customsocket/jSocketIOClientV1$6.class create mode 100644 bin/androgpio/customsocket/jSocketIOClientV1$7.class create mode 100644 bin/androgpio/customsocket/jSocketIOClientV1$8.class create mode 100644 bin/androgpio/customsocket/jSocketIOClientV1.class create mode 100644 bin/androgpio/customsocket/jUDPSocket$udprun.class create mode 100644 bin/androgpio/customsocket/jUDPSocket.class create mode 100644 bin/androgpio/customsocket/jUDPSocket2$1.class create mode 100644 bin/androgpio/customsocket/jUDPSocket2$2.class create mode 100644 bin/androgpio/customsocket/jUDPSocket2$3.class create mode 100644 bin/androgpio/customsocket/jUDPSocket2$4.class create mode 100644 bin/androgpio/customsocket/jUDPSocket2$5.class create mode 100644 bin/androgpio/customsocket/jUDPSocket2$udpreceiver.class create mode 100644 bin/androgpio/customsocket/jUDPSocket2.class create mode 100644 bin/androgpio/gps/AdzanCalculator.class create mode 100644 bin/androgpio/gps/DataKota.class create mode 100644 bin/androgpio/gps/GpsGPGGA.class create mode 100644 bin/androgpio/gps/GpsGPGLL.class create mode 100644 bin/androgpio/gps/GpsGPGSA.class create mode 100644 bin/androgpio/gps/GpsGPRMC.class create mode 100644 bin/androgpio/gps/GpsGPVTG.class create mode 100644 bin/androgpio/gps/GpsReceiver$1.class create mode 100644 bin/androgpio/gps/GpsReceiver$2.class create mode 100644 bin/androgpio/gps/GpsReceiver.class create mode 100644 bin/androgpio/gps/KotaIndonesia.class create mode 100644 bin/androgpio/gps/PrayTime.class create mode 100644 bin/androgpio/gps/kotaindonesiaminified.json create mode 100644 bin/androgpio/mycodes.class create mode 100644 bin/androgpio/networkrelated/IfConfigResult.class create mode 100644 bin/androgpio/networkrelated/ethernetmanager$1.class create mode 100644 bin/androgpio/networkrelated/ethernetmanager$2.class create mode 100644 bin/androgpio/networkrelated/ethernetmanager.class create mode 100644 bin/androgpio/openweather/OpenWeather.class create mode 100644 bin/androgpio/openweather/OpenWeatherData.class create mode 100644 bin/androgpio/openweather/OpenWeatherEvent.class create mode 100644 bin/androgpio/openweather/jsondata/clouds.class create mode 100644 bin/androgpio/openweather/jsondata/coord.class create mode 100644 bin/androgpio/openweather/jsondata/main.class create mode 100644 bin/androgpio/openweather/jsondata/rain.class create mode 100644 bin/androgpio/openweather/jsondata/snow.class create mode 100644 bin/androgpio/openweather/jsondata/sys.class create mode 100644 bin/androgpio/openweather/jsondata/weather.class create mode 100644 bin/androgpio/openweather/jsondata/wind.class create mode 100644 bin/devices/Button$1.class create mode 100644 bin/devices/Button$2.class create mode 100644 bin/devices/Button.class create mode 100644 bin/devices/Buzzer$1.class create mode 100644 bin/devices/Buzzer$2.class create mode 100644 bin/devices/Buzzer$3.class create mode 100644 bin/devices/Buzzer$4.class create mode 100644 bin/devices/Buzzer.class create mode 100644 bin/devices/PCF8574$1.class create mode 100644 bin/devices/PCF8574.class create mode 100644 bin/devices/PublicIPChecker$1.class create mode 100644 bin/devices/PublicIPChecker.class create mode 100644 bin/devices/Relay$1.class create mode 100644 bin/devices/Relay.class create mode 100644 lib/arm64-v8a/libjnidispatch.so create mode 100644 lib/armeabi-v7a/libjnidispatch.so create mode 100644 lib/armeabi/libjnidispatch.so create mode 100644 lib/x86/libjnidispatch.so create mode 100644 lib/x86_64/libjnidispatch.so create mode 100644 libs/classes.jar create mode 100644 src/SBC/Amlogic/T982.java create mode 100644 src/SBC/AndroidNTP.java create mode 100644 src/SBC/BasicSBCInfo.java create mode 100644 src/SBC/CPU_Info.java create mode 100644 src/SBC/GeneralAndroid.java create mode 100644 src/SBC/GetProp_Info.java create mode 100644 src/SBC/Hwmon_Info.java create mode 100644 src/SBC/Ifconfig_Info.java create mode 100644 src/SBC/NetworkInterface_info.java create mode 100644 src/SBC/NetworkMeterInfo.java create mode 100644 src/SBC/ProcStat_Info.java create mode 100644 src/SBC/RAM_Info.java create mode 100644 src/SBC/RaspberryPi/Pi4.java create mode 100644 src/SBC/Size_Info.java create mode 100644 src/SBC/Storage_Info.java create mode 100644 src/SBC/WifiInfo.java create mode 100644 src/SBC/cpudata.java create mode 100644 src/androgpio/Closer.java create mode 100644 src/androgpio/DigitalInput/DigitalInput.java create mode 100644 src/androgpio/DigitalInput/DigitalInputEvent.java create mode 100644 src/androgpio/DigitalOutput/DigitalOutput.java create mode 100644 src/androgpio/DigitalOutput/DigitalOutputEvent.java create mode 100644 src/androgpio/DigitalOutput/GtcAndroidOutput.java create mode 100644 src/androgpio/DigitalOutput/GtcAndroidOutputEvent.java create mode 100644 src/androgpio/DigitalOutput/OutputDevice.java create mode 100644 src/androgpio/I2C/I2CException.java create mode 100644 src/androgpio/I2C/I2C_BUS.java create mode 100644 src/androgpio/I2C/I2C_BUS_Event.java create mode 100644 src/androgpio/I2C/I2C_Device.java create mode 100644 src/androgpio/I2C/I2C_Device_Event.java create mode 100644 src/androgpio/I2C/libc.java create mode 100644 src/androgpio/ModbusWrapper/jModbusCoil.java create mode 100644 src/androgpio/ModbusWrapper/jModbusHoldingRegister.java create mode 100644 src/androgpio/ModbusWrapper/jModbusMaster.java create mode 100644 src/androgpio/ModbusWrapper/jModbusSlave.java create mode 100644 src/androgpio/ScreenRotater.java create mode 100644 src/androgpio/SerialPort/SerialComm.java create mode 100644 src/androgpio/SerialPort/jSerialPort_Event.java create mode 100644 src/androgpio/androgpio.java create mode 100644 src/androgpio/customsocket/AndroFTPClient.java create mode 100644 src/androgpio/customsocket/AndroFTPEntry.java create mode 100644 src/androgpio/customsocket/JsonArray.java create mode 100644 src/androgpio/customsocket/JsonObject.java create mode 100644 src/androgpio/customsocket/NTPClient.java create mode 100644 src/androgpio/customsocket/NTPInfo.java create mode 100644 src/androgpio/customsocket/SocketIoObject.java create mode 100644 src/androgpio/customsocket/TCPSocket.java create mode 100644 src/androgpio/customsocket/TCPSocketServer.java create mode 100644 src/androgpio/customsocket/TcpSocketJavaEvent.java create mode 100644 src/androgpio/customsocket/jSocketIOClientV1.java create mode 100644 src/androgpio/customsocket/jUDPSocket.java create mode 100644 src/androgpio/customsocket/jUDPSocket2.java create mode 100644 src/androgpio/gps/AdzanCalculator.java create mode 100644 src/androgpio/gps/DataKota.java create mode 100644 src/androgpio/gps/GpsGPGGA.java create mode 100644 src/androgpio/gps/GpsGPGLL.java create mode 100644 src/androgpio/gps/GpsGPGSA.java create mode 100644 src/androgpio/gps/GpsGPRMC.java create mode 100644 src/androgpio/gps/GpsGPVTG.java create mode 100644 src/androgpio/gps/GpsReceiver.java create mode 100644 src/androgpio/gps/KotaIndonesia.java create mode 100644 src/androgpio/gps/PrayTime.java create mode 100644 src/androgpio/gps/kotaindonesiaminified.json create mode 100644 src/androgpio/mycodes.java create mode 100644 src/androgpio/networkrelated/IfConfigResult.java create mode 100644 src/androgpio/networkrelated/ethernetmanager.java create mode 100644 src/androgpio/openweather/OpenWeather.java create mode 100644 src/androgpio/openweather/OpenWeatherData.java create mode 100644 src/androgpio/openweather/OpenWeatherEvent.java create mode 100644 src/androgpio/openweather/jsondata/clouds.java create mode 100644 src/androgpio/openweather/jsondata/coord.java create mode 100644 src/androgpio/openweather/jsondata/main.java create mode 100644 src/androgpio/openweather/jsondata/rain.java create mode 100644 src/androgpio/openweather/jsondata/snow.java create mode 100644 src/androgpio/openweather/jsondata/sys.java create mode 100644 src/androgpio/openweather/jsondata/weather.java create mode 100644 src/androgpio/openweather/jsondata/wind.java create mode 100644 src/devices/Button.java create mode 100644 src/devices/Buzzer.java create mode 100644 src/devices/PCF8574.java create mode 100644 src/devices/PublicIPChecker.java create mode 100644 src/devices/Relay.java diff --git a/bin/SBC/Amlogic/T982.class b/bin/SBC/Amlogic/T982.class new file mode 100644 index 0000000000000000000000000000000000000000..d4faa30bc3bd0ce65433d1ea8551a6484664587f GIT binary patch literal 1147 zcmbu8T~8BH5Qg8=7TPW4(+@-xMG&EUEFu>g)CeVL6M}?7Z=2I{poeVFn%&c){wd>y znt0(4@JAWnT{eIp^~N;a>6x8*=Q%Ue??1kN1F(umS)>GpDjOwtz2*7Gy5a6WdAyQE zCh=~_P&fFxY>s?^oU+ZDku4=KvV9^u()FY{b}Kf}=6Fq@zvP?HN@J_iYpWa_fecfY z1$v)qqwRBn^z2-f-jaW)@=oJ{;)<*5LTXhxe%eohr@*C2t%0Nfw z#F?>89Yz?xvlCwmpw_d=BjyK z`c?tAF(FV0m2JPNsg5$1Bzym-{b&8C=jpn9D=^lTniOoarvg7_{&mF3Jp_7UM)soO zw}Xb-($R#RXla(Bg<%}JZSLtr+e6jz*6wC4k|tW&7!{N} z8HOs%Vu7vu*RZl!5*Y0g!S#yx=08VZINDa)EKe!q=w(mP z$7g|_XY>i$zTy}37e{iRF!(VJ9OiQ%mM97KmDIV)7;Z8<0y-dWhSn+0FDw>6WBdXqPa&{Gj=_;g@hmVA zVG8cxZj9&f{`cZ&2U7$~^E1ti6z*e&HoE--erH3>Q3jMtVIGTcXB;fkuFN<;0SO!i AL;wH) literal 0 HcmV?d00001 diff --git a/bin/SBC/AndroidNTP.class b/bin/SBC/AndroidNTP.class new file mode 100644 index 0000000000000000000000000000000000000000..5a512dbf5046ef8b6bb38bcca775be7066b6c2fb GIT binary patch literal 2444 zcmbVNTU!%H7=9-SEQzZi8Wc}eL7D_)t5UUK4*@*H25Jq(Q(K%QE3PiPX?7BX_OgGZ zSG{ffDAM+Me0tqq(qB>g&L%)4;ieavnaqCke(${BVgCO2k2e4&@PmPfz=6W_jFt7u zRo^MkFWfN@6X<&;>(X+iw`_e;d}f!FK%^)I2J_NeTYYL*?ZAJcR%O+;iWAbB&Q1x$ z?Yix$dAVW>^yhZ~3aaXO%QT40?fTMy=9lhkj_0UP1fr?*qCjlMFWU)3(Q9HKOo86K zBTq% zj+p2|cN~K_Zr~XI?;(SU6FA9hlxps_2sP!nR$kM!rxJJ%XAGQvN8yG*CeC6=V4w2y z{;FM_kpZcWq#BG`G1BcMHN|r#4D{&L`zGQ@Fd)}=0DPF z{`lRRr)ZJ$>Q3NL*sSOIN-D?qsOzb{WE;Nal+Ftx03 z%Tv}&;}f;GhzSFiLNz;@nD`KvspGP(!b6_A%e-^zc2!^~mF|qZV>H!+s|>xTDocS~ z<*5WFam@hp;q(@%O4YBEeAS{pifI}Q-*o+=bWMB==3~54Qw@CtZt1k?z_Z0+dt};o z(hqy^8Ifg8`Ab@C&1%}j3}#sf^q2D}+m-+0ET^$=u%frIm)3oc@s6{u3!wx8=1p7a zEO^K-4C;kY#||~^hjkw?Y_7`&pi{ugJAs-q@VUVGjs>vSnFMa5R}b?h4#P~~4(=NG zlA#UTM3~)MoJ;UsvvujXy1sL>WSUH+AGOP=u@+6-!+q8c&uqa6q&g*e1K=Hp zOvF2QXyOsRrgVgub%Kg3*BYJdF!gEcW%?@LnpnbP)_S&F-c?b}zNRaxo1L=eGN4xH z;@L|v=4ecuzthIxJozVXKX{FNeslwK zoA_dG?7?LBo6B9%%dwwvWUxCKyEs33eiL79;K2sI`7tJvv1HdzZ~qzng{Py`1N5ws z&eF#a)gI>FIj%-9&iN{p(3~=%hLcTBJxq3qBDwVOO9^GN8Ddy%Vnp$T_JfGL<+Th0 z!oY0<%Me^FgFzH{x*h)<4{9L~Hg)TFHt_r}`1u``5~0BuJy|X49BeY_BiwhWa*QA# XP=v9maqj185_P;FlQsUnABq15J@{3p literal 0 HcmV?d00001 diff --git a/bin/SBC/BasicSBCInfo$1.class b/bin/SBC/BasicSBCInfo$1.class new file mode 100644 index 0000000000000000000000000000000000000000..8132cad509b0220278332b503359cba647c11de1 GIT binary patch literal 585 zcmZWm%T59@6g_2-k#S@Y!S{m&3&cb;ai=j6qKSzbBZ5m(pu*_bn$Do#W#S`o;RpCp z#@mrZql@<5-ky8z>FLMk+dF`56m29Ja*bNOTH~SLrQ}FnsnV8>Bt!O^-}7p~Wxskc z(1Q0EQqjN`QSvyzK~m!8?I4gH)FXEv|iY9u<>D21EYel+qT-u=G$mC}7sc487v7P#1G%;Cv%e zckTLFv)v4NU$jpYX;<2pebc+(V$q;-G3r!A4@%#(Pedd%gVRt$-4%zv5jgeRxea3( zJ>x)j0~Hb%bqo|4=N?HZbUolsGFB)i$&^W~Qs#`PDg6TEgK&WW literal 0 HcmV?d00001 diff --git a/bin/SBC/BasicSBCInfo$2.class b/bin/SBC/BasicSBCInfo$2.class new file mode 100644 index 0000000000000000000000000000000000000000..105a23b81ab6b163dff384bbe59463772f5f8ec6 GIT binary patch literal 3439 zcma)83v63g8U9Y}o4QTgH0zSJX=}Sly>)F?lCdpLoYq}phd2+C6vDNAW8cp4 zP3`ME7_cYr=f-1gDepmG&bp2Z|PY*Cs1RhjeP4( zysLlhWnBUdb6Tb~Vb09v^nBhhsX=Z=v*h8+8pN!ekxlWiTON)Z){HhQ(AdAKO)96& zrj0~C5VW^=uudL4RnRhqwJ0FM0vjYJtLw=reNNBv0x=<=*z&Z6qOisJv`#I|X=`4~ z>4EqmEfDNpy*00AlhT?>y^6^2M6HxllDkRLX)T+~=(*u^PS=tGJIbn>`M|NtJ$_ts z8zHl97dYcx572rP%A(d<9iHIU)orMdQqsACQ$n{acb$s9Q2}RozE=k>> zq84sRwWz3rDydy6uEPdNwW`>NdP(h7u?ZeYc~xx27D@S4G!(yWQ?V7>+;HH4itPvp zIK1-0jr~Sej}&I&L{W=pbcwu~&@!W1&XE60Os5Pkfi3HhYduJ4H=FANleT!nzEK6Y z3hcCF%rFDNMN99_<+R0U!I~{tghbb7+~~joH#%{f0t%~0i6UewUJ1I*mDciU36HIM z*o8Yu93MO?zYMDAK`(u*%V4CU4c-dmOTO?;xX?$njt_>8x^RrtSSUDHi{t25a2Ek& zz}Q*0#^bF$rL^g@b4EoU1_*N5kuQI|%Z(^rrC?BC+chpwF@zWmTuo1b;QuW>T(pze zRzlk4n~-*ms5piJNsXy^wba@%7$IOCm{2i^6HH*sEc--y%r_a7+pkqIC1a#IkumeS z0*yXiRlZ;u8ClBojzey|4ha=W&?g)8Y=X`iHj7T9wcg2K*jM3(QUbV3;_hO-DCp5?fvw(&N)e@F#B>Ixf~<|{RdZ3rEKV`n`BY11iyI4gqs;m@tx1fMExu7V?!yBL?x&kqQ#Bk7EhO|=i@id{gLsG_vtt=cP_s=czM z+AA9>pLkUq+tEm=J4nU(pSw8X-@l9|f7>#4`rDV$>~C-`<3@i&^)hz*8*7%a$KR+d zW1qj#wG5xXv342z{TI;wq%H9#&NY+>T*S?sRhvG7gSdtN1fP|Mi}zJM!`o`edZ_6v zp1X*S@e4RI;6IO@8@rxOa}B`z=sB)k?VdYU(0vv+ETOyI z6S8T~5)QX}j@oqN64g78=BE(ma%0;HB4bO~TVxGaur?Q2qh%J4j(0jazoyaYcpCA} zYHsa5V}HAc~mCq9gcBFV|8rY>3kZ~u@z*-xUqi)xv?juEln4(Fz$K%1-ya6 z?p?u~SMc@~ylV+3JP!{vJx(it+c_j&)f`X-OCeud}p zYrKHpi0koN(Tv}TUHHA&gFgr_{wVh2PvQXnEN;eM#3B4u+={=62>u~P@SI5Dc`?uR z9{f{0h=18}b&NT{*eS)8q(08rX97~UPvDb`<t`x~VPcQs&;qNvCL-i?;FVOn ziYE9Z^#K*lW$IxSDt1U-mx`U(C8fkG+XoMhs@Q{L0-k_WzH7oT^>}G% zfw9pR@@`tJw3eUIiiZ5IFzaPv1@_dq-hGMz+ilf?f_4X@Ju?bIG~7+JVTHz+{6xtq zlpH2p*Oq+f#|dfANoGD!8?&AnAL09`g40`IT1JbC58^`t-kfIVWQbgsqFxM=xG*^` zSDsPv5inpB-j1F!b2Z8YatUX!|CSzV5Z~7|zN~j29 zTvAV{n37g|CgTiPKc-b&l;CK{=1X=?a%NS0)a9h}maY3Rk0%veVoo=V-hO~oJcW<( zz+NjEPEyP2c^*SzjT>Laf&z`1++G`0q`^a3x4=nxwpXs_be?}J>s;50dT3!l3x$V= zq=Kx9MdT-#8OefH35YuzoaL! zOnNmMH-J(LP#+AO@PajBSISNob=|acmLqGNoLN?J9XAL+tx(WSde~R3wNmqXYP43& zB|;2*T*W8wNrJ$!%JMvff!ZaM{WLzK;Axh-d)|jFE3a#&bndePdjs>;3oDac`7A!K z;Bzhx?;Cj)H}M65&Z1`6dPJ7Qv7)s!YGkv_lId*J)Ks-|m5^2WR^e2DnV0RR5`p86{*Q}IF+{+eRxUg`vyzhhQ4TGQ;Qeh z5`Y)qCVC@NQ;Dh6Xe2oujSo*p6Y*3uJ~KQK9rfZCQ&*l&CN55mMDESJx_u^|NR19p z4}0-FfgR;F)6rNYadFyXz8mltx)L@zNA$urgfPNVzBy}(Jw>B0}?T131OR$ z=}ykdsQ49r%>*S&#jKXr@1NH#CC|NyREZB&!KMPGS`K>_-o2Rnq815RY&ar25Kx)V)u;>dE?R$1TzG7W(IcchGhh$738j=Hlx( z^$V^5Dhphi9YeS9@G6G;{ExY`e-)?u{KGEYwu&C= z>wE(v91irZg*{jc!t_kV|P)&E_-8~xv} z(Jl?;#x&{w;G<{LN1~@5|G?WUfE)5O61nzXN>hAcK-`!6!a??Xl`!y``^W`_b~HsPSq(8 zEOH87cuK)bo&UrU$~5p-_b&Fnhd3khb18<`A#q;MlK)HmhE%(ImPzn8%In#0&$Thn KxAD8irvCt7Z-EK` literal 0 HcmV?d00001 diff --git a/bin/SBC/BasicSBCInfo$4.class b/bin/SBC/BasicSBCInfo$4.class new file mode 100644 index 0000000000000000000000000000000000000000..56c4031ea71cd95075691af53ccd6428fc185bc3 GIT binary patch literal 3411 zcma)8TUQ&`75)wajgU_;0pmo8UCS|!C1EVr#h0QuHNt@9f+5C*0&d7i8qhdmhDakE zdU4w3M%rAQw25OUZIiSqZIfQ?8sVDNuGKvBp}(L%q7PZCd1)W&erF`GxUE!cjrO_h zv(G;J^6mZi|K9!!fHo{?s1j%xjCV%kdfrTNkgz7~<~9x00`(X489kcSt;uMA@`90a zG_UcwTXPfD$nn#EDM}{WF zkg}(yb4EUI+EgJor8`R6-OC1@oM}yxd5dCxs-S00XI4NY1nSg^WfQIlwThn5~%L9(?%@>9+1%BQ==UcweYLa7bLbos!>ql z9@MGPLlRq2uSU%h_o6|K9+qgtHZ=-KG(q~|K}2FZS_C{Hb?2^4(T~l&C83`%JTx-*4d-EjmGoj3YH&-z<~Oaz`gof-%A|E5eCSl;S8H;C*xk8 zP$!N_d|7SPa_f{~PG+21#Bf~0BYfntoXMO%oiS7SXxwzB^y%hKhK@NY@hHB+lO1P! z;^W8oqcK*Tv+n9O<0SG~0;3BsCTu(l8*f)6Fc?j>cylqa&Bo zXZs7zbirYu8TynT{fPK6h_7ilw+abLfhaNL`Yog9Gb$W?_&Qy8dZ0(08I>5rd4htW zvXsHm6sm~ia%_i3eR!Puo*wAx@!<)MhP&bewMZhZA;kdQ#0~Uu2`0K-jF5O7#Gg&g zvxeREGXo?8lTo*fI0EtiTR@`p6dJD5Ua|0~QvFGZC&1iHVoJhNig^ak&?fzumUs$; zm@SUIVsO4W6dsFfAYwHxKeynRSp~+%kv2c_n31>$VpyGFrD*4KcFF#Xn$TE05w5Un z86o`mCcdrVTg_`Zhkxv{w+a!Ndl z=NLpe-OL+Z3Us@3_S8vpVuC1ZIp;DAqO`#H29B>#xf}{*lV(++DZgTLY&~10rsb2$ zvM)%ys8FjWm9_JRhL@=I-Itf-#S3PZ2=BuW1@@_$Gd{>PHmrQccE;%u*WAGg-OL*4 z{lUKcWUxKx#~fZ!>G$fI04}Sj^6oO;(C|9Fx13Cg{;sD}#@AhqQfXgQD?%DD%Aj4yrHpR+n|V^%`W#T6Wbx~=R5-EW=rf#*ot7wKQQ-c~Y$GtJ5M;w#iLF5Q z%u36%&XciKxm*r!R-1%n8M)4^p3fUJP2FnnqX9}44?z}k55F4t>g!ck0Ms|Ap1`H- z2wcj3z@_X4T*_X+rR)S;%07T?VHLmaxR28A=jc4=wWNn4Eei-l_AlVUNb3T2Mw+S@ zuq)D3vw+=^fOi3VA^~jydm{ng0`^4~5x(xq+RuM4WkFm*E5}leS+EWuN-A&=Ev1{P z=BTa4B|CBx5B^OYRiub7mdMc?XupLMy-VmGX<5XnKDE5Rh_5c8e`FDlo#A}vNPBe= zXFsCc3_sdq*I!9@(#Tc^v2D8}6iW9tSxYcPHEDv8Uw* zbV}XTTExWgJc68GsF>Gto?V%f$!@O+)Oc>8(C+2(?yGp1WDmc+!o&Z4o|-?yGvWz& z2S=)_Z((+@h-Zd*?)iCKQtAhmFgMchgGIc|#n*~>vxuujyvrTRI#04*R>S+0#Vbp) z=5a)cY54idsf{&Qed;>R--=K1FQSbayOuQ#z;(c*ad>}0Gp&J6H zN^UNPzz(qkAL5tPVy|e#JbuN1JB%g#8o!}Fw{Zj?;bR8&C*=Pv!^exy*oplPzvu2B zsO@9ea)(v7MuX7Mp`lwtzs4>EQp&Ac{{wjkN1;{j^7`v8#~ljutn8@sm;a5PLlk_2 lf~)XH+$60gjxOL9M-OmEMX&lL(Sbjm2(TyZ!0i)({{e&+j0*q& literal 0 HcmV?d00001 diff --git a/bin/SBC/BasicSBCInfo$5.class b/bin/SBC/BasicSBCInfo$5.class new file mode 100644 index 0000000000000000000000000000000000000000..d540f90cf4d176602faa0cd691e3b4fd0e415e0b GIT binary patch literal 3425 zcmbVO`Ey%W75;7{`^oA};wo{1vJfS9T3NB900}g49TK^As(2+1Hg*^1S$dLQD!-@d zJvlXN*_Upt-O`q&fD2)3h-K5zvdxsC(;wJj7>54?Iz##HlkCWrJuqnI>7IM;x#ymH z&Uel|ul@U_R{$KvOByx_ghmrR@r3S}DH2I5W49gC;1_7VsL$&0yl!RV!_yayl&hhU z(l@y0N|vQh=M8~IH)lF+N4Pq$R#}%oU{=q!&6>s~fo%i2l`h(5I6>95t9Nz#dHiSZ4_4@P`MfpXuRFDO!uU~g=qaH)-k)@$a+q4vW8bZseMeq-(#nZ zCJ4MmLPJ1NyCj+rRMZ}cn;;eSR*B6ODk8B3TNUqsL^EzyRGUNy+k)`n7K!b+RlpZj z%C`=fmN8VCnKp{&E8z(Z*eN|frWZ{$uQL5AW&+#mF57mD-tZMmRuG4AL_=KQW_@kc zB;JO%GqhR59XHc%&O1D*b{v&Brk0wxbf;lvb8Zvfh2t7-WQ%$sXQrHZ!gOc! zLR$|5YGx%)fM++IK6fTDbsK*IkrmIczojdPZoFFq=2MTmY zlga<4yL}^pE)6~F9oVB=v$_*R0=+6EeGF!FLmlm7%3XI#BypCINEdY`$a?Y>tzGS7 z8U_d%FF8y*-p7O)F1dx0%Oo-MnIQTxq>deCwnS@ySIr@H-g_{r;oPd+uB=L&_u`q; zoty$^07R1S?8q6l=Y0|rU=lSND$xjaoWZJQXWUDA(TGnU)#HiNgxLEDv9*;jQ8M$aVM_Z4B|Zenbn(iZDxE4;ACY(v z9~ID8@JhyThK{er!g8?c;St0fh64Bm3&@niTrn&sXS=E@tCdeld>Wr21oT3IRaBs} zR_ijt>a?5ww*V0I{iU>L10I8 zvUX#2kvqSHuW0zPmzwK(RpN0xA+Wcon~u?|o`=4oJ#&Yd$uI(zdp^fR;^n4lFIRP0 zjiFUe6*+nJLRLL6c~`wyt)DU^6l`8CLbJIKdT&oW{zz9#W?RSKHCyH4fpY!J^Yec$9W zysR%dyrLz5ZxbsU+oay=i0MSEQa(={OnZg90^rJvzNSE{CpG2O!*zwKMU4rSu~|`e zRs6P2S)(4oreNPHfjS>hs%1r@=2qly9(DE9eS>~)VHS5ByYo++#Xm@G=QUy&tC%F!0_<|)Z%P98b zb;mL2vMs9%C*56nMG;|h;N#Z-t+`qC7eI4Jbr;^q-ohK%S$HG+3U6dr;f?Gm*c@)+ zw;e6ivV+tZ=hT9Z*r5gNjCCv^9P3=b?pWBrfW5Jnh6U`4wKOhZe=Mvmpf$FLgD-ko zqWo{97KlZ(la`7`2VywHQINW|a>k~|X1;&L!MFa2<(> zLyNd$P_aA8ICCBS6Qp-9VsMD#-iZ_bGDfaYca9&uujA1~vRWqj7x@G^W8KCbN^o$&jY@W^NxUmSl? zU1q1+6?&4=RQq&AzKpNVBUZQijha=dwuZh%NfQ6BCB79)82V1(7Y!^%YHVc@@j-=k zQb9vphbCUe9`Oneil0&9mpCqdg@?qecvActm&I@Jf_M#A#OqiRzr|0*@9=Z+d+Pav zXb^uCjp9$DRs31BiNA=G;&0-t_`7&i{KJFFNdkv(s6u6rIDmP)K(HJXVSESQrCrDI zI8ia;!4G9hX8ed6BDYtI@7vvl0f0J^) z6#*r9`5q0={}X!yZ=i>YDoDPHA5+lJ%6bipq;_#dVOjkyunAo{6=t8?jh~zf{|DIi Bqt^fc literal 0 HcmV?d00001 diff --git a/bin/SBC/BasicSBCInfo$CheckIPReachable.class b/bin/SBC/BasicSBCInfo$CheckIPReachable.class new file mode 100644 index 0000000000000000000000000000000000000000..c0bcf062a781800992fbc2a195ca40c9e2a4eebb GIT binary patch literal 2115 zcma)6T~iZR7=BKYWy89Z00I`##!77xf>8@7AXE~dEQXH~jfH;LWpfBi!fs}FgY_Ty z7rfM)UOOYvwlkga&hffGp#3AAz9)$ZO)`!byXQUU{eIq$cmMwWCxA)VGJFh|GRagT zsk^2@gl6sAW2pmf6tt~vt{VsXUXjZPFoX{EV?9ySt$bp0?~ogwj4p=$=IeIZvV=54 z(9@kf_skOU(@lYl=a^Q0jzO`w_sDh%IeyG=rbLgPdBp9q$G|j(o|J95o^E+ry;$ac zVBiTumv>;gW7mYm)_BQc*KN}>y#9J|2*lvC*$cxVO=lPM_fMfi>lH^`VWyK zNYm(+rVx^DVok5Dy~OBi ze32f1gDX$*maZeOF}@yugZNYY79aOhG!C8FAFXx&l-Be|Ykk+y648mE{36zBnv*m(Yn1LJ3i!T$ z{#ZSq(Qx=7z+Dj4k?tTUEuvRSVpvMyqO^pFw2Vn<1yj;pOiO7Lqzx3MO+1sf@m$K_ zFDZ+^r3cj_tWpmUsP!TQF@cBpnIIn~4?6a!91--uK#qJ))5_)8CvK6bJPwF@UZqT; h4#`e00%aL%^kwPWxk2g$diIgR5uFN&F-pvW?>{@a4AuYu literal 0 HcmV?d00001 diff --git a/bin/SBC/BasicSBCInfo$CheckIPValid.class b/bin/SBC/BasicSBCInfo$CheckIPValid.class new file mode 100644 index 0000000000000000000000000000000000000000..d7934d6f26c558452ca6aebe378e82a1baf6d756 GIT binary patch literal 1441 zcmZux+j0^?5IqB|3+u)Zk`QAwAr}poBxt+`uV6GNi5enR`l1sUam{9z+FiVSieKQf zRS_+%vdUL~#b2=09zcSShn-7LPj{bldiKZfFW&&1!=?%!!---xm(Ft6DAOWuJ-6p_ zFGP7K|DeQ8qoN|f5Pr#D@wCaUYWn{6OHuYz9Ab#L#kWnt5cIfH6`oO}^TNJE z(Q^!|nqfHPy)fLl)AF=8uROhCScZ3vVLsk}+#fDcVhH5yiU@&WP)7)w20w;$97cq} zA5WAtj3BCF^gj@-YIKa@Cb{R(EiT(sK?lB!If)u}CSYo5TyDglp7S>3?E_1WQ z9YgNh#()IKFx6N8f3y--kA=2$FT6yLn<77O^bVILk14KuD?2BF^i$fQzI)uhoQAVOTuKwGVGQZ6=7z z)K0P0H)T=t4BJ|Wt-ICO3MJ3rs){QN!}}F?ymVZX>TAU5I#JB4LY8tv$4zMtdUmTx zBzZXgd7W0=@aTomQHV|Z^n4uqGqt(zXP z-NM!@90MbzN4tP`E>r&u6g^(vS?9HO<7q{^?v%xCLq5Zy?gzajt6sIWa2sFKTau4I5F7n8#0yi&N<(IuD%hY?AMWD;&6>1Rvpt0GFWKTy-2eap literal 0 HcmV?d00001 diff --git a/bin/SBC/BasicSBCInfo$cpumonitorrunnable.class b/bin/SBC/BasicSBCInfo$cpumonitorrunnable.class new file mode 100644 index 0000000000000000000000000000000000000000..a2ac23564e7e7453de152f2cec4014c928afa765 GIT binary patch literal 2398 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 AW&i*H literal 0 HcmV?d00001 diff --git a/bin/SBC/GeneralAndroid$1.class b/bin/SBC/GeneralAndroid$1.class new file mode 100644 index 0000000000000000000000000000000000000000..ba024c3d33611c97fd6f94c8a2dbdc696f7cbde4 GIT binary patch literal 1381 zcmb7DOHUI~6#i~o>B9kPUfYr34w+6NCa(Pp zE?v7c0Zdr9aKXa3b?H)mhVk4^W2KM~oAl1P=X~e&opZna-v0t%5_4X-7`pQFnfMYH z+)|A>QL;?E6iIsFVFk>$oKXkGrHgWUq~ND00}3u+Il*GtGMpPHHN`caw>>ogrTn{ zEMC^DHn&=0F-%7PJHONZsDc}^6-Bn9al@z?swI0IR}lYe;C6*u#E3K`72K4DcIiSkjt0(rA$X?#(ai*x?y=*Q*I zXu%eQW|&nPT8=6)X|0uInv9h()M}Miy_jK$pF(0Q{}i=_MTBX`-Sp5{Fen$DBp3a? z@R8nMe}JyKCJ-ClMIiPW%BMP}lg>860BHp2RO)g7=MbVBHH8i*0u$sfafh)T1VUYV z=#jz>`Va8Kk-`V)Z|E9|Jw&9$b`f?ou~tZ|35hw7u~x`f6EfyN60ML#6OwQs6RnVm zCS<~aq*@`VCL|@O=7!p0-KMGo(< zg7@7D+W z4f+ZC+_e<7UG3^qANpbX(ED0-x%xkoQ4$#xyJ{XXCwtD>XYb#><>v?A{s5pCpGQz5 z&@wWZNu8ILbaZpTDmu1Nj2(&~ERfR6KU0=;QyJT`WWhBS4EIJ(xAbZ0#D=B2XgjkR z-8A!hVOF5loiR$W15}z_sWmOoxS*RcOUmM;T#%M4ASMK&O88LFoV}(m=qXdTrc)!X zV_4HXR(ry*4ELl!A|9l7V&$2xF@bQ#F3LIxY|yX{wQ99dLj>#9YLkXKM572{vxa(X z5eUWAp$%EXlEd@Y^U@jB^QIJN&e{dt9Mc^`{q}`n<*2}x0CQr8XmP!54J_z}Syr(% zo~=abV@r39MX?hd5$xhED|uxF_Wc~tiVy5CiHf8I6BR8F5uM-ju`2=>s) zfLk@}LtLO~TDra+7r09~N&pw5NT4TzZiZ?(4q4lp){w-0fe2$YFE33|cl@G%u@@_~ zCtZ&NVCozcXz)%yG)co@^zs7UDX=$Q5w1)5YfP$ir6yk#!4ZKrMa4IB$a1AKr5EI+ zVNKal^r2tFF&r1rO46;M9ga_Qjn$Ft2@NlTSyPkOsfr=Eu{a|gS+b|xMct99e6OAw z93ZbgoK{INAaGDcDtOx}AyhtRG@QX%Iyg5^2m1pYT$9Vn=JOgZV2C#7ywqC`!Xqp% zF?f%Ql?yoy!?;8mq+CIRQJsk5WsF4d3JYNQ!`3C~T5oXCR~b=#)N2TRV6Dx77Hwo&S8ov5Rjsu8HP16~%sGMf zC*494ww2N6^t@pjRVE*+*6zbdS1I#_mFIoCZ4GmHo2f**19l0-0@XjJi5^%5f!$BN zlo#2SpO1`=JHL#+E%2f=+kz^TMGfz$wh^VB{&}WJzMR<IPux^J82794 z;gC3M6->Lt;gpl^j9t|5AwE*w%GDh=XLDBBj!&R$8Ie~ex}U=;T-p%Fs*^GGQc0E~ z_>>{8Y}x@+m)Ad{Tb{e<;6Mv;q=%ZC)T9HNn$?{1o}6-gLm3;K#(fQ3okR`7e6}RI zZ=*5Mej81RVgZ?yP#a{2?E z_>bS!T+CxAQchDcCii}q86PZg2Q4x`b8abLgN%@z)i8?Ka(gzmUDu-qh5Zk zk`u#8=0uWVn_8I@!=AjFL~^Y;k$jXBT=_YX49W@7LK2%vVhc&MlEhY$Xd{VjB(Z}e zc9KMgM?6`(muU5+05emo-6W4tvjca6Lt-{25j!X`*z9III$zC$qM<#JH z=^~Q^nRJth`j4=mLn(zX_`E3&K9!Bbno)gZbv7>MpM_2P5*C8kDEV_|bA)V;l1)F^ z93z|KWRoVF6J&FeY)+BQX|g#*HiJ)PbA!G4No+!Rmwl!N@8Nwu!~9$L0Y2ucnKIRf M)hC2c@EPj<1&Fs^?*IS* literal 0 HcmV?d00001 diff --git a/bin/SBC/GeneralAndroid.class b/bin/SBC/GeneralAndroid.class new file mode 100644 index 0000000000000000000000000000000000000000..1fad0e8f44d83705c7185094420faf8fd1f8e756 GIT binary patch literal 17093 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 F{y!u1*Af5# literal 0 HcmV?d00001 diff --git a/bin/SBC/GetProp_Info.class b/bin/SBC/GetProp_Info.class new file mode 100644 index 0000000000000000000000000000000000000000..ba170a0aa23f05683be470bb94999d69620df5f8 GIT binary patch literal 5573 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 T`#t`MKd+mMzu<592b%s16bllK literal 0 HcmV?d00001 diff --git a/bin/SBC/Hwmon_Info.class b/bin/SBC/Hwmon_Info.class new file mode 100644 index 0000000000000000000000000000000000000000..36543087c484c3da6ff2f3fc61f92c0e6152ce63 GIT binary patch literal 3279 zcmai0+jA4=75}X)X=Qn_jj+KAaR`JGTb2zTdfg z`_KRU;|l;Mu%;rUpnZBIn|Wnr*|z4!tOZ*|SV8Ai{hFRB>egcB!u(Yu?-m5#^kMSl%%`Gp`qE%P|&>>vJM9XIiB)ZS84dH3XwC znU?9DR`~Ammiyi)4D<))TcMy^ zLmbf<_VIX)ZE+>LNeyj?#<3s0DxOxL`A4dH)9?%q@JOb6&UAQk+gbIUT^9L+8V=z( z1+7KX^&~b|qV2!3IbM%_RM0&&sOF&-SZ5`((odNc&=sL%P-0K2b)8xmPC__Z9%?-(<;70+!}>6 zW_hd;N5gkejHcuLurs*am?aJveMaZjS2|F*ikC-wH*7k6eMn> zY!+!gj(K#-&w_?UEHO;IR5Gl>&dJ_h)>SjZ62w&vZ%QNh6)71JyUQ9ZpXu3wW05Rr zcuP7vk}ujW#axtC$h2q_%FL2uuSo7RT*C^n)eD8`a;fAPu4^#7p5{8K3-h>ftm17I zHx%?Xqc&-K=k2mp7`>i1N}g%6y5EryI!c^uo}aiGN6#~uo1*?+Ef~&Vw~o6r#Q&@(ZY!mK}g0SS0NcIKIKsHDQ^~^@(v+Y`DMRR zuN_eCQcI-nr8dx!N+celE0x&$5IqBr@zKCKo?62#l4mE;M zqut>bCI{DXIMKI`{xxW+)Zhj(4{<#88M#{+$7~;AQ@P4+;M6K^VJvVJ6UtE`GW>jrQVM?89O7;1rU`;wg;d zX^5kFhpPoeZXn7|~#JjDrhfxH&l zX~%1nS81iTdYw8K5v}kNQ&lJ`_N$nJ!o?I&VGs>2_p1Cu6-Lq6sELOvG#(19&}>L9 z#%h-S#VkqQZZj(%FdfrtxJi;*<4l=QCLdvT_7Sek4s75D8<-mkKf?U%1`0!w`)Er> zlF@Y-my+Rim`vbTm47EA>nJ9}j}a55{a~W8PJcO>{tB_diTl`<9{ej}X=ceue>ss} z$MyS&34K?2z-XQ)R5`}9mjL8RbNm&W;PWa!IsbRne}13iNS=iTi9C%C$__A*X-W*z z+EES}yOO*qzjANalXu%sltNoW_%Wr! zv~z?SKP7s{DSM0fMp+trs;e86_<+_zl#$hkTuDM#&{m;BJwxnT{?DqISNZCLuc200 zVo%s+>QfaOiPXxWsx8IgevSB9lGfG=6I)9)H&$O(aWhOU4Yz}o;}ZrgoMT%XGc_O` zmTvB)!N$X=^h(I@m1q2t8KIS0uVmRN=U98A$Pi$GFctWyLwJl{ql|!lsm*fqSFK#N zkjoduYIGS=k&3X7k3Zlda2cqM`KGl%plVvd&+&`8a;UDH{o5Ahi5-x-w7eTn|=Sh6n_p`f6NYxNC!^PryWZ(iE7 z)`(kRnMS`c(%oxpNW_h#K4b_|+jC+amYGQQ&k{7DEtO1LdeZ9B2Qx+?6$zR&eRW4e zv`K4EB-hm%$+$k;q~$5JHVs2o5mez7YjuFG@Q@zYdwWeIoi3y@aGSJ6=P{$!6;zyK znu!gDSxBlBnlGuGP_ZqP7~U|mkScM&CM}UPtXfu;+q2x3>M>J#Z(L7XEPRBZvL6;QT$CECUWYQP4E$AW1v2u*OSbpOSI; zKSpu#@>(uC1%NvC$htIl51DLC+Arm^nO5$NYz zCn(&V=8Ofr->~M(igC8A7!}i_el;W5mg+T%NYHYHW>Od#j8b8oof+ye%uc;$P}Z7M zTp#Sx%>>g9GiVJY(zw3uhg2~xMDE_5h!1N&U!jv&JFhI6+cwv(B)qa(p%z{##Y$|M zv(Pn@m%0@?nU^ZDG;gdFjFae5=oEQqNOZ^=z&GDU8w#CzsNxn@JfKi3FNOIuo#zTF zm>!Ms!k|L4Xf{#@3(Hq@FOyf67g7qH#tS9(!UDd%%r_O9!+f?2fcf@C=9P>>r+ZeG z+LxMFMie^3qiZ4GZ{~kYp}EZG%{#rDU!u^wF#s0a%|EVC+t?Q1dp5sZq4}N_&*q;{ zXn|*?&A$4){nZLBhhRyoxi*Rp#HQ&{@nc0^fcknEM5V+Bq%m}jF372C7>6`Ekrb}BT{o_$TBN%m})LX+*;ZiTAt*`8^1?Ad;WK1$(YdYYb%&@+OnWDk(B5`)e1*=b&;Tb5xa z75WZ+7ycU3t@wa}TY9{YLp%;>8_>-T1EnSzH)chpXTPV=_vr_AAmH2#%D`FwTZz+! z#5{T(mQUT`;62Yekl$zGOz zQ=z}INyYtUDl^=c$|Nmj|4pI4(?9TBN;Y4tm`^a_iF7Js#$jh(46H4N9Tn4(YRnGE>xX-vLd$16;8jVMq)aUFgB^@Xk%bl{y~~uB>a< zlbbdU7|@vNvo`9c(cCjrZ=N^zn2v#zY4MW}hlZT1J7Ju9)XP{hX_#$;dOB^SBcci& z*#A`*5!J}2!-&CtcVC)@5EbA<=QvWic7lq43J^XCxPrQL324+&mo5d3YUfpk%Mov{9P!%95f3cJqhQO~v=o1rfojGm zFov{Ubk0sG)`7olFur1`6Mq$3fX<~ZP=e0$1+T~lpX3W(=?h+!4{q@VU*HQ~lMg=G z7kr^FSkDKa;tP)Zf_wA9r}~2Xe8K(s;8tI7g4V+4%iwu$sMd{w5ujYCX8D4Le8I_l z@N8f3urGLhKKL|WaM~Aa<%8$=f;aerH|B#+_XThA1#iv=pWzF>$QOKZK6tJ#_)=f+ zmVEF$U+`tV;H~-KHec`+zThkK!Sj8=SNVds<%1Xafq^U+}g0;4^)} zpP|pX7b9SoDAcg)cjR4+MZVzA`+{%C2giKDH~HS@?fLh4u`l?GzTlhl!Dsn`Z>8HD zn-}2qkEYgU=^;(6%hDs7TA!uI zG&P!~Z)j>`mLAvC<}7_nQ)gu9+YS508x8wz6|Xm(-x}K25-gZ=k9fX1R5N3V)`(+o z7f+~#jeF^-?KDx-8r8y}*h9}{=^VAh!!2>RE7VdCx76XTQY$>%3WvK!t@3cI9ImcT z@o=X&++MZD!>w_+{c4?uTNf0%a%I-0gf!qK!<5{{-TCE;k=rZ##`+URJyMlJVn%N?t>tF<0(t;4-p zZT4`R9Szs2Gi+`R?=~Y?w1<9_rRyaTvWXimFAz1A!8_h-dJy?pAm6CBWM{IRg6Iv4 zsRSJ$e?Lz^Cs&GYtqdL6M08!{XgE~-Xfp{vomAj=ic0)WFqxK96|JBnXbm*=BZWth zx?9kSY(pb*1D%f>v64nqH$5 ziW4X)PNkGsNW-Fo){6_t6ho92n<*o<(gtxIZ4|fAh`5(FA^SFqC+Q;b99=A4p-aVU zv_-s0mx;G&YamQl1j^{jz>#!S;8@xgm_b(u@G1osp}cVGRLebbAm&^)p8XhS!>>c) zDf$U6TYz4H)M`=>QkudM5)t|-;R6VN=^*#9HY-909A>5c&pQ;uzdZLyjR6A((UAun zW!xp;=M(#i!;kn9#*SqT`$=ioO+UYtLc!4jt`v5lRD_7=M*O`AVcL!m-7F26Y{SM3 zLC9W}Jt7|;g=hkn@l%4c%Pvs-HNeUItJY9;2s{0zHC!FuO~2nxlWKVKm%FIAI$X1x z-r89mc_BJ@OojY{l+5qV!LB4s3=%vqO3IZ=@T4 zoJ8qu@PhcSj{ZaMz%8XTmHtcbg2N{-A@MHNN9gGYzJu9<7aR+ofhb#0uQfE*aQ$H` z_ES}LNcya-I<%MGYYA6|M_a>{!BL>95bJ5kGp`x6-Zi7%HKQJ8%$9fOND;=-L(o@%B$@4)F_jSVX-={naFXQ!l1boyHL+PQdCV#r zXBL9OW+5O*g|h1KUQu{*WO8IjYh-e8$6?Ic3$q@AS&!ftk7D1)VAkUw(yYZEv%=D> wQhc;TWONQSD+t>hvkF8R3<-#dqTCLoP?7(W(xO6Cf`MO=#AI=Vm{MQ#J`gC0SO5S3 literal 0 HcmV?d00001 diff --git a/bin/SBC/NetworkInterface_info.class b/bin/SBC/NetworkInterface_info.class new file mode 100644 index 0000000000000000000000000000000000000000..6b1c7b32343986e99984dad495125080b76acdb0 GIT binary patch literal 11740 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 literal 0 HcmV?d00001 diff --git a/bin/SBC/RAM_Info.class b/bin/SBC/RAM_Info.class new file mode 100644 index 0000000000000000000000000000000000000000..0a22f38faea4ebad2b26b915a66a1b0be8809730 GIT binary patch literal 6373 zcmb7J349z?8UMee&2Bc6q}e2e9)y;bHrb@z7AlqoYLk?Pl%y?92^4DMWHU*&+1&}d zvpqNjwW5Fm${`>spyF*&n}Ze+ekv*=;(g$SCu+TKMfCs8%+AhkR(_KFvh&{e-v7Jb z_rBRY@xrl30IXAI1W=}+^>=nfcXsp*_M}G40Olyn+o$i>qX|7V8r?p$&xlzHwS)Wi zCoR*`6G(q<%0r59c;a80naivh-1d?73>Q8l2EA%Sfjb z!o7Ks{Z=}j8f{mo?lMyuOHWw?dLnBCQKN9u-aaF_OVrq~C)3)zXSlUxPiDMxxkBY& zjw_YrP_)YN4CU^MU*}u&C4g3%?=GG2^-D7Mzvk8g=+88 zaI(GGOCOiZ#TpjNLRBs^T>7ONmIytZqZjgfrG}-lTyai36}L9>z%*Yr`&70JgisD$ z!(I&8@f{FX4QmLYN=73Z<~gG=4Gqp{pN0j_D52pLXOz-#8kPxSLc{5(kWpGgvoo?Z zw4x$}{WuuF0eT=`9d>3@Ry=8FID{+c=263PDGH6vEk1=^a=UQ3O2gH7HCR9K zyB5txwudR>tr~8{ZH$>7iy0Fv)2o^b4~ri@l1;^|xS5JdS;f%yMGRbl%tfmL41L<7FmAykgUG=M>Ob z%Sh6wn)CF`p`C_4Jm`vOqO9{=(;vUWkVcGkTD&7ypOEb4tUZ7qi<|wl z5T1oDA3xLZbNqrS&~I2hDY<>6a`CX19Xu#kZUKt7XjPv+Zfxz}zKsD>SVNWd)S&~+ zd?RCySO@g95gl5mM@_o{%tT{mBEe3O^&=A{f~x#Rq3xwWLO3Csp1|)l{6TJyP!BJf zxSpVSLHvpNQP@F9Y5t3b&qz>D;BOlKj(^Z!JF={E>;0j1OQkQ^g~ihjoJ$JgU-V!> z0};f(dFA@sh9J)6xgZhKN=Y6Sq^Wyyg6ImOCy1RvTo_aVUI5-j98{Iehuk$A#AQq{ zx;2}i=BrIcthi^%~xRchb%MW|qzO7P~N3 z=NkRhQXhGq-D`3no{96&cBE2luI(r*EGw;QXUFpXF*9v#(~}1CD465OBiU)1R>n%} z6Mcp?W)5dUYLPl6pcX5fmM_Qg)P8f^i1ylMaicz>$1GmRnrc$)XseQWlERbzK)XnX z`T2TkIALU#_nPK-cB0)EefPlmChb9dF_Wys;5&7a+e?fCOcj<{lf7~+KYb(^k zqKM@(bzq)aDnVPWscJk`8&J#UNeR<>QYj^%@Rx6x7D=?+p8&GQ$nqGV>liY~~mh$BCrGoumnKb+yl}U4MKiP=ePqy5AhNW-h zZq24ub01r7o6auMqt}tn{@SLqr}XHJq_bhRvFwHIb#|KG`a-U=6}IW@J3V?6>1>5< z+){U)-Ke*|oO-b*_2w%$XJ_ioTR3Nb>do7z1y44j{FIlZDETWs=SGfV?Gandnfw$0 z*!HgDr{-+H%WxKF3NJ4SUSAAeQ4+kNB)Gj894QGtwoClQZ?nkMv;9K&0Hhj4ot-zcQ;O6A#tI)3U*(>fO1dcLAJVm`k_3OB}1+B&Pjbxtl)e#3gt;?xDmo={ww`aV=+aD7&d` z2JdQ}!h5%^n!*R}LhY(KQ#dk%PfX#VBQ@gta$D1|z57ajBXbqCyqX$bV@r)VTs)~w zc-SuJpR7ON29>J%`=TUMCLOqYg0lr$5$;-W~%?@%AZE99F;IZ+WHlFeZKFxgO zL);C%n&VA)meAL?(X?ceovc!Pm836yXP5qPY&PaTl>DdMm-#lHc=~n zQ$Ua0%1iGyUSzjpGwwhy-}}dKr)}qa+wQ}jorhgJA0=`w%9kvYyYYwkk!$EhWG!y2 zJ&tF0Pva-sR;`@EPggxil9O*Wwjb`J%==NzcXPS%oV=;^WYoGcg4FLh{E`TTU*Xq| z2z4suN-U<2;{-+gwwJl{yVmd@CGFQv;m=d}>&30%f7++On?5hoc5nIuXB%hmAA4`T zDwEZY+-kilmw9)7K8Mr({PwxBygk2MA@hO!JSg+s`FV)bJvn&Z6&p}PGmg>~lf3k& zu!wJ|5zOFhp3!a`!^M2}9;GYxGmWof0^h+Det;=FiN}1QnReY4#1U1c*gVWDlRnXn z$5V`kILl}aH%_UVyHFE}a5$~Pli1!GuFKQvY+83~xH?a(wrL%$;if#T$)@dY4KK{o z7TUA{sVYupt>(D{bEpz=wtD0IYA?a;N?|QboP*189@5wW3%glEhH=1Wj^Sm2!PrY< z_9!_8Ft?$eKl~R&1E*!GQO)O*%S!sIhcoJAwSWYgub55hG`t9O3>Sc2PlLX~m%`LT^W@fn0N~d>s#A`d^^#K$LB3jXGX5#%6 zT9bn*LD6tLxp=8SS_P#osbt1BlXkb67_kcIk>up zOwNKj4Wfb)y;k#xZKsmC?Jw7=>3OQ${#}1F@>ID5xDI1^s$2%@wZmYZYMB>Cox4`R z14~^ODhpgqBn#Yir+T2?-A9EWv@2mIdz0o4OAu|_X6`g=Ik2_1%TC9WTN{Hoj}x@b zVnNWdhg{Pj)(HxUI2g%T>A0EbZDs-4sgykw#Q6eMJ7Ddswe8(a?M*Z)$r^2J>f8{- z2A*(E-$iXGUf16{oJ!kEg4mSPajb^P4Ci!pi-XuqPwqsmtmDjaEvH_Xr{&z@Gvdj( zy-H9}wV;~;x1Mipjw(j^KrJ?T<~S)TMRg*l$|9)%M;>2ifiPkNuiJWsk( zVZJ9_t#Gm@eL&$u7 zKBI7&C*7!Ux+mSF(BMg*S7`L4TNGA$(ya=sJn43Yvpng`3eBE$r^0Ga`l`ZOPui<+ z4qA2Y?@?&;q^~Qi_oOj}PEWd5VWTJAr*MHM-LLRA@cx?I7EfC1Mt1Pp+hF!3+!{{x zn~83dHA$QuEVPH>89_zcF|Nr*mhjn@-P_W!v9~3awz^XZ+pQShF5yNjrZL|9Ai6hxgTF@QlRu3pVj7QiX8Ot6S z?zMJWNt=;%{(ruYUSHRHL7zD)h#fa-M!{4itaQqaIluq2`(X3CG4kfsN!k=2jQ4u4 zk$F`UR?Y3vO|Ul7HjQCCiJt`UV`lisi$&q5_!&DiVI{ZPLvFrX1NgaM`jM8+Bk{z5 zl@8)5K_I@XlJ`nw7zgpRZsKQnsK<(fxju+rD?E$ea1nEO*h&rvYK~=k)Go3M^6L97 zcV8LjPPDh*Df}KU@Dj9B-fjg`s}>wJo74W_7LIMD2hDz~GKfFYjX5xoruZj{nM{0u z;-BfNf5=QGtpw3uwBym?)GjN{-Mxy}0{EL?%6Kw5N0N4Yho$g${Dc0tT6T6sXJBF& z*QSp-aO(fqe@8miZ)Gx^`X=5A;6Hf*WR(hsNgYte09up7BQ|qk znLBuHxgg@H+6jurfRIs4bN4|_^T^;J`)8ojGI^!}A*YDtbQ5NI>aY|@F;7|eYMYS_ z{Rr=DDiH~3BNB@8aU;(|qo-F&O5LMlr~~@OU}4H+B@sPX$QRuXJrGq=E_^+PNr=*R zW>Z|({EUWj;$jTO(R~vXWD@HqheLO;D-`vSp(U3?&O)gAJn{x|G6F zBeelp#MB%k%Sslbmj&6MNM)>`ke+#A#GoG*psw=J0ers4$uF92_VX2qp0U`Am zce#E|&Opg&I;Xlk+gVBGR;F5Hg^~u{s-d(Q&se>Q)YhP!Ay{zi)il}Ae55j-Ve{?e z?aS5;=0@4hynjdZ9bJ}x+c1m$J7`)OPiA}1V>N}k_n1=MceEK!vlGfE^lh%{sf#Dw zx5P1W#kWMDZ)8vx5R1{9eN;Wus4&6uVuSF1g>Th5o*`J8`y_E+q}OL-WIMS#o{95S)|5=9Y*U{CW1N4yOZ}v-A@ZnoOdnS&V)91lR89D0 zP*2#(ODEluFf$n|6A->B<{sz8HtsDT7qG{VW5$#9m&IJ(4w|agkSCE_5e0}QAxKPy z<>N3hd6thguOi#>aTzf=myaXFWL|lg{L9DX)RKkeVRA7aPotiUEDw{H`8Y;B*;yVY zNAvM)>dDmdF!`E~Po$o#&Bt?z$=&iW8Jv$#qMkg?$EOgJ&H1>Bn4HeX1~HkPkE@Bv z?|i(7m@Lo7i;2ng@-P{nkC#$U-j|2T{(QWgdUC)#OeW~#71Wat=3%lzAD=-zxnUkA zL-g^P)RQOrxQUo-(Z?;sEMG#q|CvGjL2jMA2uRU2Oo8xA2%Y?9el!le$t4{aB#i*{In5?Iryym z{G1V?=bPQ<7mNtQ{-P044sJ6d5eHu~B2yjQVMHn%e8q^K>)x8wx9(j6CUSCl1DRw&A625z6=An3fr&;!?+eZaRWzh#^tyjS7R?n9gg15 z(eL0UJc3*C815vwya!L=UOdM*Ucf_m1&`u2Jb^cGkZ1Oc6qDO&q81_zE5mD2PL{-9 zfzRzuN5lTP654AO=$C$DLjy{eb zc}q-sm`jw>QR9Eyd;@b89jEPMdZ?-%LWF9kiZ@IEQpC&7l2 zSjm`_G^m(5fa_vPuZunWD`k2tRyZb8u1Cp!sp#1!)7xVz`jR_RQGEbYs$GaD#$<*g zvk6|#*S$<{uI|K`Dwlcr%2#sAleF^GeC45>azRWze7H9j9+T?oz3U9U^>SIlz1NJ% zQZFB4a{6ru8|BhSsd_IHT1qlEn}<^1p7E8eWXLLhB|DRvCd`*+ERz7>BM!i0XIrFZkJ8CPkQi>Y-VL{!EEs|I*v8u2PTG}}GbQDg#~$_+4>?yJJnoh5b;a0Z72})44+gKx zS#EhO!;7?RWkIdL^NhKbC9)FFGV)f|Z4;iB`IG`!!=GSPl=y)MkIQQAD8w#&P}a)X z2;*YhA?I*sB^bpm++8b_%)tyfk2@@oc9vrsLWgOl0+0Zn3`m839{KOhfYbzpb^Io( yshl-y_Uyy-S>R9NWT~H3OrPuMbCRrQ=0FQ!_Cz`}(#7=g6C%Mz*(5!ug#HTvHDibX literal 0 HcmV?d00001 diff --git a/bin/SBC/Size_Info.class b/bin/SBC/Size_Info.class new file mode 100644 index 0000000000000000000000000000000000000000..5d7c01351a348d22eb442d07655907ef2a147d3f GIT binary patch literal 3003 zcmbtWTTdHD6#mxuf}JHc;Ls$H0wEXUw3s9$X+jdfrg6+AG%=xVE~RT&U}JlY>;*~_ zsZ#Th6tzvI_NCW{zEtWhtyH&BA1d{=PyInv)o*4OhalHTfW*$6b7sEt%{ku;{Ppke ze*ox%>4Qr_Lpq*_rH!}s>6AHF@WHJhbVpm(VmZy6k4BnoE9(o9QYc*uc2Ziw)B+%`jy zX3{nX85hw>VJc!Zzuxl(6J4Y~(mkcXohZ!e0o0*Q#a`_5bH(#2cEKxV9V&wM?4XJ! zdv-|09(#5~g$LdM*F{uxBTC4$&Lg-ZBLX=%W|;bwrF=#&PHLGP58X6Ym=P5y8p3v* zZfn6PiPg{4k%v}gbqq7JYUy0yjtqBC2~sCi_)uSuK2|X<9H&+I5%3OvBu|KXoKxT$ zjeCcB*Bv~qYn+b1*Bz{;D;f937uOw^=$NF}Ep>#rs#q_8?Vfg zm6_NdO6u0cv#n;G+3fJDKM!Jrg2}F!VE{ct4TAEE-V#i z^h<_l@b1bz=n?!C?7OyPT1H-SV7mAX0EO*=taF$n6p;2q8`ii zX|edwq4YwbXkF3rI#uAWKvEGY)6|QJoK`C7B_Hl6=-d**hn#}v{%4thU(bb3N~w;0 z;H?(A=FBC8CP(UvW0SZST%}5118v@piXEcOJ5sTmY4N61>=v$8#2w>zKM^cu6PL0l z`WsrKVKkI+fJ3N^b`H&DbaH4Zqbuq!<8U(|I zMF(ex(To=n#tU4h+F5A#&=L%y4uiX7AWnR2O9t(NL3@?-`>UjHW%@QIIUv)wu#8OM zq!+W8EQ%n~>kxUzArjk?$k9hNYSCVWOb3pzvpuYqsD(`?Rv{B3nR=2?#QSBFVM{!+ zD)-yjlTW@tI2vqU%a(CG+H|svQ{N!RK!}08G7!d@f()LO!3hSNHw63b-~|~x!C=dV z;D8+*lEFO=T*7v~Os8me9V-UJ5#m>n?d9w^4|0OXI?03d@p}pvaT+5ygIDkpZsIKF zaSnO(V-e?Z4;Sz*1~wGnex(5Sk(AxIOmrVS``X&t8d--_r8N^yHJYPGdD85os^#A; z3znn^FH?M}4Vi=th3r5`34WE}*9e~80(`VO_&`nYn*_f_@Y`E}k5vax z)CAWFK1cBREx^Zd#a1BO?Ba|%6nFjyPFzw0;wb@431eSsk5gR*6YRYGtbELVihl-T zL9u%+EA*Lab$`u28;>JlTy1^u1nKODr`^Fl?gxz8j>2PZv8C1`x3BQscnghKW#jJh zO}Qu2MtQ@84A+|)!|S-g#45UEdtF=^qyTSlosbkM|AS`n&fTg(uzx!QA2R8CB)CR` z?=$TOJ0y6!2El>t5d4$`KO@15+_u^ocnlHhkF_&o{!NP>@dNN^f& zIvU(4;NwDB8wLDCrr9iD4VlVj@gCdiKlRh5&3{(eT!$ICY8E=jUEDm!-(-n5Zh;Q* M52}GI@<;st0d1Jaz5oCK literal 0 HcmV?d00001 diff --git a/bin/SBC/Storage_Info.class b/bin/SBC/Storage_Info.class new file mode 100644 index 0000000000000000000000000000000000000000..7080325f57e65c1c85d8c8af00b98a6ea9f713cc GIT binary patch literal 3122 zcmb7G>r)d~6#p%W*$`HQpjhe)#bO9b@KvpdN&pc7MT1!FgIVALE6FBJHop3{58K*W zt!=f|TK%NoYG(wUI@9Ure(GP;_IK}wuxL6RVc2`mxo6Kk=XcKeEr0&~+wTCj;jWG{ z4ONMFXDs2Qv*x52>9r@)I{eh0GEbYaq-jsa4v(G^V~z&@py^C$2=~t)C7i5fPqt}T zG?aGCqz0{vcKfqJa5o-PRy-W=&S z?Q>_QL{{X|6V4ekD`KPD%vf4gCl?z_CzC9bMG#{HW~QxaIEY0dv}3OhLa_09UN*1~ z9U4MC!s!wdWqA>M0z{~fux*@l!j&ALKhs$W=d-)tB*^|w+v*#J`I?e zjIcS#n@YX%Ojw2c+oX%UfzvpnA?T#tD>N*Pl*l9qS(N&|co1*P%>Xy=%1sY9@5{{) zsiV3GOTs!YB!_W`Br~)k<3B8kY3Ux0i5VhAPS=@uFNqiBw*cYble^i*lV}b$$w{ zSw#=uGYV2Y_C+6Ib#y7JW>C+iW(MoH)Xrc%m%14=M#GUAM5C26*eD+@^3f_ETcW-h zZ2MJ3^D>`XNdI7Ter)E)VGHlyiaLII>lte!vuviO6*bt7rP#$-_o5a%_=e%eft`4T zlE(hr<;JK*h)@}IcmFrW=5Sj$GK<}RATEP#4tMz;qgyHZW-%aRZw?=MO4kyNKIFke zv`orun!`t*GHB&lbQX!5SSc+luir*R)X${vp-Lrx55Xb`K1dMaOcn(GSrF~Q|f1yyOq6?^lMi1=eMc7{}>7b>QgJ?+yua|VNrIdq~5)R5F zOBYfYWD0&pUAbW?My43Jg8@I6H3B-x$hDVru4C@i5!}csPvv3~pyoTp%+;f@8yv^Bpn_90;jmPHv zyiJ|grq*NgPQhmGjyliTdxbM1;dWOehZyK!ZgKWZCnD(LZ#Vk+-^CPqkih}+@Ii%% z+d=n>nA|U5B0DGme|S|$byPu%_#3Ov(-#m*iIHK-2eRZ7UsYY)WS7sr_BT{TBibpB6KE^lda6XLH ztQ(kT5Ee;e8q1L+v7|^SHnGTf!s{+%9prd?r{=~-!jtJ-=ahl;q~Qnt9naw=exyQv MoNnPZ?lhMF3qjt5cK`qY literal 0 HcmV?d00001 diff --git a/bin/SBC/WifiInfo.class b/bin/SBC/WifiInfo.class new file mode 100644 index 0000000000000000000000000000000000000000..f3542869f9c0445a3b82b388ce905379be00d5bd GIT binary patch literal 557 zcmah`$w~u35Ph9(%wpV^xFU+E2j`NTA|xY1AVzUQJW3~aOv|JjdU_K5EKh<5KfsR? zYhqk3;-ULhRlj<*^xON(D}XXK9V8eEe${iYWmnczS35{COyBbXcLT2O+>7?T=vap2 zdBtOxt^d6E)<|`C#E|m++9`wM^|(?ZAglRssfWTaWR9hh_Jq>+_FKgC^sUGtiDCgc z?Zwu4p?Lfp8bcY8mZe;9_gDKmSAw#kLyHl#1^L1o8;!MVwlV2ko#yv8W zt|Db+U$kT>Nvfihw%kgsNNKyy)#I=yj0knt4!IF-yUg8cWykMnV;j6LNIpH_K}5>g zSotR24lS!jGa4`8VJJces|;KJN;z0(So(uT^4~SF1zHt-O^Ps$3|X|zF;0vzJ4PBG zd3rAp%Ozxl>4RrXK9P~Y6y2FoFoS8b3!encV3xcj=0>b}ED**ri*%MkETcq$962jk I!^Up*1E^Dx@5(ZR%$AKeBy$g@C*b})~szHGQ50i#B-h0)L!1pW^C7h zF(liZ1hHE`@b{NH2XQCQwLRfDAWiYV8LNHWM(KZvSHQHe!g69mZ8g^3lvFz-G z?Vh$K#PG;)I$4AV<(ruz`vQMaGdtWxQAh$)qkck)3D`?onVbQYMCp=-h2|MLk zQ*;vr4U`@nV@~p;w+Ko87BQj!#F_Ck8FiwnD#C)2nmJ~B|I7CLpyC+(zXJ-HLsD?h7UJCjW^YmET| zAup|CVW7IDwJ?%FoHwv`S&`n{mvxepwi^P2Rw5;~KMWfeB%5aptXUx=BoA!Nnn)qd zh+A`Wwpi1#($)eM{^RzBAXV7jAcd=*m=Qi&vY}Xk?FMS9b*(&jU|_?WR}E1pO}vrOr>nMKWSw$b|#2V4K)5QQ4pUq z>i@EhKFU22jm-OEX0XYbtxMGb8<(2dw$#j~rDnD)HM3!Kv*Z}%>T5ogO%r{wM_3z+)I3COtn4A`WT=;+QHCZNHp{S8hE^HcWay9~ z9xHx`?LVkz?BuwIZ4;)1Ls@`?4RK?6}Eny?8i96QjA9oUR+Y+(c2ie6sr z;z?0RMR*=Ba2B=QTCH)8oTA!MYTHC?gNTg=L~XQGCkY^~cjRrw_mm(>f`~$r2Gw*c7zpEUiZO#Qg4i9%eR#`2 zKV|FOh;tTECG^hrZwQK%C$!Q7d7&!tEit6o`-i)zT@HtNxQV~b*0;|v4CXO}a}0-a z3_U(WHBUG4x7Fg%DnnZV*U3Dt>KxZgIj(#)9b||XFpT9fEUV!w%eSZBS5w2&b^O(f zkVYANUC)|l;d;A)(n`3a*Gb6T0BYp|2~4_sj$US-8xV})F2 ze&1j-yUBL*5nIY_*5D_Ud53>|?kc&5Sy7ban=R-yR;59Xqj;SaQADO8yg?M;`F4yG zm2z&vIie6bYGGl5wI-Friun_@0ZDDXgnYYy^Lyy+Uto_#rXI09sdkc#%q<~FT(<-_ zlGV6g@Vv&2f)_My68x6Nn+3n4@m9g_Y1}IK1C8lUH*!_u4#C$ojuS88=Jyn<%QZs7 xfctdF7s%pE+#+7?vbTN56yrm6Vv%kH-E7d620qTvn|F9Y{=wbFXSlbe>>t1&Jazy8 literal 0 HcmV?d00001 diff --git a/bin/androgpio/Closer.class b/bin/androgpio/Closer.class new file mode 100644 index 0000000000000000000000000000000000000000..2c097eaf301a97db048549b1e53235137b1b02e1 GIT binary patch literal 1225 zcmZuwOHUI~6#i}>PALTnw7k)xC@lp>5CvO6c^OTLCX|riV{u!qWuVM7)2YBt6B9z> z#wF}rv1U_L64Z?gf0XsyDcB08$-TYbIp;g)eCN!s-(ODwjACAagTc@BRny4iG((-u z8hK$Va1pxB-*7d{^^E!~wJy>Y!<7k5*Q_Z9XE?IV;F>j7g%?iL_~6DBhK7Wui>HOn zlrWcgDk~V85=NS5m$|9Q_ZjH2*0j8|UCbM2s7uQVl3G^iR(6M>CHx|Bv5(glOAn8w4;N1hdSW;&eobRMc#OAZE;hmsZp-Z zO#5&{R_ja{83i{P8tqQc=Od-8MKAh%2us=Bwq^?YX#%#RE-W=^q&I}+MFf2c2G7Nk zmN{*3fXM2kb)70j*7&K1>Mp%#r$?!gf>=ZgR?v!w{Q*Za6YT&1 literal 0 HcmV?d00001 diff --git a/bin/androgpio/DigitalInput/DigitalInput.class b/bin/androgpio/DigitalInput/DigitalInput.class new file mode 100644 index 0000000000000000000000000000000000000000..1d8f7c31da224394f48ffdc7587511048b60f0f2 GIT binary patch literal 4552 zcma)9340UQ6+I)#mMjkejDZjX5!g^$Hn>R%A!aL!0a0U9%ZtfXuhWBz8Pd0?3B-+=FPn2zH`q#_rB-1 z{(J2PfNl6^7y%9ShMg|tGI=Z4deF*Pu8~dH`Jy}TBaD!SWkbe@(V8{vOlw#FkU8LL z2=*Ie8rF0e_W0;&vtT;8L3h+Bn63TWjMn}8c50|FM@*YDYdYpuC*6W&XE<1yw`_ad z(4ZwWM25$6zS;80-d3Ev%r?z*$}ALeg_Lhp>(da;8jhP9F|tKdL-jzhP~d(Er>m1+ zmTkF~k+sg6X$@5Zz+V_A;{WS(yC4^oyTr~OdfV94qak!4mo}pa;$9uKSQ){68c+od zYjuPXk+=0aJEHOy)3F5i$lFF8OTE4Z9m~AF%{rFj9t}%7EZgiX4)>ddZlga-M`}89 z14g#TC|L6CcZS^4mZM?Q-GC?VP-8;y)}1KYt~G2X>=DbcXl)7MtiD}> z`-@gKZ5G7LejNvJkPDQOx&}3DZY;y$BWo(_@}Whs7l%X%hc&D$U*eB9^+Z5*S9;2o z*&oo+fn)44@_EyyGPf+W?2es$unfswbm=&b4>D)moVSgJ+QtRJCGlt&-SmBt6xGp# zlZ>rX>~9~l9G8+<&mahhve}w9R&ehX)3B@8&^jWBLsB#c%m!SK98Qa9RtP$naOP@~oCqgOBh9Mia(~wL zgaqvCI=+E#vYM%Y9X8z_?_`mPCaUo=z8%Iz4UKn;LdPrk4(YYcQOkC`y;R^?7G7nY zFZUrPd+v_ll7>~W61JE+@>DR}BKQGex5N$@MJ7a>MD|BICh=pI0{W-And|dPLHFrG zZd6VYRXJW08NGg6%~`a%-O?4s>YuP-MWdXq;S*-oWL^quQ#z(43p~~ywY@xuV3x^G zPt?3}KccvXpM`Oqi7}_EgzcIcvmk}|O)@ZF=l&plNaE)@et}SOBj@R!Z8RayFWI-5`?c`B zl3!cc3Wkg93$T6YE-=LF1l$a^UO*%s@<9b5pyJBy_`85=A*e09qiYR7@Dx7@%G^YS&Kg)J4ipQU|?6*;f|()jlF2HH5&@ z3YzA{*Wc*hv?F*2jn8?6&f+Oz3=z#n@^lUjRGhTl_j&*Oyzj$i<`$6RsKNLSP`_!e8|17Z zt~x`pWO*;Nl}{orqV;`R%A)!lo~JMC$-oQDvH)jq#24`;zKO@={cSXakvS6PsiNV_ z_zFjE*Z&^&Nqj0rQ7_JASyc6okmJ!Z!B&=HT*(-(pj8cDEiu}vzJUr-vq=IKP`M%L z2AnMmOoeYo!`Eo{HOp!AeH}K=d zn`n~03Dn9Lf9rx*@vR^(sSv7;km{(L?1-!2eYY%l-(%Dma0y<`_r3QYPU9!c42mr< zsSx#2ROg9I+H-#3_A@Df$0_M2Nc5BZukR_ApQjm&XRw*GoAE3&`Z*@g^GuQ#aFl_0 zj8yf}<5Tqc93~VWF(twCJ|7W=_p+)3r%0-V(yIuR`rD}0s+yO)BNVeR$U}nq#)9P6 z-%kFk3zGj+F1}L{FZqFJ6E0_QWg*&T-is0<_)|WTR z)YazGP&Lna%10?UApa=PWvPR&kR(-Ai1Gdl5UAVv+o1&)E_1<(HxcX&dLCU(_J*31 z)3|Yx-_(z!y3$GWRs1SQRP!s$3_X}-)w_mvTxZR@QHHp?ln>pwNw2(cQOJ908Q9;^ mQUJflAK1#t_ZEMb9sCi0Vn+>!{!DX!-CTvgvHi#9s{aAS-4!1I literal 0 HcmV?d00001 diff --git a/bin/androgpio/DigitalInput/DigitalInputEvent.class b/bin/androgpio/DigitalInput/DigitalInputEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..f1533a9a4217e660204757f413b05ae77260e8d7 GIT binary patch literal 208 zcmY+8F%E)25Jmr>h+<>K8L*MY&Q?uGOe{z&>{i@tmXKMpE_gQ!58$DU3yoU-_b2oI z>-~5FSYsX`Af$q?)~H6CY^#-aqRzQ#-Cx*sl3j!`VNr=rWVK+G9WRwE9U+v~8cRsh z{P1lHXEm$M@BR@y7n;nH4m~?|7k)XjQmz0@a_;3^cx~f KJt53`j`#!I$2If- literal 0 HcmV?d00001 diff --git a/bin/androgpio/DigitalOutput/DigitalOutput.class b/bin/androgpio/DigitalOutput/DigitalOutput.class new file mode 100644 index 0000000000000000000000000000000000000000..6a09e3937aa4a2ac8ce2dc4fe70feae829a80e5c GIT binary patch literal 4448 zcmbtY`F9i775+w&J+eG(4iJc0#1dl52A2X!YJ*b)CMYqOTE-Noi95E&@(^jH%#6TJ zo2_lqHiYg?g3~2in=bB>HrP&Rx|g=;&;3{WOMBXW_st+nAfH1|566Bp@6EgSyWjop zeb0RG@ArNUU_bt*BckC>)5*HUT*)qW9kp||Z{`QfeyQx&zUhc*SbfesZ+7KPC)YJN ze$JZkHAKhFX$>0(OlRi8l;v7paniqFx>nctezWUvUyp_c>%8Ug%a(zq-5KAtog5b% zOSa?8m>RUShInD77@DpAIB>;}S38!K9kX1w=#GU(wNVYtwqyIYnYSOevKpGiOSsxe zSho;czW@|42gOn^gKBFZ(GWXQ%vuRVvED!nZi{1s1~fs#9R_s7DuxhPR9Hx+tYB@tw9w&+tQ-~uXU*G z_;$fcJLhfBrolePDf*^w7aiK$vRp=X9=EerPl7<(4W!UP#xj;)E{&bC9md=?+D=Ln zOv{*~uxl_vemxpz6e}{mIq~QTvlIr9NLT3B(ATz{-=3=UhZ(J!2?ddE9}x;aY@h*+ z^0ePTGmHf8#=Sc3sZn6C#lT0w~xfpPbCE^m9OtNa2ynOqgg6h z4h6cq-nN@OgPA5cM5bBSjnr%2a=AlQlw%esfF`NgP#f_<1|`x-U=*Lw@erX+6boHm+37mxm|dsx zraxJ93kDvRG&R{?pH!rsjbn@w&dhkeRY=-ilG06vOUZH5v$CR4)4;ee-RzpSXN~ns zjNxuRkj+sL~^%W<$=H^4Xnl*$|-00X>UouSsgYVDCMvp9<%do0FR=eBd=l2 zlAFTJ1bc&Lz(G;NZPGl)vbM{P$m$y_nf{azo35LqDI88<3a$Z9C~34*BE(JEzLdTb`+hzvRgOvqJa*~C*GANZW zJM4#srHJCuQ_hnvjS5{?RZf-t{8T43$L-vdu=%2amxRp~*V!DARuIQ!daor-J~$M| zE0lIJG>}w>km95}j&D=goyj9+nX>H`g1&3uRlLT6B@b$v6zTc2+ zym>=sShfdUPYfH(a3~%fBIR&?ruC(J@Ig8r855Lf`zE(crB4ZlFI|i;`Uc+Wpx~f!mdS27R^1J6J{Q87bX7vbPCNQg)m$C2!l`B~;yoSjYDg`WZ%uw{?>P#ofvfnH z++2c&!!WCSRXRNdUGQ>Ld!|@+CoE|eJmYI@?-AyBJqnNNkva$Uj^z%_otY`t|4Fu7 zDz`x$R7_rtd5S;nIF@@PZ+f2P>A1#{UZ(Y{bD!Nrj(;70)v#^34=a8t>&i2>u^&fy zZRDL%4re|^_AE(*oKqBc6(d7^3t7`jJ@M>tIM>3p)6(Q(;L4K0++?#(#2_ z$bQcvsolcNI)Z6UbzZ@~)H~RF z4F@>vyM{d+HqPU~EOw@3;VOF0%G4qbEugRE=sb?iu{ZGiSq zn$no^jxfYYel8m}EaL2CtVp#g%9~?zsg_3;@X6>L*8jN7VNE;NvnsORLWHlXQ0$F}_qZE2W|BXViwIsBJviLB4K2qhOvEreTfvurc4LMR3 zq^nE40}Uj8s{oHEy@>JvQ<(|$*$|qE$hAA%{11j1<99<>F(sL6UBtNslumTc&9i8T&dFPB5z})gCC@FNW{PEM9#6JB!%Xj<$7h3tFX9WNFvdFBPD(DaDnxr1 z!}m+!`=yZLUHlscu(+Bdr8d=kj{EZbs>EH7kxmz<;HyXi6%17f844+1MU{LRUt#Q9 zn5wTbRZ;HSj<4bC>Pc!e@-OVvF?C$$38~>56#`!(QV~N>7~ePN22v`(m#YGtWn>*e zfGyt&p1(7X*QxG6FJd6hKrV&j#W1)QMeh?_d6I&BihnIUtpW+OBT|=^k-%OBHP8-I zmkvOFuRh4=BI<(t{{IE?d4jx1C@&D?rT+os4+!#RiN6@K+8t=SGSN?8NqAl#T)3yo9t?DRUsa(=y@3L xGpb4p>0L`3@}DIU{06_}C}-I3`15-Yf50C((ZZ!a@xJrtT}}84#}9Tj{TBgb{(t}g literal 0 HcmV?d00001 diff --git a/bin/androgpio/DigitalOutput/DigitalOutputEvent.class b/bin/androgpio/DigitalOutput/DigitalOutputEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..bac0ba0dff29120ace2bce2547f6821587272072 GIT binary patch literal 212 zcmY+8u?~VT6h&`QL}78#UqA<0++5X!#Dsyw!L4FLN=QrUEB>2ZlI=CFjf4YtTHDO>FxxGN(>>(!b NDih-E?R4N>wJXRbX4zS#`3-itaes ziYz$-se)}RlwDIgP6FEmw%D?wuMP?H5iRxV{574D%SKTW1s`#^);Ut^3{!AZx+|7_ znWhp*EEyHc^}0#D=o*&k_jy&Pd6@#)&^cQb^#DBi{f23{4+tFY|39$_C>dcL#*%P)g`yVn&yz*GiD#;Y0=+&?b9}$T556mPXuwO$GDem=Y z=s<#d{TjCUy^Mxiaa$aB(ZCzgIDzgI29f1S?iRSOzx^QGh{vaPSVK3`3Ea$_?$K~F z8{65qSHpHTde|7%(8IbRjBD7*#w~1|(r^pXbguG-DbH3H3(`KPla72^ zt)gC;(QSk6z!`JP1__(F;jJQ(Zwd}}YJxV}&@0B{6#WqIw))bvOW`m45e+wCOA?d# zSR9WE>lA|en9OvVp)6;aJ{rjll6SyGI;aYiXMcO&t zA*y}Bb~VQsXeY^PUepjrB85+3E{;zNbT>}N;%br3nxo+}pp>C4lMbah$IAi&J&F@C zFdIjaR@s^}PF9UdN!q*$sbL-q0&$8~m6P)gu9ZfT8dY#jp#XzV*091VC7o*F^katO z(vkBMsp%!s&S_UKUVcPh3QlFRtUFUd)}tFd%a`r*8lJ=xM7b8Av#6W4)Jy(A9`Yp( zU*;ik`Z7gdC>c|4#Wq~o81$5er+H9XvNxj0;iITiM^Wb9Fc;YyQRMJZJdzlm_0xQw z(|mUzRI@K#6QgY#hCt0ot-^0ubQ zmu&9ay5UIwz)s>98h(ji3GDW~UadJ}TZ^ZR`FSeSrhBf;;RWWJ&#_x_X{%}%2(IJ?Q(Le?J73;dU? z2=!mdT*KZ>bPfA5>CQFWnMrr8;Xp>LVIVWeKi4sMA>ElinC`lULu(kSIgSL5;hN)U z;25bn?h72pYL5E@$MKruMBo^!IZg(S+#iTq1gGiu7`D>yUc}HvkC|=~C{0g--L$-3 zTHZd~jr|zMom4IRP{aYa=*LqSz{`{x&k%Blzf3%chsdG@V}h{sK*dM#F#X;^zSn7c zc%#O6$@$D(S8;Y7=hiTrc^ml;Fi6epJNV=k^kxRHqj15uOV?0NUtY)JIzGFODoMz! zqaN$qyzM?hafgZbJ@iyLN_5YHDGc8x#S2ROx5MylQoO3fe@7|Bh)Rk#Hj(1HN-D<` zQoO08@^ToyO^T~Z{3~JjHYu(v@vkbSIHr=~FPlhlMM>qjLW;jBsk|13Z6#v{ritj6_j47n}my*g4!tiZU{9B3tVHmzu3K3P}f25RROd*BP zHj&~DC6!#e6rx8-<;P+8Rw=|DCH^O2_%> ziA=6u&T{yj_sZiEA46oQi!i^ZKF7IAZ3hwjBmKSYU=8c!NYkfl^dEzF`;vH%+CTLr F{|7@C_}u^i literal 0 HcmV?d00001 diff --git a/bin/androgpio/DigitalOutput/GtcAndroidOutputEvent.class b/bin/androgpio/DigitalOutput/GtcAndroidOutputEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..c04d4ec2e47c7a8f9c070ab6114097b6495807e8 GIT binary patch literal 355 zcmZvY&1wQM6opUx8~?QnH+=%T(4p$im0;V=P)i58aF@=65E~L1?kK*Ri#|Xfsx(sr zgWx6~=iE!~J@@DL{0(4%y8so22{&nGB4L2$ XKU48tnojOr$S_(hpeO%0duqJSoaKP=Y|cg-4;n@aAS zRg^k=B$+R3P$lyfqBte-QVt`5QK@oFAl4&gSNDX55c&j$@+4ks9qt;|roLNs3S2Mi z)h*pJ<=#8=+XrS-;CjBB$oUTM0LBNM{AbmCkHOPppU z;EIO#@V-EwbfiNgFzM<4*QdR;2bwOKU>*Y`E`PSFH@ueg$)r3ntXNJeSl951)Dpv8 z4fjwG2uV&EDKK%0vPV0@xQ}fWH2j6`E6devqheSZ9-v6ThHfot~!jSgByvsERM$O(iC^_EpOq+ZfL|3|nX!7$GJ6#h&UMC6m> zDk2|%Gt(MBr{McjpX*1$>R@Bv9aF^^0_&8yLGRsVQ~{&dWbA!LZZUS7(GM_#Pn;CSTzW5sIEL{N n{l>>fuYSxJpOZ`i%3ruf_2Q}YCG+804y8ZICh(A>51;-6jg@uS literal 0 HcmV?d00001 diff --git a/bin/androgpio/I2C/I2CException.class b/bin/androgpio/I2C/I2CException.class new file mode 100644 index 0000000000000000000000000000000000000000..99d41573ec0992f007b8dfe05aab08142b605187 GIT binary patch literal 1739 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 pA~stq8`6)r| literal 0 HcmV?d00001 diff --git a/bin/androgpio/I2C/I2C_BUS_Event.class b/bin/androgpio/I2C/I2C_BUS_Event.class new file mode 100644 index 0000000000000000000000000000000000000000..8f86e1c99f1987f28daa28e3634936fc7552b18a GIT binary patch literal 159 zcmX^0Z`VEs1_l!bPId++Mh5A`yp*E+^n%QMeNQ825RP{W4UTs$OU)}`XJBDu5Xed_ zOVrOv%uCnzPs&P7E@5O~_Q_9YWDwQxK}ZFc6lLb6TWf|fGH?awmlh?bx@G31GBOCE h7^nx*z{bb`Gy>=Z1_nl;`K%0VAQl5VkYr-u007KsCHDXT literal 0 HcmV?d00001 diff --git a/bin/androgpio/I2C/I2C_Device.class b/bin/androgpio/I2C/I2C_Device.class new file mode 100644 index 0000000000000000000000000000000000000000..fc61a1fc255846d33c3796b9c172bd75da498e00 GIT binary patch literal 5168 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 literal 0 HcmV?d00001 diff --git a/bin/androgpio/I2C/I2C_Device_Event.class b/bin/androgpio/I2C/I2C_Device_Event.class new file mode 100644 index 0000000000000000000000000000000000000000..fc8c7b553e9c8e2467a41edb928cc6d34a75b637 GIT binary patch literal 165 zcmX^0Z`VEs1_l!bPId++Mh3aWyp*E+^n%QMeNQ825RP|AEz3+!jdv|e%`0JNU}0ns z$Vx0r)Xz!GOV{^L%1TWxVPs(T$xmlw5Y_NONClS^W#*+@Ylbm0a0Ta=7A2>;W#*&; j6``A{2hzjF$N)42=nw`5MxYg}3~V4413QppV&DJ(LI^0y literal 0 HcmV?d00001 diff --git a/bin/androgpio/I2C/libc$Linux_C_lib$timeval.class b/bin/androgpio/I2C/libc$Linux_C_lib$timeval.class new file mode 100644 index 0000000000000000000000000000000000000000..bbb4fe2a0fa73572bbb7443182c70d381301af83 GIT binary patch literal 1242 zcmb7DYfn=_5Ix)1ZA*Dr&A*RrJSHTO{@YNGM+ zd-Q|BX!Hm8ql~j#&?X?!Pcyr7?wL7f_WtXY^%6MhpaDmMUyuyL>PLj&aUkR%HC1jF8gj!^#fNh#Qf%#C#nnsrFO(B_hnO* z995rXum}?vP-k8Ei&8XdYi>=r4C#EKw8NX+4t&|LOVaZRo+!(@;w}2zF~D5wg9cZ1 zJ%^+Y^Yl@q=+uOTE`}aM*jA4t8i$oc8s{vWJp!1H3$Dxey%c)UM^d<_Ctw)MKW=NX zR@o6%fA)kyyU8rE8thht`;b=}2)ltrt~CnFDT{sWU`}(`XW*9?`;cLc+L}lkGsOI%y9d`LUu{k1QEtH z;kw{8QaLA;^ye9(`IVKzMiOCMPGJN?7RDI*PF`XXSzOVmJUN}G&k|r5Pa%q!&aS3l zAx7M^WJggGwnAIiD>tWP*$LdLSd`kLgnnj|dPfYYWu=5$Y;eyLo`o4wa=J$rZjs5y zF$^7T4_Cjqqo;e1Vdyk-EX*0)(gu{Qv*} literal 0 HcmV?d00001 diff --git a/bin/androgpio/I2C/libc$Linux_C_lib.class b/bin/androgpio/I2C/libc$Linux_C_lib.class new file mode 100644 index 0000000000000000000000000000000000000000..dbbd50cf194a81cfd87c77962b885df0e64b5d19 GIT binary patch literal 716 zcmaJYx16dadi-LwVT^n>q0A(Rb=u2gaBjYvpaMdA>NOAeLexWTDoue_TAf31i9 z0DctW*}GLiMQF7ZSA;yx#~)*-<&~H=g(xQ}l_3)h$21e4;r}n8SLubi6y~IM;0)>El=SjM`6K88i^yDDghGO+BWhULkYGuM=U_v>zB zmyF$Yse}`M&z@P;r8e6?J{Jff8*Q#X>R6o6R#44Ub1ry~nW? zE8<$OZWl31bQ`mzLveLH>jm5uZV6+UFfh(A!TsZ-eW64Y?8Qf139Gftt+mxMCNV`c zn?ajltoL1iTQdyPl*DGY(-P`4Z+U{KHv@-z+g!=S&YXo6ExLq$%#?8hMG_wG%ZQ=Y zynJNmM_*?sbVSDqk7*L-j=i(BQQbIww5v9?4P{6y@)S%=BX(THrB4A2P}_&jr|$2$Z*fXH8FMOtVXCLc`EQ5YplzZ% zPe0x$iZJLVo@6~mADzHZg;h!?C|4>cxb}@4eVC^63Y7sHRPX_1G;v)=K*h94SqqDh zkw3ri9XHQP3)(rUOUh{iZef;c8S%N3;a!b$xTkR*_cfZRYFxE+Fz^S9 ChY6Vg literal 0 HcmV?d00001 diff --git a/bin/androgpio/I2C/libc.class b/bin/androgpio/I2C/libc.class new file mode 100644 index 0000000000000000000000000000000000000000..cb8f663db93a29fff1983d3b1302df9aac03cf6a GIT binary patch literal 908 zcmb7?-EPxB5QWbqO&mK8DWMbsB{Wc=EhPrT9TgIF9m!IDQ2r2C)+V)rD`Q7?f_N>& zMJ14U03Hf4yFf7lA>j*qzu7b6Gdug`>&MRkp5T#&9HAznlO&!mg4l8&+g2D%XByTB z>!;$autE{dt^V}XpDhV%ylE3!oj+4{IzhC0=h+@hyyGBYeOD|NK{S6#D05+N;EV>Y zGbE5psMv9oE=9DQh;ZfS0gZmhMsC;Xk4G}Z=5(1k>^z@1GLR=2GB9#_N3N2vPbF!O zd-hOD3WVx;j@RxFY?WCgl$43%mjfx&2%6*aPU9<8)%?u;uf|D#Y0t52VuuV0c@+v2j}-zZ>D}S64TJc+pUb6#U=Qv(RIm0G%CBqk(s~KKrzL?<}^Q8=LFkjB_ zCi9gHUuCXm_!@gC;X1b1(G7NFG0Ug?35{20*#3aq?-_Hr!*4@vR`|P#yDAD!>rwZ(=_kA4jx5nik@V-^l`A!fXLjMLj%cNic literal 0 HcmV?d00001 diff --git a/bin/androgpio/ModbusWrapper/jModbusCoil.class b/bin/androgpio/ModbusWrapper/jModbusCoil.class new file mode 100644 index 0000000000000000000000000000000000000000..f3b60fded8add5cfa2150a95178a4d3e2b117253 GIT binary patch literal 655 zcma)(J5R$f6orpV+t9Q;%KJH>0s;oYfLK6CPzkA0VE|NSr!~}7Vpp~Uf`7$?#J~^W zMepG7!&}uno-q74aFl8F=?ySWt|}^2RcL~I7{Ms*DZ|eghvZDX#jV932$STT8W3ha mTp5A<7$bgqt}0FEFo8+h%cM-vGi@=0SrXEiIV@mlz4Q)r7nVW* literal 0 HcmV?d00001 diff --git a/bin/androgpio/ModbusWrapper/jModbusHoldingRegister.class b/bin/androgpio/ModbusWrapper/jModbusHoldingRegister.class new file mode 100644 index 0000000000000000000000000000000000000000..2ad978421629e1aabcbd9fc170d466e9da168854 GIT binary patch literal 710 zcmb7?&rTaL5XL`;Bukc12!BFp=m8ZW;J|V~y?~G?Qq@Q)2dL7sH|r)7?b?y;1<|+a z38@DjfQLfNh82e@Rn^1VGqd0P=CkF;r{50%j`2P~Mc`2C#G2uRO!UbL(eDK6r=-cB3C!*7 z_XVmKCQ%{g&}gEDkPCa=qQ2B4tuFH{R`#2WQzfv{GXt6SrKKV-i&Z}&h933)?;IyQ z$I(xwimpI-ZE`zMpQ#wrnSJuZvAmH2n^(E^l&L;BVi)IH8!tT>%_csuiM!j8vdWns z{#IHQ#m6%GbiQ*vGS*+pO!3^>jZAZ%)1G<3jA?eYR`w#5&M6mQQ(*g_+yEVcjlakQ z8m|Vh4ZeAP06>MK8tS|ToPC_;!2Ch0ihIoenkp7}4fzkS%&Hce95uOvby&n3-U~)p zGR|08XzK@^8H6=fu1^W=7gb4MJ(ieX_-b)?1uIzPeSs@${H;5*vB8C6%v-#})^6h& D@K&t~ literal 0 HcmV?d00001 diff --git a/bin/androgpio/ModbusWrapper/jModbusMaster$1.class b/bin/androgpio/ModbusWrapper/jModbusMaster$1.class new file mode 100644 index 0000000000000000000000000000000000000000..0eb08a58979ad28bb4e82000d78a04ce4450cce0 GIT binary patch literal 1548 zcmb7EYflqF6g|@xTGkaRBKWpwE89X@9->G=v^)}%qKO!c;nQ?GmT9)TWOqyApE5D> z5)yxaKgxJ#si^|NrO9+2*?Z1Ccg~%kzm9$Yc!Jd=VhkhPsd;XFTe{|&TdTJG4<6s% z7M|HS*{*Rv5MFjNi3CHs!FRZ6bEj^;tu}-eB+<<zG9z%DqCH?FKrBR8LM~On_r6YqE3njy@edNHfF@^+8`nI^s>MSry)UUbO{7f5o-9y}>=H_U*%jN|+%ZjXFC?3HN?d z!q-W6D4t=)s9d&UrTj(`HyMUH7qA|9(y5nQ(yj?Fh1(4JG7or05oP8w8isIJDZIyU z!>F8&BR~xgFq}k&VdAps=y-@n45_*Z%AW&41xXnB&9a6ZMv{2Ukc)JiIt=6qhFg~K zLmMnaz0=Kd{;VdYc~r+3iewYzVyDJXJWu?Y*mfOD3c}Pefl10Ma8HWNFkqZZm&3G9 zoix+YKw6pdy4(1fYSX4}^3z@k~s5WDmy1dcI_n}|k2}{TwQ42kr z0`*%uwAY{W&|*2f)bZ-?T&Eedb+_eNVpWDs^v^Rgu53uXw&GZ}>yv%gM6l)3CF!pn zdJmRt?)$<=hWa#4qYEsZR_#jfRh&i-G&;Q!zFslJXx0j&d(aC9=>1B&7_QNOH+=*4 z1^u+v)taKNV}NGr1PrzjBoZM)v9JsM7(*ZXZyn%{+Klew{vopaDC}eW5L2DAryY`B zl@R-eVYZ7gwil8Oo&dJVfFkBlB093f1+)wQrCmHt`<-Zyi1zqG+BnLjIff-H)108* N1XgIM8mg+$?>C%Km3IID literal 0 HcmV?d00001 diff --git a/bin/androgpio/ModbusWrapper/jModbusMaster$2.class b/bin/androgpio/ModbusWrapper/jModbusMaster$2.class new file mode 100644 index 0000000000000000000000000000000000000000..5474f7e001bf0afd41c255a8d96c8c5967f33d34 GIT binary patch literal 721 zcma)4T}vB56g`uT*=D-c7(ZKEwGh!naEl0iNDA+{pg&5yyA5dH@-Q>^UhX;f%$fQ7aCZ-22k%`J7}kZ3tmzJv@!KW}hsmiGgMqYu z|6ke`Nh)n^+eML~(igvl9}C^}k9$^%h@q7BR8spuM8Q~0O@e$ckarbMhRiEA&j?c%&AtxU{WdM>Q=!yVy&`P>Yi9_TRa%TD^da+F%) zZDwA1`&vueibayh1gqq#Lw|x*Dw(wu6==D5LG^O}R|ATk2$7MrHo+)lOu+YbY^|!#x?jK%$`0G@SOU9h6T?^$|Dj7ZwOYv65i5Tq(^v1 KasfUfr0@|IXRCGq literal 0 HcmV?d00001 diff --git a/bin/androgpio/ModbusWrapper/jModbusMaster.class b/bin/androgpio/ModbusWrapper/jModbusMaster.class new file mode 100644 index 0000000000000000000000000000000000000000..43f2caa4833f8e3b1084cb7772e1ff3d0eba4cf2 GIT binary patch literal 11375 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 K7vK{@3hx0~qNv;e literal 0 HcmV?d00001 diff --git a/bin/androgpio/ModbusWrapper/jModbusSlave.class b/bin/androgpio/ModbusWrapper/jModbusSlave.class new file mode 100644 index 0000000000000000000000000000000000000000..adea5addf0246bc70d011237ff100fae8ba3f5a4 GIT binary patch literal 10220 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 literal 0 HcmV?d00001 diff --git a/bin/androgpio/ScreenRotater.class b/bin/androgpio/ScreenRotater.class new file mode 100644 index 0000000000000000000000000000000000000000..c9bfdf335328fa61de0d14cb0d489e1f02fe3b4f GIT binary patch literal 4632 zcmbtX`&$&(8GdIM*ku_}SJXse1Cdx*kQJ3cT#ZFs?N%-Z5CWv3!*YPpWfo^<0Zp2V zu}zw!N$-ueP182LCALkhTpFA7Zkx3Ii`u78-!n6?yX-=L=<>{-v*(=e``+_@m-+bk zKkt7C;0XR1fKS10&6qILQ?q(H+MhIqF#6KAW(zZb5(V2XXmeULr5RJv-th|}X)ExJ zYYOh|(u~XV)4~*1deWZPOc5PFqD4=3#1xc@IbrZ%xGO8%Z=1R?#Z6zg$lr0*a;MS| zVqz?no*MJyl@Z!;7UwnHo)zYdZdrQTSlur>rW?Bbhys6o!=Qqa&h&%`!H=+tN^C8M zf+`giP~~iwiY-tTZ0*tw(UX}O7v_LAo^r6HlUiy}Gj;jfTP(4sb<4eFQEmt~l%R24 zOX*jrq`7`WJ_<=_7?g-Up`sLJa#o`vfS{Z`prRb15NCBN9>i7!YI0g6FS<7-pmK*A zLz|(^$|+%=(~=jpDbeFBRMj^W8Y@QxO#w7IelF@U6$cPhP%)dUD@^5`X~U)?`{?4- zoG=yaqX8#a^oeNF-Hfg~6qEK^R2;%#x>=i@P3cL^rqi5r3ycaGIpmZoxEt-ro`{9e zinahAS`Epj7%4967&9Dw--^Dr;iGB#wsAgDx4C11dg=diiru z#Sk7-P)08bgAO=Q%t(PY3UEg@eAK|H&uSW6IBH$8AD zh-;f3I2^4qk=^v%WFw}qG2S6 zFUzD)CPhk^=^4TIY96_-srU-MN^)m2{};J5^mX1b2f%uAgZSpA2T0?$H$6ZauWx#Q zG=4|HUMF@$&7`KM#6(k#Zjdp3qQ>FvxXk& zKLX!IWhr4y+0!Asi65)@34Y2fcZ~E-D%hV}A-oo&&{!;ppR;k0Q6^LL9Thk63&x(6 z8ILdMmd!VA(b`G2f5jp-mfx3174NCIgTDHbg{$8>sXuRHCV9wGKQ_sh(X=b+0u0whF8t#&V8(kMZ2K7{^_)7%SMW~ z+mff2U0A{}gxQ(WEK5*HncEWt@OK5Z|5G31X-zk3S>pU`zDwbg?H^}8+59;pTR&%H z%j%45SUGN?1lw7DZ{x2x&nq0aMwYQX;$OrL`L**sM_@O$_F)HmRH?hh zmG^X0lXEo*vkLopIUoPnHhA`$Im*3~TWENfqmL*-D&$&24%QM+pHDV@o@|RJ8@Ppo zOE|J#yg5fauy)mUJnV_Ldg2w4+t_;>M_2I3&2Jc<@J zn1?vF;*^76mutmC5JD5un04S^g6Uo7Bl0e>NcC3O_N`#nv_7WT>kI#5?*$% zrR?@7zTsT!75@re9jbnZ0{krSkpV70O?*c9oZxetPaosDlB00xaO5K4ukj)!H{GQY zsVWlwM$S^HyY#J`r80Nvb$7{e?hQJ(dI{g9XSwRn@^aG2%1QDvjdolh4;PU@3TI(p zkS=?iPJ4n5(_!(KjZ2urQ<%q9-sBpt;B`Ei!_y6qC*Vd7PXUy+hxwte`9GoO`?r{5 zGGj(%h~32?hx_lMR}M?~{u_u#+}P_GmP;#mi#G`W%ni;Zye$hykpsWXIp7o(*ZXd; zKFdn=91F@-B7UA>dyVYB=%9Ct%czH5`j+7S#ciK#<~+o6bhwPxR@&Wh9S5a(KDCNp zQq0*pl>U+r;2-ukGNaxdl8NJDah1Hx=zArHxz;u2S{VyLnv^K~P>#?h-y0r=%N~Yy zn%j9Dhd0FF5#eE|Ucquv7_Xs?{hO?TZoam!!Dz>C*8-g|+O`Gp0Y3C#>?bHmMOmbg z;gl^3ZV`ORQOJGD{0_e_kf~nAAJ)sfx+=pv|B0G?_%r^(v4YZf`MhA^ulO4m_VBLZ^KW5|dkNxHU1xcBNG!Ix;rN!nPTY zYfT?{hRT_^5MdbNxqiz3c{+%jjvi(9b)Upa4}Z#JWCj7= zW0k572n~@}S5)=Y@wuFiq?NHbO{T)iu(vJ3@84RT$2U0aoymUkG}BEi`L-OI;6Q6> z+mVQ48KX)NJ$i6fsW_4pC BolpP( literal 0 HcmV?d00001 diff --git a/bin/androgpio/SerialPort/SerialComm$2.class b/bin/androgpio/SerialPort/SerialComm$2.class new file mode 100644 index 0000000000000000000000000000000000000000..941cdf2eaaba651cc59d102fd95e0e8a4c0970dd GIT binary patch literal 1514 zcma)6OHUI~6#i~IP#8u*6jZ1@3-&>&Er^c-N()Fy0TT#`vbvpK%V3!yoq>pP;{&4$ ze}Eg~4{&3m*cfBt#)W^$#CYzsT6XOubD!rs=R2?a>(AHk04A~IgNI>|oB5Je*fXry zx+odEc-Ja9)n(e+-3=ssP#8LP_yLaY*Y@G7Q%<8b}by&Vq0%{uyRr z;Xs%ULCIXkhYO^mfe10Q*>31vG($9)Z2;v8CqtV)_!)wY0#x*(PeVVt88l+2rSDmkYBP%~D-!oV^No?MQGtyDEMZse#7)*Y@tT;Y4w zvR2ov{gN&g4O#f@wPqccy-g5sJY z;0%q;Bm+D`CykoBR&f^RXh-(ZUDYr~vy}5FALMuf`Q!xB^B;K;JyqmS;?+c$Oymc8 z-(o&;j7!RUOou-~D`Vgoqci?@i2A>^tX7Yr{-a}rrWCo2mJyZny)xom$`LQ)s-*69 z35=49z*BPh8QSrjOuj%LjxdCmh~pJ*;5C-;#w9peAyFkrbm;abQS8Aul9(o!hA@g5 q%#!XnmHsU{Q*fK~Q&@kK8ZYK(<3S2(niVR-dEB9ujIoRieSZNey?J2( literal 0 HcmV?d00001 diff --git a/bin/androgpio/SerialPort/SerialComm.class b/bin/androgpio/SerialPort/SerialComm.class new file mode 100644 index 0000000000000000000000000000000000000000..ecd45914fc915f1e93922cc644d881fca330b9ba GIT binary patch literal 8646 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 LP_5>*vp)F$G(m&y literal 0 HcmV?d00001 diff --git a/bin/androgpio/SerialPort/jSerialPort_Event.class b/bin/androgpio/SerialPort/jSerialPort_Event.class new file mode 100644 index 0000000000000000000000000000000000000000..c41ee82779be5d3cd6588fea9e2b092c8c6de94e GIT binary patch literal 200 zcmZXOu?oU47=-Vy)@WT^^bw>kW^r>9bSfwmTpT2}A*7T75TV6`1MSHT&^QO<&eJ71)7!qcU=mc+tQT%voWaSBi!YaZ% zFZ!zVP8+pdoe9jyXDz%Sr1@nRNmkm2tK>nqlCb#GtmDjx5Qf7?APLqKBSbsHI1s`l Ga%4YgR5ES= literal 0 HcmV?d00001 diff --git a/bin/androgpio/androgpio.class b/bin/androgpio/androgpio.class new file mode 100644 index 0000000000000000000000000000000000000000..1c8508eee5aa777c0db394857bf0d954f3df6f59 GIT binary patch literal 3313 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 YVdt=NRzcRpC!shH`p2fSO2TB-Yt3iKxBvhE literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/AndroFTPClient$2.class b/bin/androgpio/customsocket/AndroFTPClient$2.class new file mode 100644 index 0000000000000000000000000000000000000000..4a327d4bcac4a9ad065576a7017043263961e5b6 GIT binary patch literal 3100 zcma)8>30)V6#orLn-U&SQfjSG2wEUb3n^u%1#K(UVoT93N|G7~A?jK$NoV~*Ko{D|hnS|z)qZ(n;-*9@n&DFDBM^26%58cV81BGxl> zSc^LWlq#rkxoJmF#^Rg9 z1i0*HVsd8g>Bd6;#aRXZj?FMzD-FC|F%o z83=5|CgxF%$2HrnpM6DVsIJSj5-~NU#z!?4<=7Yv9*;2gCMo*{Es{AT5YLanR>{-^M)H~4$}p;+ zy5Erf9CpmGn*~V_>IjbV&S8Ng`P#S3(2ofi5-Ebiy6vSofRuu&nO4o^xh<_Hhc&AV zM9%Px9d=FFDW7!h|C+C@sNZ?l78|5IBlsOuL#&X$B#9-CUvtw>?jq%kUU6 zeTTGlTxvQ&d}QhVkNgt^{f6)~^z01c6rK*?sVQXQwCn7dJ{EWe&oVLz&FLo8s0sGk z(nz6xwy=rQ0?$k7GRMrtOGf;Rz>9c^-RgnW22Q(ZvBf7l;1#?Uz^iQ2nQXL~W>QlP zf!FZ{Taz;^t@Vt^)*Q@t%@ZT6_y}$a))a*i6j%6`mAM$s4hPQ`_Bv|(NVl5uCdTY& zc(5PjbUH5(MLlEJYo@KZ)}hORU(q`2<6*sgC&s2%PZ+A>B3VaX?mTTd&VPMHw9GMf zcG1zryN!6#wD|?ltvRFSu)xRogxO=I=1}vHSF;>V3KB9U$0kNKOS8=pXF|2K*w7|5 z*4Emb=g<_~6|xFBgM+h@F+C7PCxO&zPx326vk_*be0)j}C@+^|A1DvWq3^C7`R>Yr z@2(v8?#f~Bt{nCJVDs?_p@LeLk{ZA~sRa$u`bktqmrtT9x^5EH(Mta$mPM;eCQ%cu zE}g{6Xk}m$tD;wM+ZU9dhc)~!r550GtmVIO=>|kl#~sTS^^|HL?dNWtw5FUOE$#mq zy-Z`5QI$okt6pxh*qp)k`Xv>oGq_!Hs`)I-pk2~6d{$;~*E#gpXK>H`+*bOt=#eX* z>MVLaa#)HFV%Q2iJEvi0_fW53ukfj^ihs#?N>Yzfjk&xPagAJ8rnq z4(H|-GL08p>OH*gt{>n-u70A{NBESKY-6d0zkdL&WJSLK literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/AndroFTPClient$3.class b/bin/androgpio/customsocket/AndroFTPClient$3.class new file mode 100644 index 0000000000000000000000000000000000000000..18cccd11e879734998db11273a31bd32639f7fe7 GIT binary patch literal 2673 zcmb7G>r)d~6#rckScn&ipyCrzgC;S!D%e&)EkvkT5NZGmw$jVyCM+zw>F!3fFZ+Ji z=~q8>`l(;-OpQ!C?eug1r*?X7vO*B-q?y_5$vO9)$M3xI*FV4h0iYKTG}JMi6n55i z@&(h;jiTo}E1qL4OJ5&U?2)O-v}H=$kM(E>Genof9idyo&g&BkOVaQ)G%y@4bBn%d z>4sw)Mc1VpdRkbPSg<5RgTH8cu`^_AtR`dq42^e$6)OmTk)d^L!)V5LO*>EWh5^$y z{Ywl3@fvk*)smQD2&bK_Yyv|IhlWN)?c!)cB!UpyICfw+Lny9}wv3s!yk1;cknU6^ zuGTTf5Y~)vP4zoqhE=c(iQ3$X^-wTj(qb{}i6?GVd07FN#F62{*6ntw<*3l(jC76F5nk3RF;fCu`L(ml$*0 zye&VD=QS6Zd29e{DQt-bp6Usf+LAj8+ORk>00g7W+v^}IWtejx}j@0%@C`_ zIgT?RUz(`m>2}ejI@jYhg%#LK%&72sICbLrs!W_?=&wD#jZz3&L+N~x<0V|E(%JKW zvOB7P=;s)~MFx$+v#HM+_QtDfGcYzazH?#7rY57}%sb@*2kt zWS$cZv#HMY{=XQ(JhEzDWg^I7 zQA7UeprFA%Iv$J@$aqwiFTMwN!g;e&7V^o0oS;I4*Mn!Xz-3^|S^h0Spf?@%SuMkvaOoT@~c zA6t&8rQk}hXc5$m(jB5v!f>Ldj-}%f9Bmo44a@Op zMUG2<(V=?y2H%qZn~I0wvYIdVRu?7V<>dTT;Yxj>SLj27{Z*a>Jzfp<`by%XG!@*m zB|J}h=%mm?^wgs+8dYl)h_)(Bu_oY&SdH_qRA&{{h35{ z*hAk9bP%i!dx=(;fEKi4AHA7^mQEccI!wO~DTS<$=rD193=JfIA&vB`p<@lP@l=J5 zmyim`a}~}DCG-WHOBK#w30EE>;B`F2?$n>S+El_d^=`dh!laT#OPCr+mGJsa`g-Fh zbX-@rf5ZH2t9SyVy98OnatY2F-1^@UnhmK-nc48cH54FCs_$RfTs>W;9a~& zR(9cgypInEDxUzmj}OV-M`a+xf0IHD-OVE!atES=gy_f5B8o0jp>VHHmhj0VBB~NY i0+pCLdcyduOnr_o%I`1nRhfGPU*kKXR68g&^!XQY@56im literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/AndroFTPClient$4.class b/bin/androgpio/customsocket/AndroFTPClient$4.class new file mode 100644 index 0000000000000000000000000000000000000000..4158e973347f76f333a133df17f247afee425534 GIT binary patch literal 2664 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 literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/AndroFTPClient$6$1.class b/bin/androgpio/customsocket/AndroFTPClient$6$1.class new file mode 100644 index 0000000000000000000000000000000000000000..12ef61f583c3bd2a14758f24a5f58facf1222799 GIT binary patch literal 2436 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 ai_b%ZbVZ+=Wt!CRt(IHqo%P>N@9{sX;TU`X literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/AndroFTPClient$7$1.class b/bin/androgpio/customsocket/AndroFTPClient$7$1.class new file mode 100644 index 0000000000000000000000000000000000000000..d4c46eb00b4423b67dfb437d5edf3a0a313703a9 GIT binary patch literal 2589 zcmb_eOLG%P5dL;8OSZg#F-9N~z#$6AYe1Ms3^7Ukg2YBP1R3*AGFpx7#j9OqcV%L# zQaN$qk{s|UzamwU6z}H?&MAiQ7nBK8c4<~Zj>9P^J0hI z#|`XV6J9P3g&$RAC@Ti?MSYe=?0R(SJB={pNehB8xK3eGlVad6Q@2O!y3T5wy)jjf z44mn}z-_6#`HrNOl(JG5UmM8s-0M$QxDo}`_^AkGp?pykrY5;7t#(Dyuc$}|S}wNb zi#z(%j%r-5i+E+{7ADHo2i~K$c!49ls_V&i@KMkR9T~aLC@|)j#;dOH#vd8@Gv9F@ zI|y0l><#XFox=aj+U_iEleCkvTQWDiWFRpeRAespEC(2KOwXhfa6!)Hme(YD=DGLKQptxZRgDq~v!a-!x=)xHbhj7+FS6(^XS9E>3(5RJV zxG2i%at4a(qLxJHs(D+OP&IB~q|=+pjj}TO%j>a>7TZOxtNd)f*hzC;TVw5ytMheB z#VXI8>#>&dbF<868dor$!UqOUcGg@LuHr+cW?9lD6EvqRd~9I?lblCNn#gOz)3}zx z%nk%8trHPy7OpE3``Zn?=+@-C>v?X}O_<`>^+$5zy9v!EC%)GdX)NGY3O9F{n+Y1s zzlG0myVa}>d9!NZV18zJTk*7Zji<4Qr4;TkQU4$5s}fd$-Bl*H8EoMobDze2JWSyM z>q&G5sumvMF)>Lv>}!pPw>xj;4#87L`h~iuFAWUlx2K|ga+S=o1qTGxzKTxblJFX= zY+mS08da>Mu*`nxC@Kpso*3wD$8`Rjfp0ntYWz(D#N6DBYKen!5DRZ=S8o}!gzHE@ zP9=@1$(k-aHc>@-u^^4=QoS_>3wKW`7S8ItsJA;jUD9}bT?6!i$sO|MW@i&k z4B6T4CQjJdWD_|%n`+{uo$YBNZ)bX&u$B9<`K7a%)@fMW*M)AZCN$N?J*JTPs6{(}TDXY+-&ACCZO% z;U38ejvU8Hj{NbX&q-{Kp-p`L8x}SpwjfnlT{<7Ox*fBG{pKPv<{g|e@8W`a4_D0x zSTG-9$$X5eDe%%P>(D>t{W7Rmp>JT7_oeciCLYL+bE0{z5fDt%x%dF&d-^qSPAJ$qi6;pHzLy2fu#+1-YgR literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/AndroFTPClient$7.class b/bin/androgpio/customsocket/AndroFTPClient$7.class new file mode 100644 index 0000000000000000000000000000000000000000..13d89b697ffb43d97883f29b9ef7d35e2f558ca8 GIT binary patch literal 6493 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 literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/AndroFTPClient$9$1.class b/bin/androgpio/customsocket/AndroFTPClient$9$1.class new file mode 100644 index 0000000000000000000000000000000000000000..2e4509463d6fcf774d6a8597d3b474c4a563e938 GIT binary patch literal 2074 zcmb_d-E!Mh5dMziSc$8q#%&r*2{GUjTWKTGra>+L4%;*Bm9G%sAf6m$c_VeG*{`d>PGQQW45GYBv z<$LX(?U~kI5PIFfvwl#axytJG?aiuVD>p1$FDz+D3S=J2eQ7$aNC{l> z{I)53(&{MF^15Bm4NRAUP|&K^JJi3 zc$Rc}GVHu)M5njiHW{5_qusDKFiEZ0t{vVK__-MS7PS)(IeMt)G{X~&YDzl-$*R{< zlMpzsL&F41uj`mZnx!{%oPy5Mn>tQoilw)7oI!@AypGq9Nn;G}=*Z$-fw3ZoJX5z_ zb$hSdRQ|SXI#layoollreVf0B$|Sc@U?J}H3rkeQO!FXA!L~2mU|0FRY6&bB>v8b2 zwogDT^ZCXVi<~*WAEB;T+o+Xx(pbd%8cczC84vn8mT*O2LRyvzBHZ4M-}0CcFKbv9 zm^p^#py!o6+ac6W-~)kk_j-;eTlq~IS3-G^ujFs!)3}O{xOvwFri-sa2wRkOtl(oR zf$a5Y3&D7h`o!$8Fp>?i*ETC1&5sBNrpwar%aiyVw$#9Ae=LkyzaM zFaOWOU#k(Csq3K^N~bb1Re~y-4kG5=iuf|UFTDN(i&#u0}?~4pP8vHfaa=d`( z?v@HWUQ5RVd`lEVv+ppzN)MhZ%z*!Cr1cG#XuaylAW#AFL@Z;p#*xZon2~@?miZ{! ziH)M2xG36*iK3l&i2jtuXk~Ghyw8!;pl|ZVys^;7w2|*)#>gf6$QiluJ}wx!R3EcO zPU~aNc#OAyBhv&5^iEL#B%a_R$vVrJ$9uFuauKCr_-pi?VBO1(Pb0_kgCB>E>}p_v z-W=l6FvPFKt|@T#<9AWeLNK;&t#50(Qf3Qg+-^oCdlm2(K2DYiZe9s0Q?p{>J26jl7 eB>5}SC-61y)1D;q`v%{UlqKCF?fkGOJ^uoQfFTtC literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/AndroFTPClient$9.class b/bin/androgpio/customsocket/AndroFTPClient$9.class new file mode 100644 index 0000000000000000000000000000000000000000..2ef79ae9757f9ce0167ab9786d6e953c1d597bac GIT binary patch literal 5166 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 literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/JsonObject.class b/bin/androgpio/customsocket/JsonObject.class new file mode 100644 index 0000000000000000000000000000000000000000..62f6fe0a85d0a8b4f78a317f3bae37a7d9210e6f GIT binary patch literal 16100 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 b{1onf#xLPp#|GaCHnD+C?qropGyMDyGp-)p literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/NTPClient$2.class b/bin/androgpio/customsocket/NTPClient$2.class new file mode 100644 index 0000000000000000000000000000000000000000..664fef83bac6188e2ae2be7fd1c72518e5cf72c9 GIT binary patch literal 2213 zcma)7?N%FA6y28u2I5d6rmcb?BG@E_Mi6N&m=-A1C<$n2+EBlS$&d^TGjV1@?GyR8 zu4RE`mp*`x;zOwWP6kMU0$D3_&)j?N`PgTlGk^d4$6o-ZU@2%57}o5H>s0HelhGTV z@9cPvzHRuK!rJPBWg50WGN~Xc5Z}^vwTz|N)y&GqmZAF!Is^uS-G*;k8Qrno=#Cj%%Aq*?{P+;gH z994XT5n`$7y5R-b=PvP+yW0MEAUzc2X^dO`IL-`m<1vdl| zL5ZYNj*q!Y#ic_PDWnCus)j%RyrAun1nFc;LSZpe&8-ucX?zT~6pRbpxMVykG9bSc z`dc%lw`}O`r0LwbDayM!d()XwFexzjW{{;)3=^1=jQC6-nLH1Dp@{c{GuCm-*oJ|LorNaM!KAp6gdOWQVdz#S8$I64xF%| zb1RF77F5h(ULZ!8@~o0pB~poGScRag!@@{M4^%8-i4CUJ>xNws7(dJ7sUX5!Sj1_3 zsUjyGcKS}!uw?MpDwdHy=H`UJ?Mra;l%tsxvt7YA3Rc;;$B@oDj%8@Jif>UQB{)qQ z#>%F^wd8thrlA^pPVpK+mvEk}vjfR>lO1+WzXlYcrb!kEj9#krG)}OuHnVuWDh#2! z=xh3RUaN;gz3njROb!9=2TRe*y8pHRtwyhHGlrB zW^i6OoBlJ+H8LAhT4sK3rX`k;k8IoH0w2Ml=4{(=7c9;543Es>Wahsek$7B=aUk9+ zhj_4Zg!6kB;lCSKsMSL$i#9~LCek-wBAy<5iQe?UOTO;o+Hc%%!+U&oP#esmk54s_ z6X?hLeB+l7gQ2Z|Xi3^ykIagE`gg<+@Bx?J1B`M>?BQnN6(-8#`}i~;o&6Pq1*!B3 z)8*bf`?x#4hcEW9u!n~S_^RViM9L9)yI79)AD~byw~rNH&{GtnNHXq75ZSQI5j%|2 z=1H9q{>-uOQpe-`^|656AnGY%!pOp?+wcTycobMGp@hf0-^Wkf6O?H;MR^@hDSgK~ z3AFu3hY%Z`hglz+W9d+bCi-qsy)32?FEHa_(Tv$TW~=S&MGj1 TbraQKcL+6XQWB8G4YB2+8h0L4ZDFCZ3di<2-UBgsshnV{HOd%ryN z2l(9AKD28UaFx|Q_MtzjtKXR!l5lIWtQF4WoPECi?QiedJAeKC+aCb>@qHKpffm)6 zx6E{2H{+>-?U*^+Of70od}8X(NLJSjCybCl-D7o0jb~LO9lxD?tfd@*KvETG8dHts zry0%CY;(bRs#;n+*{{ZjhXw_zwIyCR)SMn;W6ZSDaW$`|GFm)k=5nTC#|?5eoV?_H|C&#oNR=0LLw7C<1fw0( z0-+IeUW*`z1_k?3R}F!;6zqc{uzyT9w24A4saaEMGV4+^Q)+fvwRHL2-wZh!-4J=*7D- zpBHvvG+l*@%)KlqX?t106VY}yR@c3(K@`e0V)zy{)XmE>GFNO@tKW z5j;Xl76J2lPO|@xx3i4%M#d@AC>9i?kzr=7f-z(-8z}*6Ph$9yZ^wODwzbKq;OJTQ zFC$g3EOyHHh?>pHPBSPwRWPYOW6fqvo4vNuW}Rvlo0OlS2fCBFV_H1Ax+ijeh>3EA zeJ){`dl9%V0s~K^z@%~he+kS&WJGzFcD1GIw&uN&qgYb#6b$#MyL(;EjZw?YUDFp9 zI9d#6Dr0FX6FXP_qHfYMTN+J+i88_cz-Mj<_ zaEN<~XU&}^eiLZMVIS!UttFG=vCb77iFK{uSS+@J76P}%)^TcP4X0NSEx|XtbX!UL zBuTZA6bB*NaSW$jS1lf#?~1@~fRgNSH^cy`bN2e~EuPA=v$0=sD>w=m}W zm*1rSDtd{oV*DpQ2VBrfcmG@&P*qXBJGkrLAEI@DcdF(6!>hPg5-Q>zTwzwPx*(07 zVNsZB+{d)*{sG?i;eMdCjOEfgf>geKqO*I81T&rlmA}&<*flv53ckj}$#p!MiFK_Z z`5IdH&&3$LQn*RbTNvaRk);sx9299>g3gRe4KCp^Ed%@vV3E0{SjZyhQ~Qy{R6(mD zpW1~`XH#g8aVL}1RwhxPH~meR><-w1@7tH?BbCkuY7`e|4KVaOWKvO&NaL! wm6Lbp#l{`7dr7MRKEvl+_wn`@{CsKSD}2q3W*R)=D*p^`!8e@I-?dl&1L#5fZ~y=R literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/NTPInfo.class b/bin/androgpio/customsocket/NTPInfo.class new file mode 100644 index 0000000000000000000000000000000000000000..46586250c78c493abc34fc8be5422cbeadf32449 GIT binary patch literal 1421 zcma)*TTc@~7>3_z3rov#ma_^7Xran_KtUxYiV`su43J)UH!MS0Xm^|4Dae1q3lqh} z3op6wN2%|0SHu{Ri#^PC=6T-lo9VajU%mpE#X}1zfveKr3WIXZ3mmr|>7W_~?w-=l z%Ep^ze><>{5$N2N`_idMzwE4T?kZOcq_ih6Sq#FmBWu##QH~o_tAQUmJ{SAC=4^OX zWeAT2nio9Z(@zA_`KgjXW--`OIi%5Q!$M0I0&O;$&@9kV^nA5auWqVvLvB`-KzA{4 zWu+uT&y16g8NK600;9$A!&9BlUnt{LDThnwv(PIreu1KF^rJDBG+NU@pS`KjXX78e7*%;asHu%>i9 z^i3xlfL8`^&Bh2ubC|$&3zN+7=SmwlFiL!d6sqf7Dc*4l{}!}R@LW~op`qTiF>PYy z)`NQJswL0#-cAm|j8O;-uGM|bEF~}UxED`-KhRQp+;ldLTXA@>qe2x0+xkF;%GsQi z&apMZHS7cSOrl$Qkk(Gd{SoVDtE>>g|smQ{8fwBLBEX)b?|6>|UKQ&^EM=Q<4 z&K8^V#kXd@^UU#{!S?U~_I8I{3|wXQLBc92rt~ zLaK6Air)qrqykCPA7P)6%EvK!3^(22c9RIwGNfd=z8^gec3-?cVUyHIEci6D#SczQ zSr8UC%cuu2l&ClOG)45r#*WxHe1fLTQTm8Kob?Vd^Dxt2rgh{@O}&ZN*wPR^B=VFc zh9aXGLsx8QjSXY(v+qyoakuiMPmx+UL+<*M%u@IV_(gV;whhJPOG)-(;RG#(V~n3w z#ErX8#T@bTY|NvECT`qNqUm5$^9&I;j-?Fnb^1TT!a2f43SUsPMB&Se3Dda6#-uO< ahtF09xXtTMguA%M4>=BI@c{GJvp)e@7YR22 literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/SocketIoObject.class b/bin/androgpio/customsocket/SocketIoObject.class new file mode 100644 index 0000000000000000000000000000000000000000..afcf933825c9f611a514408908e1af9500f10891 GIT binary patch literal 6257 zcma)9Yj_k_8GdJX!zP;vgb>0dKoUYA7hr{^Ero^>QjkqUs7VM-s>aD?l1#FhN%sjm=9ax3)!% zl+YwaB$SOP(P>Q|Q_n~UX$jp(YU7$ZCZWwTlS*o_aMaMFsd${`!wPY0(Qv0Z95#%Y zs_4SP!w_S#eatW(?G+pgY&5Ww?0cA91 zhRTR4u|XxSiEn!{ket*K5_a@_pk&eW(uA5kVr6D+b75*kG-~@8Zf!=5=yO^^qpePz zaaNL=q0`hBnwmd7sm9fWF`hiF#MSUAU{9x$vL3*sRO4Ml>|oiKDuE`)tJAmd@|A(~`XmW5Hj zgluO#uFQwffdfHwN?2pWC&E+I8Sd^s(VJ0xNUT1}3eQ=!MZ0hygbyPU#IYRqSm0%J z<1qT6j z7{n(etaHRI?+&N5*q9nG#i)duh@MpA@ziYc$eE}*D@v?>TCERZ1Y?3xJH^|$j0sE< zlrlT3>g4(+8ZGeNR?E1Kh~Q3RCJ4r1U6E>)p<^cR1-51m77pB8lv*cLDO+DyT=@b_y!YF;lF}7UOT$Z-C1)`1s=$uBW+c_ytLq9;_kEZW*FFCz&DM2Nlgt1>=| z&t)LyuYN<{y3DIxM_#&Rq*veb{u8c2>MXTjR5>{P@a2`Jh;`_Ac1b1dW z%A+MA;{slhu+oZ~W=lx$YM~xH@0I~O%GTzAX!CImj+x*d(p|FXZ$D1@NqpJ(0y{e`iqGpO&#u zzmbYZ)uWm?Vb|ndZFURtOW0r^hl3u6VPnz5uz%8sCwr9{aU!nF)yt4YbX|>i#gs%s zO$2d=Ev-nKG7nXDRdKck@dxuv^FDpE2H7XF?`U8x`+2Eb0iU>k@XaS~ALhurhdJ`* zVUE0am?Q5k9CuKXcOgDk^Xn-D_y)AKE~2XSI;w~Ki>SGZ4XstRSFyQOx{5mSXlV6a zMWc8$T``sI;B%{KXgyWbP~iqXH=>q*_1J_aY(^`#pqcw*%LH0D1HQLmC*khm`wD*T z<|v>X!(^v99B%LlUC6Uc*EFWxtm0ps+d z?hZ!8a1;B^VWoI`5Bt*CzhF&vWG6e)c*LGOl$|`3#$kK1D?8bh#u0P!8jdbh^$2u+ zGfcb9g4xT0x{uLpXEyI=&>kSJ2XTO>$5A|l$FUzz@+->L?&Nsf4recQ1yQo8y1F_@ zCr)_Bwjn>Zos8|_Wn%kywsy}ltD<1)7=jH$J-5UVNaG`7Ok<$!_9d)nyM5ydHwCet zbqW4&Hb=|P8kz$lTG zk{KVysaiT3L}DG+>`0yQOp_Ne;+i27bn?O=FJ{S$IC+tDc~OKHpY*C6S0iK# zZp`P%)2ymzOx1;r(8`vCr@5oB5jI|Cg4~VpSrX@C7>f;xZ0?h7J}`E~o1^#ew4@>0s@YASh#K@!T^sf?X_w;0R{6`>TJ+v(6%*RgNN znmx3L&o4+$AKqvl>=#&XuTbj`dBR@g8Tcyp;&m60b{CKUd>hu&GB3a$NXQj|#NvuT zBDf-uSe(cp5xnq3@&=JC5XluHd6P)qB9innNO;@!K;p!N1hr$*;T%vz(qYG>YY{IL z#^eT(yhkKAiR2cM+$NGc%OK&^-UEpf6B`M=H<4H^A%jG;1y3Y@B9cE7$zO=%uSD`U zBKi9=NWPLSI)`CtymZI2=KkSQ=E1P9dFuMQx2}IKt?Qefy1wPD>t9Rjdf8Lgcf57| zdud(gvby#%gMwG3f-u+cy*!e0$+?S^+Psj+oJ>c{lmAh^e3_^GYsKV$oG)MHDgTpV z@;}X&ul1C_TulBK`SRO5 zpLKb(es4JWf2WvyLH@cu?O!h@Uy#2(Px)ULlP}2Mu&4ZQipdw`Z`4!%-D2_u`J3{P q_u~c`=VQOU$x&PfZ}Go9!KV9LPE>K}eO`oqx4jI%=XiH}+5Z6x7LIBF literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/TCPSocket$1.class b/bin/androgpio/customsocket/TCPSocket$1.class new file mode 100644 index 0000000000000000000000000000000000000000..7d63ca4b1dae634a994c7b7e56859921b581e3b1 GIT binary patch literal 1064 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 E2Qqm9`v3p{ literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/TCPSocketServer$1.class b/bin/androgpio/customsocket/TCPSocketServer$1.class new file mode 100644 index 0000000000000000000000000000000000000000..59b3a811dba2f89664270c570fa4b3bd88888019 GIT binary patch literal 1190 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 literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jSocketIOClientV1$3.class b/bin/androgpio/customsocket/jSocketIOClientV1$3.class new file mode 100644 index 0000000000000000000000000000000000000000..9fff8bbccec04baeddd489c6e7f08d16baffbbd5 GIT binary patch literal 2652 zcmb7G%~u;$6#qSvCLx_t8VVIl+fif92W{HYqAdk1l(tYl)F2g*;xKt6Lnrg%%ml=* zwp#lyc)IZ%SFSxr(jGl_>B6Ne7am>c!iC4B3-ze?O@KIa*m9s^@WQdGjc3e?(gnom&nT4!J2gmwNgIn&z$#_Q?L53rw=&R`7 zo6H6M#x=t&j2lHR$}U65Ef`Mx1i2pGuJ0~}#zoDHTb$=+owBZT$6;Vo42l$MglLgK zFvNxzv_&mrYF0jzb#24SuQPR5F?eRYXt2vR}Ve!#&agN*L+MG#o#fF8hnG>3A$Y);|l(k@J-*%+&4oc|mxMm4!xhTpG zm5~im0)>@bElW#cx+c!CxdpDfUCD_sk_-nPFHc|DFmv3FAWbL+s35b|vdzktq0X_y zlF4&nfEPyL~#~fVVt9mU5mAv1O?~O%@E0Rx8Kxc2R9{Z!Fa;1 zD0*=rj2EefYYx0u3i{Ab*(dw)!O&+3z0W@ChrInn`h=uYni?a1o;YHS1^O?YtGLw^z6v_T9f!uMDYgZ!qC=rV6_Dl=rRdmZ`6&= zM?iBSvN}+s$ioPuK(}-YzzP;{gW-^^84e$i6ZeuWiWiOfc?!;Q#|t*s=#I^9lG>Wg z`Y;D;M1I>&Z8kV+)O<_2qjCh$_1Q$9q0TU9C{de-DC1?5w!)sQODkeTEBRB8rq!cu z6i6f|A}Gr|_}d|ZC5EV+MRGZGPe-s!18zq3k|rPDp)oR}7Bok-gsR%SWG<_QrJhk8 zt_v$iz@k}Ewsn5Vkb}2n!>T+kYex5bz|u|O(2^SAZb9S}+`?^!ga0!I8P3)w*Uwu` zinf#_<+2>KEN*+Vgga1arvzxzhZ-B@+5sA4a_M-KR*pw$;dqqRjYnzOkcG5rz+SR% zByNVD>xm{)=@sltH3wJFlxp3zg633nXaxsSt>G24rdlH_IG9rJa1nv5gh#c`Mt2lfN%*^g*oKcSOoFZ&r+*)RBr z{p!V+LQo=q6yIH#C{kRz>93XrL6Kg?Ig}{EI=o4m6zSjOEcGYJ8EiOy4nC&cPg%ot zLU-UEBuI*7OW^K0;9iy%DIl_Hr8lv+=!f`&l7G9Z`dDld57=**VZVEE_^11ENTj>K d{_?uuU3#d)dw8E{kY?LWd_Y{3;7Y*s`487|%O?N; literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jSocketIOClientV1$4.class b/bin/androgpio/customsocket/jSocketIOClientV1$4.class new file mode 100644 index 0000000000000000000000000000000000000000..aeea408ac0207a80607218aa58d99483f12522c2 GIT binary patch literal 2822 zcmbVOT~HHO6#niFk`Py=5vfqr#YW8!5=Eq~2viU?%1>=D3MtYhxsb(Vm+o$$wD!l2 z4}I@T^`SFPXY{!<1)X;4^r_S7L#NZ}d*6KPQ{QT*?YWypfC?U+T|%w7{tVkYI%7lxBQ8gBQaieVoe^p0fR zFNssxYnOE=KcW|fS#lVvoxE;`j}Y|0X0baNYUi{<*bpK&VVAO5VcQIBj6soNjVziY zBN##hGuoV%C}_rXBIQ`RF}+IFS+>Dd6Gh!Iw4z|BktpB#1d zJU_Pf_H{&=-e{Vks?W@c8Zhisum#nU+oiydfaG3OP=lc48We0T%U)8Tlx2GqY{T{- zeAuU82UG%!oR^q826RIVm5P(X8qp>TLZy7=Z7a}MAqqyru~SKH&5rJx1<+x!U~{;A>L0}nCM+_ zggqh4gsR0b=L;e?Ez~FYEvjZs+nFGuXq7G}C|Hq-`@B>~)AGbo1#LJ+In-ung^^=u ziX@|J4tq9q25_9>H?DT6Quh_9`zpiX_0TuO2;u}z`XNhg#no~Q6m)^gC@?LYzJeyJ zc4ws07+2UC#Ovtw;}oTE1;G8Jpbw`h_@qCwFe`@Ka;;kh;W;nOKcnERJRWe&vNVV! zF8D#oifp#m6bxXHAT+nj!W77!XXt@7F>;H5iZzKh6kNocE6D8({m+0LrE*=E&kIY~ z=9Dw9St2pnt|fX;QS_1+QE&;=0?TGoQoElCd?oH)@dj}PV}6XTN@_W(3Nm<$!S7Ct z;VFiLmF2Qr{A)uIz*Vwvd4L7+HYWYhs6jUHOhFb@zk4iAw?)64aAz#Dcv_#Dq9_>7 zNZt|}eV)DE9o~9qb;hI(AJMb^HIn)26jDD4e{RQ}7`^VrblWhA~7dv*QIm(_0Z`PZpKiENK|RavNXRP-*`AXcbr2 z*2)zD)Q04Oa4D?^m(p@@DXj*V(qbSDskH;Or2hhOyN-$q@V85Kp0jLcA9>T*Y?Y zj49ex-;@>cvp^f#Ws@B9cJ z@uT>Ix3M~Yj5QHed54?AZdpaFc{zo|O(RdK+e$z8bj(mPhiH{uqZ;D4?Q&DZJ#x7q zaRsuPYVmJuXR*I<@i8tu#yQe2!l0+DwJ+&M3Y845QcQN=f=ti{oO+++X% literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jSocketIOClientV1$5.class b/bin/androgpio/customsocket/jSocketIOClientV1$5.class new file mode 100644 index 0000000000000000000000000000000000000000..d2b85ecc17f33e73458b17b3b62f087bb256136a GIT binary patch literal 3412 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 literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jSocketIOClientV1$7.class b/bin/androgpio/customsocket/jSocketIOClientV1$7.class new file mode 100644 index 0000000000000000000000000000000000000000..0f1bb7a7f6120810c2522282b6d8b1d8cb683a92 GIT binary patch literal 3355 zcmbVOTXz#x6#h<6=%itwCA5V?kpLBwv?W{$2&EvDf+@Wyt;K?+PSa^RC7G!+lh)!D z!MoxWwA@?>iWk(SiZ0*2sZ9n%&!~#Z@C=GvwO6npL)vLO0xP>}rOB0VP(Zt7>}`F-4DtT5MC(qZ3^1@f;jCHLlsZ5?2}WL=^fbNMDvMt9r}Oo4fkd zh)pqbH)*~k8lr3OJ0)aLLSFO>l=7C|Zj2XxkT9FJ1ZlTSS zkq3{^N@UDTt}T=yC)XCqn1$IMa4eRQ4<7~d*Nd3>4VtbtCE{JG*{XEK2o7(95m91o zim8eF)S^pJ$WW0j(@9$|TThtHEK_ttABr0Zi`ZxNgu9`)jI&wn-6~v{<)lx>3QfvW|G{OoP)Ys*Q?HHwI&BcU1M=dP#*(F{9Q#4+2;r_8lU`{24Dz zRZO5wSgew<8f%DYrN3X*yBSLT;lL#0R3g=GtR)aTd^JAdyI%O-&rms4^sF&F*oaLM zM8&4}n*>5e4XD`MQPr-EDWdr2`ZJSp7OOpY0NW&NB_7iOoJTThv7Hzo|JI>?waKaa z!U-W#n-}Ttkg-#kyKN)6>OmO0BoJf%yFIcp8qi1~6z8y(5s+J^aZ)B>R4qseqglp7 zcsLz)9mBS1gbg%AjCd$&7}1y-iqd%w?W7Cg)`CYQv@#S<^;||9_K3b^h$3THp2_NX zp-#@&gLdqbKuu~+8npOOgifs`<1us)iAi`n!&YZ~OLNm!(^Q7C)Z|_XoeaKoqzT10U*f0*rh#{U1PUmFvw1THgIfbA-?5g)p)#5VDZf&p~Ps@15A#GxbBrPO7$FSrMwU9LP;8`f*LOHuc>!XUB zA_ImTjo4>lQcROf`xGITX6x>^ij6vztIz1_W0=)q(-3Y{`ketG;jq)&QzPGvqk@Ih z@O9&D29N03V)oYTbK_ky?(jL3)i}-hebGLT-PS%+*kpFSBsA$3`~fxT4qvZg`Ex1c~wd!WE8X1yb6 z(mRsoyd!DKJCbHRc?hq36p;TM(mLoqk7OWNJ_2vB$Tfn(V9AUT6bFlPM=(EFB8^}{ zu*5xr(xC4$?)jFCbFhT2xfBBY7?zS=OluSb%TPvAX!l|{tsTOB@F!ozZcrL7fR&d2 z$QG1eKf{W6%>}FoUV`@;0`1=N%cv|J#Y%70DAtvit{%mPQEVZ76m?^G@JJ4MUO#*D zSK%;*rt(qjZX~^M7(Z8x;n892yoUYlm+^R0WyL5Wdy;lNNzuo+cnRGUk3pw5d97q* z?)m{6+j(WnC=z=v;1Gp+$_e!>g?iozb?E!5 z{4~~(Tu1j?_*v}YA7eK^hjxA*3jYLA{wa>~3ph^l1pf@@`RBOEzrc6=OZ>(!;&+mN z@UQSE|C;6VZ`eGN3;897z0IP4Sm`)>q`iQ{#M(^y>_37RiN{^|1uqeQIZ&_xFXI); z@*-)kQb}>VMp-YyjK49P1#cn(E`dpSU82T}8>s(_jxWO-=t>p>&e3-#l@9h3K=X$o zNFhSA67}W;qMTBBiwGpI1@y%G4qZs6%6ZRwJZX9xl*M#Hz{eo*E2!dE39>8b;NLr2 mPi2zYy4c7JP^F4v@gd#hU>GMzx@dBp#2L~&l)6Zse*Xf?(xLkR literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jSocketIOClientV1$8.class b/bin/androgpio/customsocket/jSocketIOClientV1$8.class new file mode 100644 index 0000000000000000000000000000000000000000..af8b5c581bb8c7fc8348f1a2b82b2ca52efc45ef GIT binary patch literal 3351 zcmbVO-BS}+6#reavLUX55l|3y)zl{8BYvP&5iKCvARnzzC{UnFSi)kmOLsR=?5EX! zxAxl>+uG^0GrsuJPC=$lee6T051qdCu@9Z;-%vZX=k5j)gg8=W$T@rW-gD3W{eJhH z{rTS?ZULypmm=~Q!iqj(8u1Cuh{RHsZ6qxtc2>0`$ozhL=uV~k969mrpK2owHI=5mZ?e2)|I5nP{5o`xwJyFxT{D0M)hhEzfR+pI#5mOS~ zimCDE^q{~AWT?uO=(3Xs4iRK)1B!0w(@7&`5%;WxP&f3>1ujd2{aN6243DcZyDro% zB21^bo72XWmKae@FRB957uY`TrPdqCV6RJMKP!WuVmQ7CQQRl@0!f-&Ym-*U<_}W7Z)hk8M8N-Jo zI4Xk6HFI1S2MP5csd?k7-IP$c@;3&vlW_*?e0Ug*A{vOsOaSMVgeIII0%(2L^n}{( zNWOSUi1guk`X?ov;?uph;g0$c#c2^lSnz)DtAtjxQ3%DksR07=_$uznGK@$FJc$ko zkK(aR*n

s|XuvjTy;E+%Vz^H4>-G963clgj**b7tzH~ywZCK-RR*(%itPg*qzPl zf=-ub>_ad5MNoy>kO3_@9ivNYNq7Fj8CN=pt+_K6r`kTa2%l;TD4 z`!EQFYkQc%6Xc)`Vnl+8q%%Ti}x6O zyk_&xTi@@+2Q+y=b~vkds`E#@etvvc{Y>wbUG`jTGBx-Us?r1Um}1GgA zT|OXNYRu3_=={D;BW1?alNxVrrOVn>4Hp}g&t^TAFf96ZXjAPmgD%kL_<~{Ey&DZf zG+P*H&hG`E`z?_rfb^OW|E!7Xx@tPrTeTolBlplZS$!%%U`0l0S>h;WBX+<>4~#EGokC z4Q%_CCg))X{pM2$2tMp2zm&!(2zDVrmU|CiH;tXgg9y4?vD=h}0I-V6pICY2t*flW zQ-2LL;qT$UiBPY<@&>9a=CH?KJBNdn75nCJcn-(OpF{IJ9=VuDD{p;u=QqA!9_^KL zIMYUc@eF>cn#U6}ICT?)y*Kbwdv(QP+$!58s3yO6Q@|PJYXc>=Ay%kzP-A z=NwW!*KnRfJ?n&efkM6HggXEKLLFRH0#!}|99@rt?kLI#-SsFCim+bTfPmn~ezFJY z`M6Mu)50d45jLY&D1#!DBQ9*gC7}YB$zBn*;u~Qbt_$1oogm|PVF&&o`=_uIe+j!- zp%7r3$Zior4tvMAfLO%>d*r>03&h%5`sBZeSBS^c_!X}be|b=F7_X6#Jh)EY>v)5_ zH!16_So06ov+y0n;1R(@yd_dy#%;9xO_!J9Z4A3Yz}NKENz%a}0knAroD?E7CsFS# zA>pe&^u0-+kBP=i__LXg#9K-l9PJ)KGV==>mS eKuYD;;!}Fa!wjyF6{vB2hO6ZHD0QAZ{rv}mO^@OL literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jSocketIOClientV1.class b/bin/androgpio/customsocket/jSocketIOClientV1.class new file mode 100644 index 0000000000000000000000000000000000000000..5e223a6be3e524e927ad81765bf0726787bf34f1 GIT binary patch literal 19521 zcmc&+34B!5x&OX1H<`)gvLyrv5EwQ|W)i}_1yBf}K_EyFBxvK1+$4j^Oq`jp)dd99 zYF}%$R-@gls9kKUwgf6I)oRhMudj<;U)R^|b$_<6&#LA9&$(yr+{xTP@@#+4W$t#C zZ~1@U`Ofzp_~PH6K0`zc#ch5POqJ1iTQbqn6HC;#_NLN_?o^`nIxAhfr%7EK8tc1a zRy@6Zo}Ub+(R-qMqqSYpct>sH?mbp(nn`T3m`XMd{7}c_+a2A{RK779Kd`UUN?NHz zdwO3qY1Qst7_D8i8Z8Q2qg`EAlBvMji%D|FX-X$!@eZ`|x5VPHwAIGM%}gWXmetnM zmFQ^Eea5O6t%-OX^x%_zJzl+Ti>30ul&CM=Rw@7+ zN;Z;5h))a7ephRYPf?$=x)W&#$=4DC4@O5S5oa2=u{F_M+mT3gbXm3XRqdttLHiLc zZPxDIj+R(QJds4NQMPD%)3L7FCM%5=MNRk?P4~*LGjhMKaOpwIZxwnJu87^J0ks}&|qq)Ac) zvADIlw|lph+!o#4rSO&jbhby6G5M@t8tKkhiYW}h4Gq$eOt1lhiAB3&2Qlg;m4jl{ zC085xb*?vB(pB3fYimvNQ$XIWH>t>$>18Gbsn|~&n8puc4Sw2~V_x={LAsnao76~K zUb~2T@yEtqHXcs^vZBVwo#-uk<3qTg_>9OKKc#T)5ym>UKb#jQPNxP|) zDUeRslK?-Jo}@L9Wl}qJzzR~myVvcHrP5HjCz;Iwib26Gj{OJdI%Qrimk^+C%-W)T zL4Xpnfn(}$0s*?7$;`g8+Jcm#UO&NE*~nGSaBFWo9qR^Zdubo$?6A`Mjik-Zh<;t? zC+IOeRG`kB7o`34W~r1LlzfNUv94;nr1vJ1Zl*&pq-a}PQ)h3wEwL|tSt0>(aNW^yB3B3jsDW4mUcEgB9G^L}h*KLaS zXsaC2kjmN^&{0%xd+KeF^>PoS8_SN^N2<{-VD-r=XapoXk4x(~F%v!zVBTOz~+ z@?&i+DYcFiQ3JixqDrBIK48Pe;j23XGb?`yf}>|ldX_#1A17hXv|FwVLtIiku<9br>W-z;RzGk z#2}rf=S})NodrWOK4?0>BnKVHErtP&>H_p4%*ElfB??-A@)F?gnE-uBhGEHMB3VL$ zz9J>?Ri-V&VEsH(XBiF9*8s9C4SGf&eI-EOfDLIpmXJZ;k~F-;^tNHp;F5K2AeOHe z54#u=2NIai2k5(Sb(+`~%|w9|;}2keS<5b=LizGX;7l(vB?u3`BFFftN#CHK!L+@g ztHZ|t{hX<)KI1V%?a^45)m9z4a%)4=fp|K)f8G98OL>7%dm;(<@=Gjd)uBzPj?i+L z?yu=Le)>;Lq2h!%!utGiXkWQW87`aRQ>{7HM0F~>0m=#L0K2W=Biq(hmN z+=qT}5w>T=jan<oFwNJViDM8zwnI`7<$=EtJ30QuwHS}|l$w3*&6}4veD)4Qq+uB+hRH)z9vQ`EDYT{xZ!K3^< z5+KXQ$B2zh9?fGw02cBAi+3iV!xm|DLn?PnIoep0%VgI8W=}_BafrT1POhfIe#FDy zG|Od{x!mN5ay=azOCgv_!gka4-bgO^lCjAqSEvQIGn&eHG$pyIQeGjZT93icV{3H* zo(|AD)Y(gt!i}6Vv@#UnnM|`nQN6}mG7hW`$&J=ttkI!#C)VqvwKtaNO$GQOV4_7b zQfYDtbp$vJ-KfP+t=}O(R}Uu^n;+x|WA`(KYn7XR14~15bG}55X}i~mvK=j+awY1QgMocMW`#W&q}sN5%RQl zMw3zOsgtSdP+V=la07Njy>Vt`86cU9nWlO+&vFhR$fdX?=S$(yqz_*9%hSP(iO`(+-p=98ZPrm8u>o8%@a)!W*N1t7qU@=0!I61{0ZUojlpSWtlI3ICMd z9^@^2r8E+3LEDG1fN$_H36EDEd|;vgb{-kHDeP&UfIju8nZg31M7icb+Dhe1vdTk} zyViKjC^sQ|jmdA~7Q{66Zeusbj3$#&Z0`yxcde;v_A~ZNL*MBw4Dz+yDg_4zzx^LC zIFJNEiCt@8o>?JaA@XFp3ad##HN%FEsotZJcgn=x8K!Dmv1`J5A-5^D7@|&X-1s_^ zyBIs~B3ou^?0J$`k3`{lM&blt?`P0eJ)E8CONn)j9jTt+V-@06#y-(B9kAvxvRl zv@2S3aBj`AmTMw2gZv_Y(a&Fim&+zLl2(VczjlkvM<(MYe~G`WvOVe6P79HmS3Sw4 z+i^djy?WY@6Pj_~CEFO4Kc1Aoorf8(zsYoj)&Y8Sdbndn{{Yi~zw zI-a(RA51N7uxZEZnoYlHaJ{lOk9EzSd%Wx_t$QMQ)15*#Wt3e!aCY)gfqP~>X#O-R ze3ymf!lbS%m)Ij^la8`f{(s&M0&o~Ps-oQy_Q*POiSoSt>|}3|Yw^w;HK6XqKjNSG z`NuhRvW9pXs!pOb$v>4n8{D-q{ShmMUuUL~xM`3XYgJ}(W|i!XmZ^c3oDJ}}N-qU8 z`1#i|bv3X)kFJ;}p$b^QSWcBBt0@?WEHEH{S-f4YrAX+Z(ubXXG}(b(1+Dg@596ra zr`$l#oU1Fzo~|?BS9nq_(^Y>l`S<+G?6@cbGXIaszi_{t&vfxH9Fqs0F7x=|Ygd+A+zZe!r+T`6X%%h&fQ7O~ag6B4qOw>P>d73?S1%$Y< zW2qQU{j827Dv7GplD!l0NFJ?OJ+rA3b@0t78$c~%@`lDrvL=y8r%*BP*`#X!si4>( zHu}ZofJ@eWU~`qY&cYsE+BzCW+mRSgBoCNklh};a0oAR{E2b~I{ebRi4}KZ^O_!CL zxe$886CEO+yvf3&AOe_!`1tVga#<+}-|?&?MriiXi*qEy#bQ}~22 z$}g_K3;k|~DN5A4?b&x%o1$DyM7v$tcTrPJSMRRPzH2u{l?bDqm3?=eDVC`5_GI7n zm|~@Rm&m^BHN_=ryfoe+#%#dxzhr$^G?lVaesNIkwl4q(M2HSJfMtD}!Y_~rb2$nN z8)L7My>6=(X?6({=C73cZX6{q>s6=(U>6({-B73cWW6%|r-#n~Tq z#c3dQ#qm9L#Q{A~F zb>W&5bZPjLRR1hB^wF5855)hTK^NjplK z`|0X_y0)L%B%>Y;I$aH_yc+CrHHdgM=yElv@oErvHK_G!(Bo<_FKj?Brzm-v_V0wU z4))WH;gZ8A=!p6~CZ+EqDg6zA8sT(d0d1rpZNjn8MgU<8)O`hoX)7(HCLBB5hI4RN z(tWg@PSFnfE?q@`qn&h)uH_M^6PHpams3n>C2UJxYXv%>+W=sI26wju!b07>1)%fM z9hkX*_*D$VbSEaYQC5qqL|Fj@J?nAY2t{e3dfDUBeSx0kFuj%j#qKQHF^3;>G*zFX zw>>~5PtiSnR1_|G*Gam+7j^( z2XjRA`O|4S4){L0+$cAU&(P&Nebr4T=rJ`?$R6n<7zvH9F8L^as-2-C<;E%c`0|4C zf_^%&ys*5mo(bM!ndXYXxFM*q1fz7|tFSw9i=dn2AFcIe^ zCi5tsfflOdkfV*Fe&LroRB1 z_X3Xp4G&m=`43^P|Di&j0>AqT0BtBh&Z44H5%Il7>-}{69KDSUKcOysx%%%_dHd^G z0iaeM>$5S3^O!zksmT0ub&F_Nd! zIG%>{(KBco&!pKr%Z2qy&8V~)IN@>FIpTziCSWtJl7Lc&fMR=c-;V5DBhimIg1M@x zgy*=%F3lYq$H!zLT#wzX$DSldJWFK*rwBX_U}U_BJmp0^pZvUl#_&Qa=OwOrCgsjk zj(LjVMr^BJ3n4;hWMYE4$fwv8q=>hTKdAJ%hKjjf4LHtjs|S>efpjT|dgXx-J>tcB zL@5h7VyU13-h|h?1}k<3D~2bOgRzXH?ZG&TwMo{(Ukpc1@`OHm)RDv8@-EWPllrhW z*tb)5DElhqY1+U_d4^+Ixob-)&%zh!GO)mGM*sR6EHjIZGNX?|a<17A&;&hN6-L8X z*;2YEIoD~9>90chg(gqWAG}dWavP5Avfp)cBZ^+(KI>j^tj{YJi+rjw@h#Yk>jj z?K@~WFU33C%*If`tF-!cx)gTBuh96Si4gZPM|UrvnM6Z}7WX>Itun27TE-CfNPRm+ zaxKML%F8vcwul~sNtW`89BHgNzcem_o{67;G@gVso`N(!0cm`aDq+Mcxt}iOlVtHJ z7j8Nn?mIO1(nbr(sI-~7gjZ`o-iWs{ybFi>c}*Y9j9`iCqYAlF_0c%R43-t2Tvqyd zEtqm?$+oQLsEnTnJ)Z|XFDM#9_J|G*VZKaZ7qf5!?u2q`@EDbl?*_hHlkz$idKph1 z!mKLVsj)+yQr>7|M1e5xJjuFVz2?tD?GRY8QlT&I!EdMY+~Kf&hr{+Anr)&x96RcE>}bnC zJGx!j(an75g4#OlsjXw@(bhRC^Pw{C6Expv3_)AB=W6RMe8)dgTVQ8!Z7Hs#w(iW; z*1rs*Eng{B_{P$5-#BXYji>Fta=O*Ghi>S z<6MC8?mjA4DAa3FKfg=Ut~NShtf&vF6*Z)uC*m3VC+rhOtLGq|--l<{+NIW1-+uD@ z4$w&7LFo1ds`A}P5#LR;)ORyo<~u}J`3}4E-0Z-!*@0&>;Q5f&GgjBgs8PrO@CW!| zt!hIX=8-pQnDw?SR@kywWEi4 zKc~aKU%GUB#9{e}!}1Z$GSLyoBHJB{Y#3;fhm}Qsh(D}#d`RoK#@6v{o39Y~`beIR zKb}v!a-6=Gp{_qeU4MbP{_0XujYDsZLvIb}{bbe@Bhb$Y#7ru{=YNKNz=yYfKCbk0 zGFLyJ%I8seOrwA*j6zzDonE66pzTJGZZ@u>!$$KE^dtElmVQ3%1i6nwKeF?BTR+QG zvVkh?WW#s~xRM{*Mi|kQ!?r%FY;7#=o>h0!-k$TYH=~vOMjMSZEGjeFX|~ZpRYoVx zHDX|N53Mz>Q_R^Lsr8OB*Q;QPRk#Ezt12o2jq;BRXiP;xv7~u|O>BuxY;h@nK5~j* z$ctEw1gPi%71z@^Bkh`Tf-~a;*Nl2+`xR={)46j&N1njEu3V6DgBs7y17>nT%wP5F zd~9InqaK}w{9}N6=DpYiN=D_1JiCwPO0RBA<@8(^BIu zMTfm_Smw~N3?aAN^I|goHtw8#!%|R$+K{H`Zcv0RKCJ{Pkv>{FC`CHq=_^#50Gpzn z1vQ>j-9sykcPgq>_K;RORIPNVTB)hR)cifrXJ-$AmldE7H75`HYR*TW&bJPUzWYI6 z-!SNt`>q`NP*>IT-J>gyVIw1R)e|B+R29R!F9vIl|l&PSk5HxGrt4}idjLEv!^_%H~3WVi(0 zB9BOD0v`nB5`nWKXK8xm%*x5{rAfY3kI|S&#r$XaSAA4a@%T|6S4@8B+#f|YGc{eN z3yqJGVSEhKe4L7nCup?sB$XRa(Ini@FiyHOJj)T=EdEda4W{$7s}1 z!IJ98nTO8(bh6uxs`7|&8iaqEir{04jWZzpvvBIq&_v@|nr?iKW*N^xyk}@023=r$ zK`~;dX55bE4~mguqz?Y58JVdWDWg*U6BZ*oH8Y+H6-%$tSnR+4Mne3J3jRhsI>7(M zFKg~D0(a6q7Rm(8p9kdCjHeX|_>MmR?cL|E1N%Jg>hlU?J_=#{oOxys=)>z=eO~3) zJo?c6G6U*fHSTcrd7b~R`^>^sdOulos2{La;4it51t*)GbGX=~xx365*kxX}i(n!1 z!yyde?y`7bm(@-eAw=-U%D3i|;#YoOn^otQX zJs7DtDMn}0zbcxe2jPF-jRM+xXc4*n#9{~VixI>92D}C-qWpvna}-}*qgoy=97*sFH-eL=BxXl&Cdj`yny8ZdOGgGcjqC5|~bhiVBcb z-7lsh;bmvD5IkjxQCV^n4g4rhR3cw26>k$#<%p?qK zxm3)ushE8}DxLrpPlAf4hCoGSE)^)l+f=YDtRkgJM`bxH9O)ORi5`(P(POeY`kE5W zERCfaMHF3KDrzAbSORKgnQXIs!vDukig`6>X=x1{+Jc&$%L8W?`+bYy!KRi6$_nS( zB`(xaN1#fDvHQh>9Y+hftgy`g(77LH#jKLf^l8v`2D$ss;%}aL2Gyfy!RT`+NuH(6 z^a2WxFCy&x0ww5+bO859=xZ)P9Cg%jR3X+t+Y+$|ORt^lTS5h^&e1d~^2?e{zg@oZ zi^bTJ_~}&yu;Ls%Uctbx1Qp`Ef>xm8{0y@Axr^^c zhwnyhsR3FqRyZL?5sBAm0@$4iPH@U%ASYl#F*8I?Rt=An-@@*G2TpzuPW}K+{s>N9 z1}A?3Cw~Peubh_?A6-5eCpa6TIq8K)q;XHdx;j~|tK(ti(8VTM6juu|+hAZR+kyah2Gq?wZ9l;!WzVMeG)>>aI<6h)#7E6J4TP-NnWABB}0D zVz1aI_G8@L5Tv}~ACBnj7Ja=|Uk~c*o%(uQUr*Xsaex}cn=g`op~w^B1}YLaikrpZ O!Xj~tI4W+X^8W<~C0@e- literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jUDPSocket$udprun.class b/bin/androgpio/customsocket/jUDPSocket$udprun.class new file mode 100644 index 0000000000000000000000000000000000000000..0078407cf8730614f32464a2466a3eb5e38cfda0 GIT binary patch literal 2441 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 D3toCp literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jUDPSocket2$1.class b/bin/androgpio/customsocket/jUDPSocket2$1.class new file mode 100644 index 0000000000000000000000000000000000000000..f51e6526d0d64d0dad4fbd50194929661f0099b4 GIT binary patch literal 720 zcma)4T~FIE6g_TB172FP0)w#+K9)i07#S09Oha3@X-EMf75eU`UK0rR!JFp^%=)J9$Q#om>25DFqB3n`dc~_MtZ#+;eGsIV-ZhC z%GU#ixu$;`U?(+^8tiU#Ii6w3!gGezyA!mroX z^H~MNB%=eFhMNADy|0us?N}sY!hVy?Uz5nzm{@Hj XR>lflFJTogXs=LMHMoTMNSNS%5KE#p literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jUDPSocket2$2.class b/bin/androgpio/customsocket/jUDPSocket2$2.class new file mode 100644 index 0000000000000000000000000000000000000000..ee06e6b286599fb526b0c77e704cc055c6a4ea9f GIT binary patch literal 2467 zcmbVNU2_vv7=BI@x+N@aC{!pFEf_EflxRT(TB>cK6783yMO#GNZcfu}x4Ut7L+Ol; zAEV=p7kcf5`~TyYG2FpZ8<``scSl0i4Bc4J`s0 z!zp`irE0kav*rixs_&XBGAJz1UYah2WPhe#LsB5UY}_&mw&7F?Qwz(|3^cR~90|F# zz_JUb>zFmqlTJ_=HEi2hu%$q2uw?m}m*{Bxzda2I?73yw8QV1t+o}q5jyIf^0?%?P z6w#GPV0aZ7D4CRcT{Em;)fW&&x*W6|D;N?O&pwUMoi28Ain(h7$x*i~cY{&s(9ou+ z13GpirKo3g?1`vDI&|z!A%Vj>+R-DB$SU=Yam$gDwbcdb%|zIAj;lPc8J?xyV`fqT zCUD{@LeKOQjU-Lj0$thM^`^L^3Js+20*-6Q2<&U1zq)QR5nsm%ycnTrm2dhXy2?(I zl^F8edrNRf}>a@mtUO`SrYz;0G#WPQ?DW!>|SvJ;u!rpzDQU+_Tw^DQh=`9+j)X|SK?7`4OlV^c5v7_x)a}8(PjnQKcujqIcud(4Q|Ee_1 zrKlvmP1bg(6^prf4d(?8Ztu#Gnq`-z*9I1(ZJKR5DztJmio3a{MuG4#=H-#}=(eS3g{zjme9|-6u z;w_F1p`2jtj0&rDAREKg3=i@Q#LQfn$x1dfevFaTO&h8>UH5 z8KQmue~x61Sk0=WI7m@aTv)}adQvI82i9hejowIk#C2_HI6AK5eF2Soq9&&nIjhu7!LCHmIA|EnJ}vHUU|60@qS<%)NT1G zt}%kui=$+h+?r>~F-z_6bkoP+v`U_%Ua~5V5!5_hk2OUW+l@Gn`zj1RVp9z<)`ZdpeSw}?+PPy$y~^Q^BeN$wnXF<3J^Sy(u06h?*znnH+|R&gh*x#)EO z^rGWPZ`3w?U;0d*HRFF5lIgVC@<6&%?fCHIh7V8fcYZ<>{I{cn(rHqc&_XTXaQ@^5 z_T^7)V1K@E16}#9Swg2Q+J@Lw@sWKyn~rZ())hLxw*~@ z_b{k5Ht~ArC?9>BxXfb{Z*O7p4)RZwoT*Fh`U8nM<*%zWm+aZXyQRB?B8dyQh#T>F zGdwFC*+iIdUIB-Xnf@od`4rvwjA?#Ooi8wiFHyl)Si;x%7T<)R3=twG9)r??A7NmD zuq5#VOq6NoC~ZhA(*6*?V-;#A_*axgYMI*8X#E@ALPOy`4k4*QC|cv{Pw+uf+^1wS zpLEFQMXM4D6+o>qOT+Am^e~IFLf_X7?W5J+FJt zch0%~=VyQW2Y>_kvxbO3%CJkWQ?8g!uIN`iXR_)PuSqX=_1w{?3jw(=wO>O_p#7>b zW#lZwF6T~9T$M#nL#sefuGd%0o5vWy8!3bcAx%xY>c4UInBQon#c zWmqZCaLdv&D*}nphSh@Sns%8y>l!>2$Mpn6o`%%sgdxyPLpQEpk*=&dm%ZzTD{~VE zjNH&5CF6>zD(#YV7wWE^h#aj0rfqsp2*~tm9A4^Pens<5yrH??^8&G9rzF=v;Bz`O zv?=QII@TbrsBRr=p(|>$jz_RgQ9U}=qdkr&w(96WQXraEiq?;swmjuePDuA`E%u2~ zr)XH`4cAn^!_AmVh`^3joG!JW0f|wCMeowR7ndF&jx;hFdIdHHd9sO|JYR#Q62o8B zu?xEe)-ahvH%=Lo#6u?Cq$SK~Z*6OlaKDB(bW6N~lbd(SI@gE|i3ivnxS>Tpd&mVy{wh@%g*dPHE`!?CJkKxtXWv$0h2 z)zcMe%TgN%ovp)`QzcZB6<1D~j$ciZBN-Yq9b4IXQpX^M1lo*Zk&#MKRp0-KICZQ( z9My45fuThimuly@juT*I%$qnYuya)=de3WkYCiOqD)?HWhJ4dv<+Ndp=dQ;t={d=Z zah$|y1;?iqRF+9GeLRUXItn<;A~PzC7NeeBj&+H2Xt%!><7PG<`*B8W-4-&WGq( z>L>XxlK+z8zog@9_&Q_ZIkmG8vFW9f{VHyTj)~^D9Z;Jk9TJxt!R!-wa#g`>V0$5e z`&E>$2<&+{)KY$lNGeKmO1g2Baa9HSnm}Q74y@D;o8lbyqo6`2;b^d#{uShORPap# zpz2Rdn%)`TwrkyHq&b)K+x^QKsqHPE86&>uyOJ5mf{nuquv}DFZo?F-HN@Llf~^hD z(f*~Re6A=f9t$dYBpJs|d|Sh}Si6mhk+&>aHmpIn>`!tm9h>LZ@w@^3Ck4sTXnHrA zxL(GOHT+1RqrsK8Jz18nj#uz1I~s?TFHaM6oeNPOvN2RY#cLXVwm`>_<5<$Lm5!e) z$qUUW4?nW4YgIpJJS>n4YDxzam;4R5HuZ7xM?7}c?#9~8)ZkbYU4HCova%$u{QgM6Gm6*7j7m8178ZJa(#Pw$sA~AFz=lR1>R;lVx4+Y| z9Xp7Yg5$eIIby17O?%V*Xpa))lq;BJ+fW!57-@1bG+5hN4Ep(t(!esq^$DJ3{V`kB z0Efw#^sYGULLcJ~v{kQ$unjGL;c4Lem)<(kawn#RV=?rC&pdZy8p z>5fffQ>MFR8e1~6c=RviiQqB5TPX>#5!*=Xil!HjBgIvyT<}f3ky-M^xOF=^kohY* z=dg3^KClN_=S!U#@Kpt7Q%&YPpY-#!@tH9Q((eh!Nhf%*gbOuHj78GO(c1Jd<+N0HVlXqhQ)RcrXBFbPCPHtxFdS;j>zCWv5We$_(0_Fso0AL zVxQ;``$a+=6vxFOaY7sxH${H{r!xw`;&uq9h8=%j|UhU|Y}{FmU2Xb^0S8gBX;Zt;16 z&x`xo2MOZ$DEDj1#c2CCL78kPrJ&xDnZeypNHo;~Kd4%W1OmSgsQ2lf+PQ}hgWW!S Ugg=r>a8IdGe`Q`={)EW?0I{sjy#N3J literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jUDPSocket2$4.class b/bin/androgpio/customsocket/jUDPSocket2$4.class new file mode 100644 index 0000000000000000000000000000000000000000..44d22bb9dd0ac254fa396ed3d2667885d8aa9be4 GIT binary patch literal 1859 zcma)6-BuG<6#h;!$v_w-rho`i|03oGjiuDKf>aWqO`C{q5F2xKGC799Fq1WNMw?#u zC3@4fdgX1qR?*TfeSkhfAEtDlNdiGxOS5L??Cf*)-?zVg{`v2(*8mn!(-C4wa=YO= z&9>#_Oy85vuIHFfgv{+cSiN5f#QEfc4vitc!=Lh8i`&iIMtw(^Qb&|wCeZrQYUNDF zHhtF>w#*fHtHtXr!4Q?(mY2LqflBX=w8&sQ<*lUTZc|9B&G133bQHECUCVCLp{P1k zuQP;dd4|(RCT+)+46H~;mMq(n%M5o??-TfhFtvO!y~UsvoQ4<%rD8zGkdnp?3?rtb zvj#?BDCwMmQ#c(%7}Ewuah@TZQs$#2%NF%hF+_lvIo>EgVG0eV4 zuE_;LKqD6|$|RK@%)Hx|MIA{>_oy)|uFE?m%abu&!E_AQkk*l6xcu&p7|0;YFjNtC zW0gzJFqx|5-|k5<{b&T&k<)R58gdj`MKy2}9}ynB(NGx=829q++J0;hDLTY+!ZmOgE98|J z<%^BS3=^qhdLZH6F$#Rj^G#EDUYDwrW%pDt2&!ns(*9mzz{4SjCyvvg)9zCt`G%*Z zx2=~|0u{;4CuQCa7^EYn;kjw7ou%buZvRT_;=>ODD8X)j7Imn=Qwq z!7mHB?KBL0k1BDj&&W-u%t&3>@b=a@YgnVBz>EWasuV7sA7z!fa+-`}N@8 z=qrS);p{%<8GgZdwky5Dt$lnJ!gIRnD^*R||B0naHKN>AextxzknEI7g1H!C9$j)0 zIp))ldbrCvK>-&7ApBkdL)b+g1uEe@rU{faDtVoJsWQ8JUy}3{Y4V61;4Gp#m`?M} z=vxFXgbpx^P)`}w@ijf{fX?@IT2Dtye1+gDQG{}kEyKN_6;~;O0yUC(f%~eE0n}+j ep&kkhz6+!ZHiPv6wgT-nJj5fC)F3D~^!p#R1ijG! literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jUDPSocket2$5.class b/bin/androgpio/customsocket/jUDPSocket2$5.class new file mode 100644 index 0000000000000000000000000000000000000000..37c19843f0fbd23fb540813e5c65b779051d708b GIT binary patch literal 3468 zcmbVO`*R!B75=Uzd!6-$I0`11;97taWD!RMoHRjBg6+h_t?h&;35i31wX}BDS?{X5 zD<`HDngT7MPv|qy(kJ*QX6Uqj44KjgGte?TI@5t^2WATYM~Cp;U0K@r0il*x_vr3< z+;h%%&pB^={PHURIx(%FMxaf%3$8O(G@VSol(J7>D4VuAF&L9I{%dEwcScX1D|av zE6^~hTWyw;*DbRskQk^M&H1irkCA0{g{A1YzJTbb8M!y23pCTr)R|L;Yk1D6e@1tW z%t)u6>FK7RDw#aPE*S3RszZOsk&qk{`q`qMKdp}$p8243q06*Qf49JB>PmcAdgT5U z&lKS;pP|3~s6f2eDHy9DaE%59^^&?)!z!qfx=up_G)Y~rVKuIj)E6|YL8GLS8rDKn z5yOod61YhqmXa#h44AetRGJtu+{0mJ5(6^IM|Ibfzss9(nJ$4%SKw&dHU=zC6_!9# zs{O=LUsT+J4h5S9*6J0MO-$wKoTp(6G6EU{ZWiwMg4{|y5iR9}w-2k>irW?3CUA8C z2b=ip=T~>3z%)};C$Cse&iIIG}`0_!V!_v*er=IRq6_El`h zP8r8L8242F@*3KYO13V^wu=!-F@-Ajpj*M60ykDYU+I2n=s~Z*RXjg0UB7prXxK&} zNZfEe_6fB1T8>BTCyTBzX*wk@Nxo#SW81uirejNMcWKxUUW9r*pJz8On4F_m*#$8b_6@xB#s$gCd20~&OUuqfz7 zriod)p7~w^+On~1J;)(WO2JVnI401sGBlT-HY+7f4dXb?`1nrf3LQu-wMisT2yFRm zytS1I1XA8GCkl!Pf*fL|Tu*#%B6c6-Et@$c(7GvV**BY!xcbNx5&ZQdeq8SVJm8Djt(8k1sS$ zc_EJ2r)}qqz2EWt2;aAGUcnPAo7E%qIF4oLwuUG1Z2^U4vt%3?Wrs)&N1;$=V`Wyn zQcwwqzhPD-R)4n2YnHEIvQ4{FgoW&%mE$kEtW?Cx+7ih?ZgE&>_PVP4BKP^Swfb^7 z$H}oe=d<+=>cw(Dx*X`0KCM=Q{u4P)^Zu+ujpPdg2_n_gQata!Li{CL75{v@f z^0qBMsq~i{O{5-Vqe-rWLF;D=aC@%`;e^Zka)Ix;Z|B4e93E8MLIE z>SoZIp2H2lCua@X_+3YNh*@kTtw|c2__>*@kXZ?}*E~tSIJb7iy2PLuO8*h-=aIfZ z#Q}Be;6-ddPP%#X9Ci#%W6i?vIb?@AUc>+5U3}X#i`^HnPEv`z^XLn=z9g59c?`;R z9(RY!5xHE%vEzy1Io!)1&9k^ak)MSzi&K=n2um7l;v)~y!l(5)sB)P`-P+m@=3tB1 zJic zV3yTOt{<`*aTT)U#DZjuqM1Q{nIr#24)d>YbpJCN#b0o(_$xMwzab@FMW=WTJH+c8 zcK(jN;vX0kZ?LhxiF?Ic$cwizF5ba;@lWdg7v{vf_)xrukHo))D*huH#QS2a_-~Nx z{Sq!>S2@`=B8%_fyG(an?8Nu*eZr#-FX9Jyif5Z6*VFhRsgDBcM~I1rfci1_enPOA zSoJZFr(mms?G3G$a0qb)qP&6HdYtFKf;|dc1+hz1AAMi9=)Jha@i|sj0BU*2RB(>Z zW1AYgiOZi-{%15B=YcN-_}@rMqPI3Zi)UXV5rH>fNO;!-0>22T=kUv5{S|&4?C!yD T@I0vmxuhETXK|c*0X3fhT0pGx literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jUDPSocket2$udpreceiver.class b/bin/androgpio/customsocket/jUDPSocket2$udpreceiver.class new file mode 100644 index 0000000000000000000000000000000000000000..76e355001de42a5ba9c26b65e829ebfe812a69a9 GIT binary patch literal 3072 zcmb7G>2nlC6#wUJWJy9Ii-8aoXLmcBg=BV}nGMAI zzE3tEqF&r9)SaKAH3klnPYMQg`HS+e(Z|0b|TM=w@!`O2pKcEA5nY$lNo_?Vr&hppPXi zf!ZF+o;WZf9qDF=y#tmb+Y;?oTURGJs%$A!du@3jWqHTtrAy8vt5h}ybvCPC`ET@r?#sRf+ zo{5F1P^$|}7${S#3r&u?W4j7YACHX;brIkSdN-KZf%q(>7`hGr2U0;rexYGZZp1DF z%pV`~|7J0<8-oN`u^cgXia`&u`q%F8uQ_DmCfq!G%{2n6=6B7QvU0D9TS0%$Bo0v+ znU{FcOGy(cu=>r&x6fNWo!pJIi4kN7thDrcq@DIg3I&8$>&AW)qZp$zxThkIx%^+YIE+PR|L~lVc_v5L4xr<5&~PY;Hmz8cdpEg2n<#Geatv(~Z1|aU2i`Q)g!? z#S+c)me1sj3POxSafk7oz#Rr|XId3%*gy77+^Mp!f@f{bx-(MBw7X5*qs&&O^=BPI z@IE|X;C_L{v&ArGdonE@RdXH`SQ?21Gq4(_W1BL= z!)!9Pw{ygi77NJUIeamPD*~iqdD=4+pv_U`iX?@n$4xvDgyR*8_>_sKgCJX5hG$tj z*@1Ty+iu;kl^K<(=DJwl=7UL@^DmM^m&Jx3_v-4(dHt+DsLGIvFp{SGw`TIG7gAV{ESdD+?+17U$c%8iQ!dYAB zL6sD<51UryFaa}W+tS%MYPqf?oEHS1JE&tX*7+~tuTr&XKAkF6lP=6`(VU?gImwl( z)$>au|6){eT*9xf3bH_Fv}qF6(NkC&)hAIKtqD!y{AkN0Rz#a8aZ&R(sE;SSzfqrwMvtwUwuZ5aW6GfS5(C0OgMsx1bV0hnHZ1qe z5ho~DO4<)6@m=&2od0=Q&uMfIHZ^^D3cbCgR8OP7c?vt#a&Q`ZLbp!i7N4FVefaaT z?VR@MExP)ehRw;F!ohVRF78@eLe|>Da5!DW{|ccipZN0saEo601@u9^B|aFc4aEmb zYD%VYZ+r?5?K+7y+m#6QKc@6Q>B}e7q>ih4hTd(N!gIcVr}2{7nZl7HXIej|p*2MF z>&z?P-CGz<N7^YZdrNTZB)v zN_i9KXnz5W`7h!C literal 0 HcmV?d00001 diff --git a/bin/androgpio/customsocket/jUDPSocket2.class b/bin/androgpio/customsocket/jUDPSocket2.class new file mode 100644 index 0000000000000000000000000000000000000000..3f4c99fdd92ba5baf7e004fe90f058cebf08fc82 GIT binary patch literal 11620 zcmbVS3t*Jhl|E-?l7BM!0>5BrcEn z@7#OOx#ynqoyWcZeCLB#FA>plK5LMR>DoXz7>#uGh9Z?6eX)3?Cl=}0YsD+~Y`tl7 zvuZ9i$ip;gPhfwbvO5s&s@&AR$LfePx!MCv{>DIf;J|JxYQ-X*@dJUVRoT8gP+413 z&6H*Bx5Ah>r7=a^9FK;=T^MvVStCa@TZ|a-{*FkW5OM=kcD%nc5b8#EG|`)BfW^xyoY-94&MS_-(+~hYYpQhyy(=3y6$&}V?ldjRNxh74}t$8L*)UE4GnnaUj z-h7j$&}1Jiq!NRQnI@;Su?5=<^;jmAQW=x4%Zl4QO!>tn4z=0c)dp2C6}GN6Md27 zP}tho*VArATLSIf%B+!&K=-ylG$h~cPEUMyC*DT}gww3^l$v}Oc;^*obmsFo==9%+mmu%dN=7#5m3Lg_X`VRDm6^)lT9 z`1T0@^(GnQl~#jEIpp)9b&E-jG>ysZ*ll&})u6+U-R*&XrW^&)aG(cx+Duyvx)n%B zS*2&715k^Z)J!elupc`Qw#AgQq-j2}&7B58`O{Ni(gU@9q3)m+6`^;S)JpIO1Kj$o zO`S{&oF=p#&Iz;Rj>9`4$&tK`UD`D3{DMLx+WMz zWCbifMfYN>5WRL_BQV5Nnu(ECv54#-hJFvx{U&{y9?&tyW(orqm+Y$b(q|CWM#@=e z^>?PTG300!-G|9rw{GI&IK%BO-onB+_jK!s40F z3mSGh-OnCGlz(x2QGTh9UZ6qo)GshKjw{NK-plBo21y0G=}RVkna%>~(Y|m^Y#aOgFgkkU~YKF?XKzFy~4wYcM@jw*t64PfN2!!H2@FSyJ zQjF?~A>>$5KoA&il-`ETTC>j8Y`YoswFHdSk~Pt2V4xAf!$)5wzmJCL6@$*BMo0p6 zqZL08iSBI($E|2*paXI0Rk{E-g+S>;5X+xPAc}i^xUa{Gq6mqEt8?i!`no}v08Yg_ zeS%5fpw|JQu+<;0@3wkW*G4b`wCVfjH)Z|dCF%s!* zNBFZ{UPj+A={w@F5(hU#Vu=uwOK;Mj8T6+ZNXaLfG3n3gFAybB52R%STb`1gvbVo7 z>91wciMs=_rbyJX_kf`MJ%rMfY0^T=TM}CS7FAnP;kp(}dacOyeUpAbZzEpCR5^fv z-5}jRGU>30A6|m2$OF0>DmQ1 zbH&<|M-GQ^619)PK7%KwVbPWVbzowx&54?_pVG%XQZpH^^-)f0)MAj&G%C5L9XMVu{naC||XT ztf0Y5GnKoWkvOclkC*Ur+1CmH9Bfo^5a;VnzJWg`FuE`wYO(#ZRX{UO8y<_?ET$`1$H%(t0*JKuq#K%ddY z_fY2H@}rV*~|N3)G_9UdL1*N#}Kpy?Ky3beij5G=cY89={SiQ z9y#MP)r^hQ2HQN2)Lfl0soVoowZ+tpsN5^|#SA_)K9sKT@j?EiAmUR@o#R5ObL0BL z^rK`3Knpol9guWpn9vjG??o`j;an*b<$T{O~jQ>e2Oce&xX zzTabNa~4Z>=5a`8_heAwPN%0OQr2insg+_~@V-Dt2d)o_7Bk&2E)_YAWIJez%MzwF zABBrEYbEc36Cxouj`IQ)fjmL9Ob#Q+=2yLSRmMtCEl)8(woUY|Of|1p1%rCx^@4ih<$`+R)q;BB#e#a`;u25cjMo(SoQ`i1@}Nbuq;!~Ol)BH+ zO!+8~k2&&DC?D6#N6~p&aDmEOF>%olEqPh7T!GJ-O1u&dOUuqtb!qt!-8e*b=V)DN={eefg>Fu*G7V}V@W=v} zN#K!B)#RsDYK210#a=-qYJyC%t(mkDN^{{aF5T@_kAsJdwUmC1sxQ+fJl373O~hrMeVBGlxnqd#9HQV5b%`D_Oz3t@C=z+yBwEq(u7T0kLj5(glxlHpU+>UJ zi!E;$vmw!MFwC$=uPx?wJVi`D*4%fQ^5ii@vEyX82g&0(>pq)YYl7N;Bi3qyQ8zi( z@H^JXCU^c?aAfLF>Y^E%kwvEJMSOaZfl@w4`%-(E0^1U81OM%Kht-N3@STn|raIPe z6HfrcyXhW$3ck*OR7_k^rc^v1kAqDx^nEWim5QdP9EOtd{fO4$Fg;>i^u7eC40$t# zx_DYe#PoxUw*#6YtHssD$a|(!%EuKdYPoOb&yiywvK*J;TA&B??ZY~ zVI6%1eI9VAqL(0|p!j9_11&UP#E5}er!UY~A@sSX^E9+^klHFDI!*=BzVDK8isqC{^V_e`pSWoIAWd|B72>~SI&tM9 zc&oY3 zk`1oY>EGzzG4DUXQR0l|@lKM*FVLqce+f##)KW6ur-=r=Y0zu$(^P|o(fBPbHt5iM zl>08_T*1p>w}Iq}IfRe51b+WX|AkMujB7sD5K9i*mTZJ2n+7QcMQTfKgo6KD%z2sa z@Ov(uq#XjzVfs~7mV0IPAg%CcP5HG(rr(pFz3e=_3xMWlW3DH7n!Hm!7^LhxrsnhP zf}OVCm(6PMxhrn~=m>V&>{w4(_sAd>XlKpiZ0)Tn^OlumHt%7)4@#J~7Osr#U|X+3 z@fTp;iwNDX(OkL&^L`zv;v0CK{5pdAH({37DTZt7Lzs7%zD-Bz4ZOX0(_z+O+pJ#t z3VUHpaqhd7Fjk%eK6}XYUEs&0I-s7gjr3P^(YdRnAN4sIt@?F?VTjI zU!YHn`4oAR#=L?sW590Ov|?Aj#=s$r=n7I_#*r+H8?@Ts3HZlXz%=8u>=DyI;Jbn# zD;Uvh$qKpT(%%(iW&l2u=xq%dBsZNaChIVstHL->KI=-)5#I%}z6S%mr4*_&^JH70 zvR%aQiTK3_N6NIX3w@tkzxUu7hok&(DCo%n-{#D1O$wa&s8 zu{kclQ<9@||NBy@LhT=xPZdw8{mL%Lj>Fl>G&TD? z&uebY(%Fg^w3e372H7wdOSkP9IP>rPv9IX~Zr%L2YH1>DDp?wb~23q_wQ<(l9S; zlF{-ZURl}-pm^>SBo1?x=d4aTd3>YFI=m*Cbp~l^X&%?2oEW5Hv~E(ZIcTj@t*K~j zP%R%?H=`x23xsunutsr(`!v=79BS~tDRL|T%2H#AR`4*bRjWm~=sE9XGC2ywd z`BtjoEmY6VplYEFycIEMJMG~exKVATgS?Xt^DcUfZ=yf02wwfR$3INw>GLRXo}r)fDW1&F;l06WT#r7_dvQ|l;}>z0c82feK?Sqh z02u%y0W-eeb{grkD`)iSP_zgV96%!XApQH5OJ&gxs(?%Nqwk1n4WM;VwKkz8ZZ?f_ zeofm9I=0WC$M(U)mMmPjQ2Ezg{BhgA230Zfp$COLzFTYAqe8G9cMr-bp#a4eAo__k z@6wU?9K36Y*78psUR9UaE@EpU^49Ua5JJ4Fj_*^EH`77EB|o&Wh2D>#AoEwiF|IvM zi}9$&{|)ROnLH9FU$>ULta*s<-<~=XDkd&JEqBdSB1#bjrb!W3010yPva5>aNPl>> zW6N+7GDuY+2koNc`XruF2HTn3hSDCU%vABogI0l#RA|jnt!%Ukr8QDc6|7tcnyxui z?3zn0c5jnO2d3Xk6NAhe__ z4OdR;io;oRCT&A*ep-a&XZR$Zxk#(e;{T}_;?ik!)B@Y(c*>s_^n(}J&ldZ6(0)D- W!WY5n3~toFupo!Oi079VHf7=FGauwk>HA_76FqEtDksT2=|R*6I;8X}6N_0ZZSS(3%gF5TTA*yCOA z`U85^UU}Jerj66u>GZlkq&NKwo!aNyY!ak&riNj^(|dT{=RN%X_s_oqn8fD_A_9ZD zU38t&y6L1!>s~5de5~8Ex>cxJy6?COVgh^D^bI{_>2@h~V`a@K_yUo2e)@AS>Mpwa z)@`$5ObfKXY1*cLQ6M@px-1Zzb&7_HC=wbJ#0B=`Oxws;D=UV3TVJsZf$p4B(5+?N zHRURdgBlKDP@vPRzs)&rMQ7l#k?iQQ6gaHmNJDwH>iJGZ-X7I(tf3~UTrm8y zQl65F1ks3?zO6GdcIkZPvaU+N`+3MOWP&OxU73dVCuAn zlvLU$m5OV0K5v%FIojK7}f1x>+eAl!~GO|StH*t%F zlr9!oY$Nq3w(E{AOYgTeybYO`vhL*rN9>ooY#-8JwdgnXlIdV#H#cQ7wOY20iapY~ z+hTb)_X=)O@eJ?WgKH`w&v%0(;Vw6bOBOr>D9+A5Cim&tBX-(sFQs7$|F zq+yX^A6t%H3eRzA`>taf?K8t1dHj)D&#Afv3OAunG@!Q6+-XRuOM%p!DMwS!j(Kxh zA{0p8s@gssESsLmk(IV>$0roFhQP7i^L9Bsyi|5vU)DD>(zc;nRXW(S!vPy1Yuko9 zYw4b6cnUri7}~YRwi{INsX*U@74XAF^tXuw89jzR9)dBefXZwF5)u7YdZ*i)$A za}$9U3V}kM+-r3KbYTxqY=frqH#8mL-OaIQ*mvhC_Wy{Uu^;OHHT2dn&^oK9+w2Z zdj9A5chrjp)C>eH4iVQ;o{wRef1_L{IPWE0M*_YlLnLty^>^?t5e$?6JGe^>_bAna z$a6xEDG&;$h{9l~A#fk>g=qhR8raK+Xs6Vq8sa^8XmG4Q_7v}r^|$leAFpAB0ZeI6 z(3{j6AxtHbYBIqniDZ)r|z0RGC<9@(dK8yp%V-+Q0le;o^ zGVyts+{yaNV+{`jcJr{fla-c7g}XTITnRW(X;}$4NCX@VNDfXzB?tUPqtPZ4-L%dj z6Ed+#yE1QGCwgE%{PN=97hIihc~dV|HNbL-wr93wpsR_z3+u!ot6qAScq905SL<$Q q-TjyKE7YC)pVp)B18h}na81zjBmQrC*urC8$RYX>KEY?lJN^L-fgJJx literal 0 HcmV?d00001 diff --git a/bin/androgpio/gps/DataKota.class b/bin/androgpio/gps/DataKota.class new file mode 100644 index 0000000000000000000000000000000000000000..effc4fb73324bd8a587d074b8b5218d64507518b GIT binary patch literal 1107 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 MS;LR<)9xMr1B_qc`Tzg` literal 0 HcmV?d00001 diff --git a/bin/androgpio/gps/GpsGPGSA.class b/bin/androgpio/gps/GpsGPGSA.class new file mode 100644 index 0000000000000000000000000000000000000000..f7a77cace49914469243af277fa10904a25325b8 GIT binary patch literal 2794 zcmai0TT>jz75;j5hZ&XuEh`p`kd&1{GCNw>2;Ce|u((JFn_Gm8jIfOcb_|1;+0D$X zWa7w)6UUd_D{n6QC3y&`#ATOKK~ij$lQ&nn@{pILk{4I1@|3sy042)TGhEEY6{ypF zditF1bG~!w{q(;dehlCgeyTwcXtdmdS6V1LrTjwK&kvRTp^2f%J`Hh!tn zAwNF1WX}fz3CF!*6`jJezaWqr*%FuxJjY$=5r~hL3buegQks7;VrwR?z%DvLU=I%p zC^N$pniw3P5D;9)FNJJ;Dx9j$Ij$4*3Z&b2AD*3}_yCVjAcp-0_Mt%_)}Ecx(I|jw z2s~myMU~_Z7|`?~=%PmuYASb7oSiW{S4&XM#n`&762#Lzlq*5ZFrK z6!U$aXRVAlevrTk>`&k{&T2R#P`?$p<&}AY;v0AzUm^00;q1Vo<$VE&13DO{s!_|W zSVbLQ6{zm31f^vwaOO2UA<(ols{IwGSg<`EeVca6$YvZrvz`u_?o0x`7?3eZO74Pz zix}cHR=I4u1%Zyez}bb(`b|CZh+zXy;wkzQlp?qV8rrkFRC;PKig68N0(D!cNx%$D z;A>0}tHArWMNotJb>Gncyt zbbbO}O$W}Tpe69~MQ)6MTQOlMW^s_Sd zC@9L<9@9M;CC8RwOl`*vJln4n8CGR!i3QE>V-!MMH^0xY4K;2zZl%0oVB3yE&+X2@ zURqf@8CYz1Ryp)bL!cp{;bo?k<4?(s8Fs2`4D4j7;?3I^9U0hKVjPCAQ>Ly!^W};g zILr2w<2%Ht&vi=y>$2n$qEg^Nx4df%WBCb54N9dhSoxh8I zo^c0*cW~+V7*TLFem~J2I_@q4)ZHfEdSvCd!fB;X8*4_Sz33+FachnbpML(3{A z%+xa>Gi0W&g-mY*Np#V(@*;KHMXNX%?LEJHFLp*uOf-;riRCGn>`e31Yg6L(ydgc^L&h?{Y4g z+^NvIsqjLmkldkAzf0d&PzZZLt8?i8E(g)yr=%ciXg6lp?t6~r}99|=< e;Cl=d7+QfhNN@Uh3*YC51Q$QRkMNUY)&B!)eQ_)R literal 0 HcmV?d00001 diff --git a/bin/androgpio/gps/GpsGPRMC.class b/bin/androgpio/gps/GpsGPRMC.class new file mode 100644 index 0000000000000000000000000000000000000000..408fc94bcfa91349be3268be7b1157bbb8065460 GIT binary patch literal 4531 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 literal 0 HcmV?d00001 diff --git a/bin/androgpio/gps/GpsGPVTG.class b/bin/androgpio/gps/GpsGPVTG.class new file mode 100644 index 0000000000000000000000000000000000000000..5cdab49f1965603da0d4d3a200b024c498366e45 GIT binary patch literal 2535 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 l4>dw%ejmRtCHMnI5(PP!9Qn$%$!s{|nimGuZ$D literal 0 HcmV?d00001 diff --git a/bin/androgpio/gps/KotaIndonesia.class b/bin/androgpio/gps/KotaIndonesia.class new file mode 100644 index 0000000000000000000000000000000000000000..6ee16a2a3ddb8debfc6491bee1ce89c10136a3ff GIT binary patch literal 3752 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 aT&23x&SDrgia8;Rub>2Wje)o^>wf{2M0-jA literal 0 HcmV?d00001 diff --git a/bin/androgpio/networkrelated/IfConfigResult.class b/bin/androgpio/networkrelated/IfConfigResult.class new file mode 100644 index 0000000000000000000000000000000000000000..27e34265b27cbf61f2d31343f9f49eb8eafe1d16 GIT binary patch literal 3890 zcma)9`&S&*9sk^AVU}eGWD*iAvBe}BScq$DinbfE1S_FgFv-fs5G@Y7158+E$;>PX zt<~1P?9+E!)V?1{+x$?ELbN%j$DjS9Oh0$-?7}Xy$G~Cc-tYVIx!*f{`q_W~2jB#5 zYKRFOGo7MaUR<@yX~*)fmfdC7Dw)1jOiwP1mz@QB@x0|#O1_4;K=);H%}kd}XEFWM z{AH`)3$&fSU>1w6fRWv1=6u(77Bl2N;o55!x$QQs&ne0Hl<6%CBqeF2O0&J0vjSq4 zqRQg3wJy-f5Io;>{JdGJSP66sXo}r0&>;;-lN3#0UudykQob&r2j*++167LRQ@vfq z7AW6CyJ|~@CgTgphXh*XhUVH%+K%my3hWzDiB^2Ol+N0opBc>4Sk7K_Ouyn} z$=#(H4YcCp<)W2D3y$cx7e@tJ1_tx(5O_dGC)(s{K*wISO6l0IR>L|Ds?~!!zJRt4 zJcO@k__DyBpa-?kbUcg`0twrwCRJl67238>M zSl8I_{Z$J)V35&pvJM>z07=F_s^f8dgOL@=j&It|Ton;LVU+?iAz%h)lQ@MZG>i-E z-3I2$dVzKFbbJ#}(#?dW;4rqp@Xtt?JQ`tZcg@4hcRG|6KCMGTn~e2aI?mL^s-Px? zrgXF_p{I15l|p-~3DuA-<<9F!D7mL~%t$$7r<{Vj6uh7#sRS?Tn5`4cRptq7i90Fy ztd4fHCGc&5*v#1^GI(BxiFvj)wCoA=?JU6flIiBGD;3KrSee0DX{4y51G?<;f{sNj z5#&pzHyt#TnB%GI$}*4X%F4XuK5fpI2xB8#E|{gf>DqE1665}oO@D*g?d~wv=3_(7 zfHiq`snop#dBMS9M%S(pll6|)SF6sBHYh}%BMyn2Zx)uP%+)YPebMqK8C5u1>H%9D zO}bYn6$wWk?9NcEI;c8BQB<_*}W-7OW?23DJG^chrNj zW>i04aeRBl%G;hzK`**ATgH;KRz0(brDlI2=ndBMMGx>kDrgqc1yb~LwCcKvB{ z#Uf-|1ITh{)!S#wCLPCe$4jQ?S)PU)0{4BcJq`b2JDZ5fT2vk5KAzMT-pw6|$!Wt? zOpd1D&8ZT+ISYa}hez<{vF6*5*9Eor1hg*hd1=)42KT%=YWp7Ud4bgSz1;H}sqOpF z&Go(bBK?0Uu-MPoAg(aFIQKVFw{SRRbZz4PlwoY*Sjy&L4DbtE#1e9}EI95$b$=;E`FyZs!yJq+Kah7FM{$|j{XuVTV3`u~=LS~buudabLYdWzVU<*!6vey3 zmCJPo+8qodu0d$Ls5L6!j*LYSH1PH$X)k~7u#I(cG~vPLljTR6Z)v}mG3Ba`{0R0)FHp}5My`v!AHHdVF5RqVSf!ITn{6cE#!iaQno-T59F>|*r_;J7sDAF8Jlw)J0#Ui$%IvqKnIS=R(NKT)7Y( zoPRMk>Go~BeDM~3lpT@_6&@7DaC zVxEiCd`B@aMKa%0%zPu0KDH5lm>=5U08S8n_jB}~!WGPM;J(0#xxtjTIQ-<;Rs-#J uh}Hx?;86b~3(|r=ksHIG@fW^3Nck)O{^sHD_y-B{|AFiHC;t6F`~Lxi85bP@ literal 0 HcmV?d00001 diff --git a/bin/androgpio/networkrelated/ethernetmanager$1.class b/bin/androgpio/networkrelated/ethernetmanager$1.class new file mode 100644 index 0000000000000000000000000000000000000000..a81ec1804458edc9a2a2c8f73da2e9e1ff528f04 GIT binary patch literal 6252 zcmb7I349z?8UKITWM`XB+jMC{L!n);K$0GzZK#;kLYo#EP18u)V2WBMn@Kv&W+v>; zCJhQIML_OTDVK5<1Qit01O)Gji1&#gq9`gLDjq0M>i^BmUOD<}a?HMY@B7~Of8X(c z@9mRsKk_($I+?GbNKj+gT}h{>-*Reg(;ak@y-71+xMo+a>25cZ?CUdZqsL4xT&AH| zQ0vKCT}ryP(Xnka?pgzuJG3znYw>4kG!ltUBi<_ryW6eQ!V_q!`A}^&P-~TCTkdMX zlBz>jt8Nz*H#%MBGzg~aC_%UsAZaU;=g zBrWwGbQUYvg2jhyec>{~p6=Ld1`I3VPdqok9=I!TO+$5iDJn2m!{LIeL-mP{N*p1G z81cB7^8C4eDxTpoh6Wi>Gv#!@L%>94qJw*YV{AbE+o9cp~1ZVWnVpF6ezjapr4E$4Ph>Gql<1VT~zA zP8_W>#~}%_sX@moygL_<<$|SCh{skxCr{RK3fAOi;i|SwVV3I2i>0ZXWYKN)oNlJl z3739r#Ci?u1jpxmK}yLdph6WLr{Xk0Ny>D$w6e^r2$_iz=rHQO?J=dskhi&{p^Zj= zP}gC{Zgx^`1RJ?bl@97nW%MQ;t$1HKiiy`yXuVL0hPT1caF*bRe8YuOIyzNpmCfJ7ZiSmE4D_koxH3f-Rb*3f*nBz_b6vj#Xt*&1VTqd5)5BI!zVcM zU}4ojiNo^22Mn(vN%owihBdg+LE>c+aAWK2czTpj1(qhvis zY2EgCf18;cFq3P#y13ggER`8DWUbaV@lh8HF$F%3T^i13=seCHE1=^775$l9{<9qa%Y~$@qDA)2@}M zU6V{2L(Pm#DXzc_rE-mqYjIr;+(`m!D&X=nsxX)hK5);jGCA(ur}0ne_%v?F0pQNk zG8F*1#d$_1+{+qqqmG-@eyfv`RamzfjJP89TXfv224(~UE4`5jKFeBdYHhEJ)kko< z-(IPlybX8ixC?hvLuHVi^8L@$iDptKw_@esdvx53&k3dlhoNOWU7j+91x`?W)$P-9 zKfa*h^USxx_3QW|9^j6Xvd-aQk%!18h2cv&zKn2$L6Yh_hq<#g1%Dx z9KY1?3vR%fvU1IxZtZ41wy=D8^7<8?W^{KrmhDB*TU$aMg-M;sHf}Da9XA^$m^(i* z*>h+Gp3(8F;-+kNE|1_jzMjTw)u1ut2jzJkzf-i56>dU50mO96NhM!G7gs zX_MXUc&Fgk@jng!Wzl3JtuSOobi9E#1*O}KRN!H+n75YVb?n#iHV$x*nV%h;n<1aE zOJPEb=u)H(A0d(+qmoqRVi5>;;7Jg2_BJP-jGL!g>e4x9%>8<)iVbh4>+N{LN%0D{ zkpd3qE?et3Zpuv>{eDlXR7yqHWV+y(Ts$p%!09z>n?27_(BMhQb&^B6%n)9Er}yQ$ z1WyHC^&B5)`jc;<>k?*aVYB1(ru!SlCrn;Xf#PN+M85c;5Beq#uA5(J!OZIT(`LD5 z!>q7Oo8;`*UM6Lw5IIc8zwmDgMtRr__KDz}n@ta9P??l-H*4Z|8p+0lkxH4YhFL9z z-)CsTOZc(l{63NQG~vy9{=r2_tG=rd<5w0T(yf%0sqZb=(!Bo&`=_y0ha&z0r8SEl z=3_7P2l-ZnS$u{m1L;H=+q!C_6tkhon@7Rmuqcy`=Jd6|CMo^~)ibqiI3#{IUnL(>~7;BaNkY_)NVwy5hBi&doJ-nw` z9+nEhXT|xf|eZ63YvxV zpHv0$=j>plt)uwJ-H1ec_MjwcDM#lZ`o2sKb!K?A@W#MIfsl3{Y z1U;?30qBFxm?;0QLg)b2pjd-waQMGXwa?a&;-4JgI`W4~azh&Cak)riX0X<^Dps6v zPC*2s0~y?Y4wkA|%egpA&dbZgIoUkii6MW!&<4)O+?H1s?~|99Y5D$=rwjZQJ%_H! zI4#IWYJ6lE9~;3Z3PnGe6}?m;dlXl6?8Q|r!DqcU&Eh4MOy{e8MW*vvs2B;Psnw6UT(mPvKz0;jo2?Yabvq#R>>{WA-9Snx64ks zLoSxP)|_sTu8kI!f2K6ydzmzU)MFE6{vjm*7F9kJzelIl~{Q$!>e;!pT9>&Rw4 z|Ann$UIE+i5?*HJ-az?R*a{O#3I58_5}A!fcoly`Sau?Xzq2olf3RXy&HA&wmYwZ2 z_W7ExQPY?IC*@VmE=6QNp4V_c|Mp`ApEBe)6dmFd4ph`Cq zxnlPUQqg~QV|EQc-(up7@7DfdU3qm{At5Q|ObUfZcEwyAw-*VMIY7u2 nbXtThZZ9$mA(<_6q(Tm7&k;(2pXw)s4w;K-QYrJ~C{+F*287xp literal 0 HcmV?d00001 diff --git a/bin/androgpio/networkrelated/ethernetmanager$2.class b/bin/androgpio/networkrelated/ethernetmanager$2.class new file mode 100644 index 0000000000000000000000000000000000000000..3d74d1e51aaa84fa02f86c8ad5f8c7ac0baab3ea GIT binary patch literal 1517 zcma)6>rN9v6#j-5wvCdTV%4@Fi-;F4Dy4GMf{8UG;IC;rmWAz3GrNW0JNd&z zz-aUVd?@3YErCb{*<^QS&wS^c@7(6s?{7Z=OkmYO3qyvxWhE;$TUxI0cct1^!r{Is zTf*NIiey#p@`_N&aRXt7*cRX6mc!kO^`^KbO1^hBklG_L5`dKYvjI)1f*M~w3l9E2LhL=*!U(&61^=t<>IYmt*E?%wR`*I&R?oy^JRuT!Sz)kr%lx~s-ZwF!_#>EP=&-$Pt=7nyPRO*0 z869FQkS{9kQkXi}2&gMNgAyjVWABK>l*Z4R$ZEW4d%;;&A2GCgdUhBF|5IuzSwYrS zNi5j9-?~rD?WisrHRilqa->HsutES?HnD_dU2U2bo5*}5)!DP7Zw|g$qWj-mc3q)z z4);9a8OSpXoZG0BrPG%K5;R{SdRx&Fi|K&_VsSlgbZxcJ+X0hKJ83sb{~=ntGQ<10 zlo{DaEOUtX=K$D4-!=jQFVRb1twd1t;VP|khv52=nNQ?I8?I))AQt|C8|$I(7+8-F z9^w{>E*&6wfXo3#3tusQ3iI?BGj%5B*)itD3Fd@k9AR?4gd~>f`w9+T2ZVbYq>cy^ jJbEs_=xCz%dWKdoeArLFxBrY(`X literal 0 HcmV?d00001 diff --git a/bin/androgpio/networkrelated/ethernetmanager.class b/bin/androgpio/networkrelated/ethernetmanager.class new file mode 100644 index 0000000000000000000000000000000000000000..356314c04532910960b3a21288092880e937983f GIT binary patch literal 16219 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 V*cuh#b^7zo*(LZd8rb-j{142H;=KR> literal 0 HcmV?d00001 diff --git a/bin/androgpio/openweather/OpenWeatherData.class b/bin/androgpio/openweather/OpenWeatherData.class new file mode 100644 index 0000000000000000000000000000000000000000..1ef74e1e3541c4a6bacab6d7df9ba1b76cba70d3 GIT binary patch literal 2517 zcma)7U3U{@5PsgIZMI>d7~3LRG^J3IHV~~9l~S|?(PE%Vv51JeNtSj?x?4A!#v*>> z_xJbrg;(5oq33XlM~|Gttv|uP;yL0o?=DtaOSouecHWtJpLgb&=^uapdJ(`NzE2>g zu+?(QzBe`JczJKmcI&nkOxu2bochsF-)99@0!<3*XRLWEU$NY&{CIK3E(HqBCC~HA z3cZE@Pcu{X+%n_y+HXi95nZ5=J>DoLa%EIg(W=@CslpP1iNJTP%vQpKy4s|wp1T^KqFLjU>@ikrAsU9Wx}fW(dN zu`>Ih4k*N{3xu_$5iEK!$_SiU`?Tk>sxgPpZ*r|!>hY4tkCxqz>jZoFnaNJ_Y_C_g zlZd0o#BE3`v=toJK2)16+Wrx%SkayFN>*jk@*Vk(j7`C`bB#hRqnHNeg>6-6&tz9N zJArB57csHVfmmdB2G@FbzDUur`-o8UTHGz{FatlSdZ>;X#G2 z%-HIH42BR$aUg~CNb(7<=9ldKj%?xz_;8KtGJQH|K5ylwlU6smTi>W4Aj^z7+P}BwB%%I z$sy8`!=NR(uO-Q@CE2Yd39TiWsU>NsC7Gussiq~#MH%Of+`0Vi=2xDQ>_T(j1#JGA zN(@{0+oFvLY^8374(P=;ezCpC@>hcY(#e2JzoG9~Dt7@p`p)ByMckRXdlC0C<8F%7 zeWIVoi0FGm{Q>cLFx1Bu@o?%;s6Qh1!=Zje^vO_vT=XYG{YlZE3iW3eQB2vPJ|%W1 z)K5vy*--aHe>T*s6scOM*Tud-eG$+9#K(jW?_^iNIu3G}9nS8L9Ms?Wy;O)xQ6XZo zLZd>QVJbveCkWbb~Rtz*%aS%4!s+jHw_$=`t<*TS6HFkBHk1f?}RrO&c@ZI zj;0?j|Jg4f;=1}Fh;Dvu=CbCt{b$ tmX-umg6l%@_aiV%93S&EhEMP*C)#=p<2_yS+jAc?-hH~6l%^&fPkYYPAX literal 0 HcmV?d00001 diff --git a/bin/androgpio/openweather/OpenWeatherEvent.class b/bin/androgpio/openweather/OpenWeatherEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..ffc056c915028f87fe11e3cbc4704cd971f01b52 GIT binary patch literal 239 zcmX^0Z`VEs1_l!bZgvJHMh3OSyp*E+^n%QM{rrN|yzofszWGMza^_euws20M=ER*3U0ajA#s3KvX~;w ze1(5^Rp86W!Zg7ithjQ)yX@uQV8%$EB}}`+bmiIz2y-^l$e7v%8;e-t^+lVn=Mxs( zqhS@V<0=+b_*$2yam_TY6L$VLWN7Xif$7||F@Z@!?o@Z9hCEc}n4^OwcFm$#Wr<1t zG0!qO*-Nm;Z(uvZk=M@v>Fs;u{`S*0+YH0t#xb^b-%n#4d5-w^m}G0354ghA6(8Z8 lJB54v*uF!thk0kYht(c7{%|tE1aE}aC&J=8;a0z@^bb{$j>Z50 literal 0 HcmV?d00001 diff --git a/bin/androgpio/openweather/jsondata/coord.class b/bin/androgpio/openweather/jsondata/coord.class new file mode 100644 index 0000000000000000000000000000000000000000..205c768951680b5683a0faf1c2a2b4b1e9f00637 GIT binary patch literal 698 zcma)3$!;1!5Piiu4C4*kA&c$c1!uq}bMcKNhs4SWnFA406y?w`E!=24jmAUrTd`ze z$(H;9`KZX%0|}AIA(yUK)m8OgRsH;Rdk^3QjXY9>0}=X>9*mUs^hk!^rHIdEu3PI8G9R=rZKUBi zuuveZv{We1#uq&qeG9ls===qPf+{8i7CvR7yfEq L{nPv?$vS=jU2c>5 literal 0 HcmV?d00001 diff --git a/bin/androgpio/openweather/jsondata/main.class b/bin/androgpio/openweather/jsondata/main.class new file mode 100644 index 0000000000000000000000000000000000000000..e70159598ce935196a3e5ec8c19915612ae2895d GIT binary patch literal 1121 zcma)6ZBG+H5Pp`H_F4|X3ZjTwM6|`i`GT(nQ9u-}@Bu?&3?U0$%i_H>*DK=h@egQX zN}`EBz#nCt*`w5mF@D*3W}n@eXLjc9$Iq|d0Zd~xjV^&P=~u&`R@Z?O)Rq5U$!JG~ z&Tb>{t1^;~C$*nOQlNiV?n%d$e$83k+Ev?;Kr&KZT|g`fWZx>~Ha1=TP6=4Nzv*fJ z2+Dne^-wh$&5#a=Wpa0#p04WXgFvRCEsAFm=~Z?M55wzKtw;xUw>fn&E<$|5XsRn=-ej!^C)G z!$yyVc>%kWKbjW9E!ya1ZppkuXL=0z%*F{OpVQ23u!f3_GdN3QwiYzQZMCF%XfoWc zDQ+l6sm=s_9u`?@2qD4mVJsDIELCbOm1QhRGnQl+OBu;_(M=jsT2GSakQHRRCk~MN zZ0G@6Q)V-PLBe)?0H-lTo?xA{^5u^ti7$KKF#I|eQ^w1LTX^w_5_W+_=8&YdkA4~b*dRfga7VCr KB>1JdvGNO1eC})j literal 0 HcmV?d00001 diff --git a/bin/androgpio/openweather/jsondata/rain.class b/bin/androgpio/openweather/jsondata/rain.class new file mode 100644 index 0000000000000000000000000000000000000000..735171bfad1b6a1d0a6fb79f49f25d4a22de5031 GIT binary patch literal 694 zcma)3$xZ@65Pby*1CAT+J1*#e%f#pn^`OQR(SwG>7-Jfy15I#-jD!A`CI%Bt`~W}7 zSdF+uJb3ATRbBPotLoRcrxyTQSc@S_x5JpDz!(yCg0 E2MS%05&!@I literal 0 HcmV?d00001 diff --git a/bin/androgpio/openweather/jsondata/snow.class b/bin/androgpio/openweather/jsondata/snow.class new file mode 100644 index 0000000000000000000000000000000000000000..1e429bf9234fa7d49a380a9718abe8c6f3119bad GIT binary patch literal 694 zcma)3T~8W86g?LZ7TgvEt)EpCu?uQ9M&D2$)bxqe2MtNn7&EXF7(#c*vgqGx6N8CO z`UCt?#yf}~5g+<8bMBpc&pG$b-Tm!90K3>qAVSCs-wSl3qqM6#(!Y`-Y|6lGb+zw_ zP`F)RUnLMDOti$MaNELfxF@xitcQf?#decG#|G^%itj03g$Eou#VR3oq&=BJ6t)cu zNkY1!eEGfit0sdpQEN*=rlRYjT@`^c@h}++o2pAFRQ|(SnP7$bGz^sAAWS*M%8Sj% zY&n4`!pz(BVNbO^8Cdv4NSPsXKY5z{6tb8#cIODw&L~;2ngn6N#u(zJ_Op#8Ec581 z!`t%+i_Y<=hoM;6!U~se=6G8JrM&50BW%5=G$Q`QVjQg7NFZ*(;r*W*BPAP?$nY$u zx);>tH)YnFG=qILqhp1eqWtd>zS%n?G7Q5Ee-Fngj$FQp@3GPi#{UeSKkykJB%}Dq z*nTWP8WS9m(dE-LpRj~!F8oFIM`rp4Go@?H_pzAC^|8vhVDJ~leH4FlVFWMa$yels KXY!3f)#d}xvyp}X literal 0 HcmV?d00001 diff --git a/bin/androgpio/openweather/jsondata/sys.class b/bin/androgpio/openweather/jsondata/sys.class new file mode 100644 index 0000000000000000000000000000000000000000..6abf2a4efa8cedb2b8d78f4df3bd3c7c0fe38561 GIT binary patch literal 986 zcmah{>rN9<5dKcD+qzs_K|oOpqHb$pz2F6*pr9m>Mq`jB#`xp3ds0qQcAMR;rcdDO zXktpDi4WjI8Rwi`C9y{T%zSfZ&dfKL??1kL1F(wSJW>pc+;aoJ-IKoK_k{Oe@aRYc zPABv|mq*+Q2Vou=hRF_p$DJ9yC%-SYch6buN3eJ_xq`n%7NBP$e?FSj8*8ErD8?Q(e3U> z`^QZYyyDHSuH(0Ox4{Fc?Bp>M9m$Ykss8`Yh?NofF++wayFAJ}j|&Vlf3~yTmt9u` z2Id)xG@{ZQHH%sibGWS7USXKFN0XHs1u!g_$Rej=ubNoGHR_!AX!bP1d3$e^LsD$r zzzv3MwIp3#sH{F$7?%H5Xr%V9B#J=QL>@T1^c$ zXFqB8Ig%5Wo5avb8p<_7^t`xyy^CJm=OvlBG5j;I#MPkcV!8@S2yt0C{3x3}T%&#Rcx?K-$ku$wj6Zceo+&+ihp{zWy% ze$qxH)Lqzc9NZ%;HEU;1(vs|!i$%!>Ih~v+4?l2GmhdCK@&}Rs*u^TUgz`Z$PD8zC zZE@wcb`eeoZ% XnPBHLHz?qgv~q^D^@sFHUiI`h)grVu literal 0 HcmV?d00001 diff --git a/bin/androgpio/openweather/jsondata/wind.class b/bin/androgpio/openweather/jsondata/wind.class new file mode 100644 index 0000000000000000000000000000000000000000..da8e27bd8218d6e1a8c3032fdf85acd60b51e676 GIT binary patch literal 793 zcma)4-%k@k5dNmu_O9nDcvA5PN&(SpEu2;KrQ(AU6Ou+BFii~MVcXl3ZR%Zfy~@9( zi7AOB`~mz?#Mvu~vBvnaGyCn#eBaFM+4=WV09$xrBS%=1QJBQNp^k%isG=h!(?gX6 z{ZSl+GL^xRjzSxG!hBzTmcc+qz2JSPuevFrFd8Zq5@?5Dg{sHiE+O9=kNC0pQb#&{ z#V>xf&FGsrR4y!B6L4{bFxS$NdN=;mQOO6{87P9+io0^qmWej^3oxG^>JeeR^&dK$ z1SgI6(nLqBWx=nuE;Uclrj46~#XsTKV?78};@}oRn2sy^S!>%a%D7_$-zAj&*>cr( z350tB1r$x}vOonZ+`Js}FnKgf{?4q53%aI*H4d#;cyt-ly2*Y>*!UaM4EfJ0Mu9J2 z!!ZhKrp9AKy!$WW4T4>-m`!b){6L_DqJsvn@W7OLCU64_1a~hUCtdYcn+283Xg1C8 zu);|eexH( za~kgVEvcSBCGYF3vBI%vl+xzdhBi9i6qs)QA6z~VDDR{_8iT-ifH$Z}cz7GYM@_(M zI>1Dv4gDCLcggOnZbAZ6Q5vh{Kw0hnLuc8Q;9{c%G*-C;&kl^lEZ+H;6R3?M0?fN) z{x3>MWh^iE(t(ZXn|8Uz|F!7!s@sAgR+E8JgUx%x< pt|4zR^TL;f@)2h{ex*?zYIu0htP(!pBfn)H@h2>@C5L)!J_Gyhde;B| literal 0 HcmV?d00001 diff --git a/bin/devices/Button$2.class b/bin/devices/Button$2.class new file mode 100644 index 0000000000000000000000000000000000000000..71ee89be1892755f214a0a9adaa5b3f55cf6f23c GIT binary patch literal 1444 zcma)6U2hXd6g^{`*jbifQW8kh(k)4-;{=v`K}!OKgh014A8Mjh@Wgm`;tbjBTD!A} zC{%4J^rcdN0KBAq;jI-C(1-_KEA=;36?fK#*aRdjd3Wxdd+(fk&z=4E|K~3NOv1?{ z!%+0Zx^zWs&n8NR!QglvIff5z@^x+}O8WMqTochEk5?Hw)QXG;k5f>kJ!+b;85u}* zhC)kkg2PJ;xw+62onY8)Vi!6LWUV0iQ6GTLUc_TS4OcqgJE4R@zJ}R$;v^Jk9k} z6NfRd&1;NdXa}!Ks)o;ln!TVR8PukSmkbQyvpkM447>+v6D3WzSQE;L9TG}%Uw$XN z0?G_iJ1B?B(rPxH-o<_hqb82x7(?ebf_n>!E0T@mbPAv$nP^M<+QVAr(ih&llJ;QS z!~`a{TQdIEnR0aZPMA1}!nW&#-u%MEDbP5)lA2QO-_cS>cd!SiO`K^>FvYNIo%;#Z z!5oG?RFk`UiPxLcXDozCm3n&0`r^~0FNxg^v|=$W{Hiwwqgvl-5n zXuxi5tcXa&;j-G`k+7?i+@3u*-6HXo>P_LQ)}5y4&hT{x(+L6*&G|fzMGT8(F-y0Z z6^r^>(JDj#JPNemncnhDx?_|_p28^q4fAp8vzMM7Ys8 zqqn&%mi{2J>3LK7mEFSUk6^c;1A2XJ3uCz(guMnfUxm%+GPBTSR_Wpc%P>$Qdxb2C zKCI#he2id?K7;|LP{&zBm`9ASp|F4ii@1erxD5|?2>l)#_z^$gC)~x)_zm~)5cly2 zzu-^&nzpdF(WYh#&mzQ^l#@iK(|4nzum3+BVuy_pqBTof8O-54y>m2$^Z1HZ`n#eP Gq2EiS#9Wd9 literal 0 HcmV?d00001 diff --git a/bin/devices/Button.class b/bin/devices/Button.class new file mode 100644 index 0000000000000000000000000000000000000000..284892ce8c0ffac527ffa5326754f4b98d9ab447 GIT binary patch literal 5637 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

j1rGtN z(!AHuNQe$>5aS7Po>H5iVG>)I!*krj3#744QC<@26*lo2kBPgDHzd8ycc>}f=bd7Q x#z-5$AoXbTKpLWd)!)l+F;0?9Y%u{kzW{+859a^? literal 0 HcmV?d00001 diff --git a/bin/devices/Buzzer$4.class b/bin/devices/Buzzer$4.class new file mode 100644 index 0000000000000000000000000000000000000000..1c9e6fade912fbfb2bc12a0bf726f7d0baaa2305 GIT binary patch literal 1211 zcma)4Sx*yD6#gzP3`~bo5Eoonv|tNL0jq#4Vi5&Xzyt!37c#Ur?dUMm%mM^SeDc9Z zdBX?e7I{$nC$rrQ)w<9rk zd6`?ms01%rZ>S4uL|2VeWHN1WHOWxoq%}L(M!t9S>8-a#|T z%fR4MP>d289+WGP;AilJL_kGcGx(T0KgX>}bx!9DRdF++>Qkzv2|aK0iV}pcuz+Bc zO7K{&A!9!dN;tsaFN{!d2sI3)YJxhogJ&43_Y^u4o)Y*Y3XbB~PAoP2m!@V!Xq|%k z-J$KmcwE5=1b6Tq4BdO)3ddC=X_={vW=1Y)Da}#!ao5SXPNCK#qX{7iCm9<53!os3 zW?IJtcjD$^cDht)6HX~OEgTX#hP70>mc`iPB|K}Ycd8m*Ls;mqBxQI?zSi&v3JrJFZQT@O}n!+Ra@R-Q^ z1a){yRCz{-=Y)BI7+zus8yLqcT*qsqut`}yP@a!i!6)1%>|K1pJ$%hhs6IRA)%=8F wq;=yw&1hv?>Y+EQAp08RVF{g{{8dK&Uh)*7kKP5mRLupdnRY^Gu>tyi0Y+^TA^-pY literal 0 HcmV?d00001 diff --git a/bin/devices/Buzzer.class b/bin/devices/Buzzer.class new file mode 100644 index 0000000000000000000000000000000000000000..04760f2e439c28474982513af20dc4bd3c6e1521 GIT binary patch literal 2814 zcma)8Yf~Fl7=BIy2?<+D!>xCF0UDOJu`OOI6-{Ytso`RSNU@5WWD84^-MAMh{UcsJ z^cVDl&J^0QGtM|VR{f;o=+AL{&hC-~0$P~tIeX5&&-pY-_1%Sea|alHqFQ zY0s^C?)JM;f%eLJ)i6!3QqXMyF(D8sXaa`_u)eme+qz@jbJsLm&lE!osGgH>wdzM{M zkj4-XY&cn2a)Tw|ITzv>78rP`E+nC%OXfSTpcSz;yh_rgplg=r%I%8^j^mO*c5nK* zomv=eSjoFK+cG*aG$+wsQ}8<85NMmO>gM8%VX`S{o<51o7>(mCfdh50+a6n6(G^_5 z+vK&RyWtAkmL6)z5iS$p9rj^+8~QIKF@~&c_*H?f^v*6xUYBuA!FAkV%e7J|zwEgs zYt6iAS@&h8hD5W=wyu>%rIw-vjcQL)!HZiG-j{c^;{8dj8VaM6j3=DB2G}1Z?^@MG z`5yUNJ;(^#T^tWIYyOV#y67m)Q^mrBqS z+>+{TA1_;u9ykkip%rN~PCs?HORFdofqM!TW#HDM7wXfC0+*W`{L+TBg-Eq`H^D5( zrf)ql!p`R{&o1gW4CxE)0sr$-yDU_w$2r3>XcJk}v|P5v>d7)dAP^#Jdt7$-P(t51(G$KORSMjCV9myb@yn!KfCl&8tT@a6%p4z$x_@as4#U(El}B zqmR|+Q13j&*$tdipZK3sB9-Cv#3nANa_QJ6M#$?K-h7U+JLLK9CdRqZ{g~KMNPg$} zjR2fxzJ8v4fae=TH_mV`OlT-Y&n4zBjy2S#wN{(=Yjqz9>mKvVU9WV~&wHIGxzT_S zt0ClS2)D>ca_o4HNm-TY-0&u5>PQ{j2i+%u6@E+J_tAQTh!8EoEFWUdKgLJ9;m%9k zgUYZMPfGAFO7QQ76g>?oD5m|LZ78A?o?^i7f)vwJ=#p@wn11$A*ldO@?Go|?2X}__ zHH0j~Xv)HKe6u4 jb|$Ry^SJ{X4i{9))5R(t3?}$xa!?P1 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 literal 0 HcmV?d00001 diff --git a/bin/devices/PublicIPChecker.class b/bin/devices/PublicIPChecker.class new file mode 100644 index 0000000000000000000000000000000000000000..7707d2b63895c367b7768fadfa4e8d028285684c GIT binary patch literal 3325 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 literal 0 HcmV?d00001 diff --git a/lib/armeabi-v7a/libjnidispatch.so b/lib/armeabi-v7a/libjnidispatch.so new file mode 100644 index 0000000000000000000000000000000000000000..a7202b6f041988e531337997840964c6e46bf384 GIT binary patch literal 126384 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

    +AJPzRx% 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 GZ~p^HZh!3n literal 0 HcmV?d00001 diff --git a/lib/armeabi/libjnidispatch.so b/lib/armeabi/libjnidispatch.so new file mode 100644 index 0000000000000000000000000000000000000000..24ec17426dfe4061eab8fb6712754807895b5a8f GIT binary patch literal 130964 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 YRk~^lYJ17}_Frr;uWK*Nn-Jgs0nd#8=l}o! literal 0 HcmV?d00001 diff --git a/lib/x86/libjnidispatch.so b/lib/x86/libjnidispatch.so new file mode 100644 index 0000000000000000000000000000000000000000..1ca34031d02b389da7d9f8a278a0ca463a4ba3d9 GIT binary patch literal 124268 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 CuxiQx literal 0 HcmV?d00001 diff --git a/lib/x86_64/libjnidispatch.so b/lib/x86_64/libjnidispatch.so new file mode 100644 index 0000000000000000000000000000000000000000..21628363cd69dda4953b0019d076e1de67883a0c GIT binary patch literal 126768 zcmeEvd3+Q_+W#aZ5)hoAMB}jS+x@*>pcws;x;DHe@)(gCQj7LO8#MS)1-#U7FngsXV{qOgAqfK`` zN7YkLJyrG8)73RsmxLyyrlk1v-vHk)eTkIMSELX0^rR z&;8KftLQV&k%ULF;qljwtFQV#CE$a7Mdy*q=Nlq#zRkGr?;GqZQ}DAD{A~qaso=*c z_@@fqrQla8_zwzRHdz96K0{G&=D$+Ga}@mu1@BPsFO>YJDtOy8NqD5v+t~`f{d@^O zROM5y;J)dKUcqY=JYT`@Q2AUf;EKp~Tc+r{E|9<+god|&B&Xu$Z>`RoHd4fE~mYik7o{<6itKHq@~o_SL; z{1^q#_t2lB;9Va01O;#Pz^4JucI^=Pmuf-|7ecK|k=Fi+8!EBHzUKVHG33f|>`->cxhYb4>xihhfN7b*A&3jVEv*LmQFVj>aa zgMy!^=+98_E(O=}%u;aQwUSWFA5rjp54=smXKj-7(+0|Whk&cXT(?&=y~;<=kG=iS zAAtFZ@;7ainQT#c9Vy_hjMd{PMPH}#FG!JybNivM=m$Pe<L*{p_4s+7f|q&dZ&dI)5BzxrZ}q?j zp*@*@n+HBY!Mi;0h=TVhc#W#ppA~%8vSj(UDR`5DYxxg_VF#xR&Q41@G{{uM%*VAnNfNMc<|9b$$QT5B(}dzg^Mq ztMb|05B;l(zDLowD*AsZcvFi6Xy5X1WGLEQ!F9cUDd39Pbt_TyZHgXh6n|Cy&@WK* z9iIGe?1%nVMc<|92dey6EBJN~e5-=@c=G>N!F{I8K-cS_A+o-i3a<4(TEJCduG^W4 zK1b0nR=BEu=oc#bm5M$?(O=&W{Y{F#%|rip1@BOBOXc%IKl!|>=*xaB2}|~q_iy^4 z-winD(=Lx6vfohZXV1UkfRjGoGyfjl5B;%k ze^>NF->m4{6#Ya+|8PI_k1KlLZzZ9czI@yJq5n$JXDWIvj}Q9pOKL-&(`oZ37Dhk zYjpnqknmGg2fkIox9^bfv_s_m9t}THX7Hs1`<_tnj@1${Ou^sL^a_5Zf~O;n!FtVV zmw@p~o_qyw;=&MrhfB0Cpx|wfNr|Etq$4<%tcozL9fl z7gmj2SP>4-ombH?vZ20md7Qao&R2BWo(c^De78p`vDXeeJy2CoCR)$_b~8 z8VUR*HS=cAYpAOTSI%WV)r;!qg{yoEs}?p?g?-_<^|gz}T|TK|AwU%i7SvYys_Uz& zeAU(S%B$+@YwLXtHFfp#YQojNs+!8$+4E}VOb#PG$JY?9Ur<%!tEpNvvEs4{1zc3w zpn&5gWa^X&qczR>ib%1P)gs%fdW{9Lu`=9PU!?)#A0=$pp5eH<#ko% z)%CRt%PAgH$vBa!tgX4Ms=i@fZB2Rgf{HnW1yRnNs`@Elm{zf%vC1b@kSK6o&AjkD zsPc-cDRb+qDrU28vm5KkI!S1ioXM%uin_Y0*$G%+_U!tqhK2+{)fHK&dNL0el~cM4 zrfaN0x|ZdQ(A3E%iAG_q%Nvj$ok-RxO*-_%7o3tHU|R9l)mPP(SI(<0zpSDjR)#jN zflWZ2l~A{D;-o-1%%XfjMMGG%e^REWj7nrWySDM-1=3PMOtBRHt~Di~Ldv>IA$c_r+qp|PeMy{KGfUp}wqvfBAo+A#M{b&eEn??g^3 z`}R&(EOoheq6tE$d#4H3gny!_P%S#|-ZPm-z4b>@i5?QJN^TFI1PU7xUA7M63OW*P zsiCe4<~Fag-wZk4c(T*%XjX@vWP27czv}XGS$Mzs*VbW}n!P~WFdWWaz)^7a0y(<% zTgwHt4OKoZ2TPe)=IjNPNvPVI%Bp^guAWyjd-CNAFRonxzJ|*e_Dd)Rr1J8R z+a{G6mY+0nV)@DAg2DfsdHjFFT>RfKPxx<`gZ~Zl#Q%nQ@?MzHq*aU0sqVKmgx&v# zE%{Ttduzy_;@w+2{uJ-tn(?Q2_tuI(#k;pg{3+hOwIRlpahIbr_t*P@A3qoKV}JPa z0#N8_f)gj)rv7L_Sv;>E^B7AU*H~R$Ro{O{pSag-_C86VpuNu!AlTdV0JNAJs;g@I zw{8$ko?BawNm-1id#UvK^JZ5ioS?eB%qYxL_>|O8L%$lpTvA%Ku(tm4y-|o}t(HS{ zf5IUn(Yg%{l@&GpR~2NsiK`*vUIqJ!`5Rg7y=g$Ox26GluhTa0vQ-*u7R;-ezc&TB znq}eqhH%xwx_+tEdSI^&Efx#?DjFU&Y^mY$1~}#YE1K?iu(-juaA8GV!h>7@KSjBi za>e4fb@YZu)32+lxhz3XsN3gd8t&@C#u{eZpNPoie2nO|iwrEB7F6Z?s<8~Isf35q zFN^5>s!NORtgYlHt*)%THjIG)>jv#uENl!{Er#1UFYKGwP+sTYD=uaq>rd!;4gKYW z_OHc?sXs>A5j^`6v9L>)VXu79Qi6|b1OVJ?g#O9{ne^cRiaj&VtK;hmOiG`nxveHYJ--NIcTnV>BIFWTVqXq)tq_g z0sRm2nxM2QJhv92aFqn-4{OwNIpz~0*FFlow~2tn1r)DekpzXvuK+IdzeOg?O%iRd zT~ZVwT8eJUqm2+KKNL*f5QNK`%%~ma+BiGTQs&i_!rTEILW>QTc0kVWE z(67s@YGyO8vydwh@yFMaPpaS#W15WHF@$$9E)}BpJ%s1QxcE00*LYkDagD@v2d-b@ zV%+K%UWMy_a6ODGitA6fDsVO7a{pb5#4=nfadH0+ z|L&1DUlr~xxbDSuCoaAoXv6h4Txa9LYXBd&rksN7R$TmBg6q$?80x$Y7ylaM&G}b_ zSABdhaX+pFxGu!S@NO=yTW~R!djqZuaOpUtj=C|tdLpjJaWUli3tYd&bsVnSap^y9 z12{@2aG#IsE|tC+_Z(a`xKeTP??znri6@`$0o>={YF6o=sry;#em?Fi)H83t$F)DM z>v6H!Z^G4z>j_+USj6F zoxt-!xQ=$g(p}Gd+z;0XT%(v!e+ZfSigg0_0Ir|A(tKpt{uo^P{X-d^3vlu8Sb6h} z!F>v@skkQNIv3Y@xcHYXZ_dB^{e}a;LlDY#eE#YG+@AO7eixvHL4_wpR9{7Akzsm#H^83_2 zWv$P!rhh@!Tf^JbdvM?8WVqh?SF9kq55#6&N14-ak<0f%mBVr+eUa3O>sN*YBO@df+|keP5ji?kkgW`nrd*GQ0UgUviD7fK)`xLy) z1K;(stnYLWyhp)jdEgsWK65?r4h66Cz}ppku?OC!;7uO*Dg|%xz*j1Gs|UV9!B=?T ztqQ)<18-6ARUUYgg133#ixs@x1Fuu?4i9{;f^YP|XDN7>2R>cFw|d}Z3clR~Hx%6V zz>5^T#{;)jJMQwpw=1|$jnjJk$yfPgc;H(VeWnNArQq2fc!z@Lc;M{{p67wLDR{mI zzDmJId*CY-T+d5dem&0Uc}c_dIHu<%4cFsXj~a*RhpLf5OeC#p+@L?E*AzsoaKY(2 z>%WyQIA>h_x5@=)8Tzly1=lhW*Y1M5{r?UZe2C5j_l+*N+ZXO~!GG?e-|B*EUzF(W zE;!d?`pd1*hNb{Jki(OMPlzoVW}ZoPMeP%XGoZG!XY}7rfF1 z&vC)^x{l~P7o5Jf{>yj4$7>+&qg`mf-lrS+&f%wuKo4jMi*SKsfg=x!S$Mr@U1R*0|Dx9y9<7) z2IAgZc*t!jzTRUF!@sTg>ph0)xul41@5SFS`!h}Bj(3UlV5T`_#5+Vfm1zzY@ivj( z{TrmY{En{_=^adS$cVR!^e0SnsE9X-^m|OR+r;Zc`VFQzWW;BQ^cJSM9F3QW^s`KJ z=!h4I^g5=wB#7sW^kYnO2#M#2^aD%}Wja%&?`4|H`M6J{|HL#y9r2zY0XXJ%rn!8G zZx`vCnI6V;mq`DbX@*bY9U^@#(_Eg!+eG>brnzj1uN3Kern$t7w~F*7Ofw`AZxZQ? znP&JQUMJEQGR>hbK1-y}W18WPc$r8~Wcm=Mi$r=H(_Gre^F{hJrWq!P=ZN%4O#h7O zOp!j8X)cH3K9N3(=_8o#`GNI6gy|fnw~O@tOdrW~mq-t0`Y5J5L^_q}qnU0K>D{*? zoy+t}k>0`dF-*6L^e0Sn$c{IO^m|PIg6TSueuL>ere}%t7N$oqT_)1cGJP!5MM&fR ze}Dg{f&bIM|7qa=H1PkS2BsQTR_jeEKEwPlFePYyJ=rk7H>`oZt$A=cjA%!ApBaIR zWMr?Mj4KWOJcrLP8bPELx5t4k)M?34x!C&6E(WoRig=_MfR-G zuiSzk++U4yeyT<#DZRa|8)c>Z&4utvWW_R6#o|W+RP8^am^xPSzArQFTi#`@eYe2h zQ71>y7k~&#b+qgt7@MQE`)vUsZ_QpPCjiy4k z)vaje#zPA_keRg4J=(7`jlP$!priW({}DwA{X8fu;piuN6!LSl7xQ47mkX@4-|W66 zYNo&LD5?8~ezbQIxc92Pz}k;fds}U-z6tQ^r26u-`c4MOe?$}O<&<|vf90){i2hqk zmd6iYp-g}CYyZ;aW$W^u21u2sTO}8{_SJ_Mpk*DIzK!*xgA@M^nFjQ>Zr3u6b7acV zGK~~6C0gudnWfX7%^wTB$rhUiLHd$*4QL!`NAxFclSJ%QgOIjIOS}Agt-*XP?KJ?2 z{^=P%gXZ&-X|h`l>$hyrQ0%JQY{MLt8!~@1%qh7UkELM*RfBd}ezYUtU%oz!L2UrX zJdkYbEz5~^_@l+JPs0N98ZOnujIaU^K(%})06+z;hjJNf5ZH|xTCgQ(J_8WOeT*a>dxaz(SK+2L*a>wsnaGJ4J>o!CKP%S$Hbzljj;jMZFCx zz2LeO-(!p&tpTUch@G)A2UUx;@1f2nWL8HO5B7!cF|15xJ2uz0fatyr^O=~p z%JMLV--*mVz(E&}6}mFBT$!B(o_Hh2MDV3QN>awSbh*2ZNR)dRNlQSg>>KnZ>G@7q z)3>b`*r?r+8eBjE5 zVusA^s?ldYjpMQ0yY2ENdS`E~$pdO_;9ngoK@Xa`)f{py%1jya z^wr&lRkGEneA$SlwHjv0R;%Qdl&xmTD=2FO?i6{<(@Ue)Og=?BuD;fYRV8%Q-UV0e zaaJilnkFm$682QEMR{C-sU-!UC0P|cF5PAQ(LYcf*1p1SvIVL=)ypYEnvfy1)B_WL&)6C7Mz9_L>8ir=}E$@ zP8r`>JnCPN!PmWy{|KM950+7Ce%RZpEjc@6+OEMNWPS`%d;O>A<#a^Vpr**Pev8+7 zu$)D_q1-z$=yyNrZl1(=y3?_Jr^Dq#G*O53VOTv>gCQmB0SrUP3vP`#TB`RZdY6E& z!<`i7HVpJaS<-h6=a%^I|G2dBgVK}_Mej?lfHQ7DbY|axY?#MGn8RfTjZcYs5jjaB z9n9Sh=6KDP<{(=fAlwuqJTja104zS(6Kdis$iW zZY#!5q~&yDDxbp=~Wq8}_h9){%U4_3+ZDP6{jfR(naI$#!0^%;?MDSVkd8YsFn zgm_oX_3)Wn(5JysoU+|4c^-llkH7;Z42*eZ>GEQ$iqGhgt>}?kT~;ajWZ((WDT|>6 z*Z8i+*VtXTJ{V(UcZ$Ec3H79T`|5*{-Kj?GzTC_-pD!GW>>l8cT7VnomqGIb=z7B; zDZVv4f6XvIF6gkg#d~{0=6f*kLLi`h`^oQmd)wvohyp+ep=M&BPh|1t!R{=&AJ_J$ zi#Q;u2rW$aEs8E+V!Zqk#Y389BROvfdF&|=#egX0Hb^rQ2>T?6Vz8Q!LWm?p(c`mj z`tJ>l6~6#8XBjYC1B%Vhds~|@sL0U+{s3X|QnQCMiJ1CYo6s(XRX8UqY8_&?e;3_j z5xy!G%Z=?&nxxf}7xg;>Jx{L8WM5f#s1MH_J;695L--u-%)H13qT98AuMONkhO9<^0z+` zV`1SPkUeO2pca3L$Xa3Q#Nzc8$!uVV%|MoFAjFWG!A|2|Y6?He?*15RuiV9Yr}Wyt zXNsfn27hxl?4l(4ng51&QEVVKbypx(+Pn6X0q6jc@AiqMy$Z0;zw8tITd=8MXSKgJ z>zBVw@qHEW*QSLc8&dcX3T0ON-&}|5*=qmj+BUrp3z8zg}HhYgBG=!BPpEkqjF^*jdT2G{R0!hNTmB1YnVleVAon%z9DM z(tX*nhc_cJd;)X@B^R5YK!3fh*>qIQZ-o}^6(3<-dK79$`xkV?&ynNU?FT{avBJ>t zvL`j{1krL3CC{fY2Y>TZs3Hs*Jwdrl!YnpO$LXMub#Er8gjiM>dD*vPjeu6|zJ~d( zl~&1yeHG$q>y&d~_$WQ=CA=TES44LN)zZ!pC*IiNlz_Q`zSn~vx_z%>`trSkl^X&Z z($ak0cT~5=2cUjBC*@|1IZkL`B5xQ=F8xNhSxr=c<4>SgcLE>(TBr~5M0bYAPZdsv zs6K3RmwgXLHp4mqnV^$CCM#ah5wfn`PTN2~y_yBl5uF+6-7f65paY{+F|f$*X!s=@ zWUQ>sB(w@gB8!mKL~d#h9JnXw8_*Un1TtZ{(E~C#g*%f<3E50(OKQb@9Y@xPLvTVefs1wDWK4K)`*^U5 zrIf?HjSa|wQ_1|6gLyI;1MbP>9>}h&g}W)4rZK@?n~VW>kldTZNUW+hKAFbFePl8Q z+!M)dxw*eVd-pB*%gGpUPat=xoBN(*8durTWDK~`XMAzLoBM)f8pVy(VAktPq*P_o zn>(PU@Cd*j2TVhRt#P|bp0K3;s2!|a;1ywQII#;%h&pF_L z8~l(1W zbaG@o5aOgDCn1E( zjB5iS*B>v3qAd}L@7?YmuMU;?BQeeyR*MbwAiDpq)Zf#d{HDqPk`CG@+%^ofcF4M? z39>Tigdoq4K?HfQ9_w}DOa~Dn0!;!e0!@cq5=i97npeM05gV6T%^lB--^wpkREJ7z%;cP+l9i|>V)kHfGSO^c772_)j zthHC6JV1liQcOU-{^nEBfOZgt1mw?s6xYd%ldiz$fuu$LKAi~WzR6K?;9n183h zwq5}3WE8iMHfDbBsy3Y(gaZ@tqAYZFbVtrd@f&EcLhc#Ji0_$Tmr4Ew`@7>vNa0_) z3>#`%ccC{5p8?s;KqO&iJb=tvY42wFXdWlAMlYOWadPkP6=~iOwgxkUez?UyoxsdCTF#XzaAldwbz8lin+Dh|@(f-A#d8_ESTsVlX-ig9NweIwT>3d~vPkJ>Wx&}Ko|O-MGie$QMNzel>`-LPsc zS-zC443Z%rvxj6q?nZXfqL8byH#97b&A#$ESlDZ?V(`z0SoV-wEGJYE?XG5%it|U? zpciLEl3^7?;wv@BdK9z>VuWIU>q4oIeFcf>7JP2cN6|2}BcdH< zr~ig@WF(M8l?jwTdN(XR6uX-ifC#PmPw4dmxRFAsa4ezM*IuC((+`41l^KN0yM-CN z1&fkuvWKByWqChd;q(9qMr>5jhz&oF6MQIE{B5QY3%w4X{&N63 zX&1$;E4wY;2sfXMR+2*oUSOFbpZIT40>%L;E;!(SLB62nr?FdEX&i*j4bC$7S`01GIlknW!Ao1MO5I@w5-|FDG4%^vO^E zsNBU*|BDheKTBiuKLuwfEjK<_cyjQ6Afo`BF91b8dW!LhBGH5@z;3f%!Z5F;aptq| ziNJh8!&Y+A4-@6kZ-PT6a>YI{HNS#-USIk6qvJ#;$vVAnETEKOXx~`Wlh|$U763>& z%L;q$9$0@As)Mm$#esElRuKqO00=ez?)<8PRct6POL z9seVr)w(?Eg1L#-pr=Z!$uZ!5qD2=AkCW}aS}a=Pa|8hGP3(mtq1$_qDcq~A(HeOkVCLw`c^^DP@{ zbp$LWX#Pv|?x4B3#O#zVhS*JnsbYT-=Sl9rSb}2X(>r*__c6A?VCzwd|B+6ohsW>2 zxFPx}e*lFRk^Jrqjn+&!U+akdsR8W4c|nal40Rz6AjApR80zi9W7Sms_a)|rK6goI zS+UaDqbRhyfWjMtyzhm~IE2OCh#wR3zJlRW$V+b22_cVX3w9>(4?QRGu?epQ&6mcO z`meiK7<%EEN9Z|onUS)g_ai6%RZy@czTf)^`#dS$O5YQ!*foO@%CKN%&N~*gK{u)= z<46A0#Lw{HUv}_2{F$jB_=Lu0wQPqHMSui(+z6W9cXgcVE6{!Azpx{jsBoR&%bG7} zv0fW!vAE{}b&C_0#Sw1^f;&6}GaPyRPcZ{8{x}DZjVM;(eF=68(r0~LmBP(#x2_d0 zn5tWYRzV2P_S^4L&1^68wfHQ&V(Tkj6*#}te3APZFkc1DZvs)n2p)&EB$k|=x?|KnptDeL~1B?fEC(wao%4+_sSaAV_3oI2!x5Gc`FyBuHY87Wh2EkIYtUN4g zCDW6wlBq+JnKH=K8QrKL{^rjyNEBNQX^1bIB4|-+ev6Gw14?7nhokv|Yd=j3uKge_ zGyv-_?2|$?BN?RbBy=^8IXWYE@>znXn3=GIt7iBMM)F`rjQx?-O-y% z3b2SM{2wkMRp$M@ zx)bmatV~-LO6j5dTm%NON-l}b!7eQkH3Zv6!y2Dwn76h-s)^Rv?e=9DC$KE%IqOS-9&2$WDychvT`^2oA1DfhNV_1%D(nEl@p#zq|*k<)zs=?6W4P zaSs7Dd7y!J%1va6@=MHb$5}(#Bk|P8cLyyx7q9<~m$&~V#n*`W~zl`K(bnywynQQu_gv6~JV6 z8IjT0iz8o_iOwx*>u)ZAGYc|yC1D=7w&RKQgJ41_*6`gEtx?|k?SJ|hrYl&U%$#uIiiVVl-D8HTmL zVGa4x7&FX&?Ky+c$dTO{{_9S}kY){CG85C>CVz7dQjelxea48F*1kIcHWnWW5Z6Jv z+#tFg`rP>P6pSqB%Ce(F1nu9cE`cg(Im`{H+_QEI;x?i>_rv2$=!Kq z@LEL&`8UetD*HbuS1i+$%WW5H)PJ{Ju8#kMa>epGx!fXIF15=>kqxHp<^VK`ZYRzj zlhOK0l@mMDo@Mt&lAXzwbQ%gF`u7l^5cZMWj!rQc7%kD9XeW}OaW>j9_)n_c5_S?y z!3)9!%@|m;Fqb9~r}XHp&-|ravO^r(R}p=5vMrd?L@@`W7?+(J)-5fld>@=}N)>hTSvz^BQrm;@ zBZMPzHUg}0Yt%$C{8@NEo5WCSS+q#Q8pHwrk}ol}R3U@^r9Cm~`5)A?ongD=dfM13 zXg~O5Z?AkGg{eR86VYDyMIkFSz8~<3c31PpbLVs37`2PDg$-9Deyz~fcsPWZk(_TW z9OoBWD)laHY^5%y)VKI zBBJqy-L;kOEBw~Cuxx_E3O6kCQHTPe|4ao-BbE6B)-adLEmOIx;}@%G$Nnv1B-nrg z>V#{_?K?k*9Hp4Lu%uJ|d8yd67@tPx6b)NgJDqFjo4INuw~4PywQtG{KKuq53+I-5 z`|pLqK&65SR#xf-A`NXzw+_2lJ0LW?*&`GHOfR4WGAWe5xfioIq@h1h%01DyHsML` zSU`LMr1lk`L(7+-=svZ4{A)_fh9ewm(lA$Ss2m6prF?GQn9!iP&aevX&ouj>KG-)x zF}iwU1qX2qbDcdJOqjMQsex&G^VeD}iIQUuYkwtw^{Nv5MzFhyk+Rw@3PU|5L=jxg^rjEVN-iyV`mXr^J#bXH37_zp-Ody>P7cn4O@P}Yxj@GD(@C4A=fGt7O;LP4w*5m zD>LnNuRw$YMLd#AFm|awOik_`(f5+jbzU^TQ%&@(fERsN5_*3xI+}!je~PE<>Lm0- zUi4W&LrLreA#-$m6GnZm%OanQYua7lzmDgyLIal$Wpr#9hUq^nT3P#5GOdWiRr}kRX<_5npOb;~@Wi+h&0CFIC=Ch|Zu)Foga^4*)D_k$i$ z(Y@1OEa^ird`WA+{ZG88FP7haKm&MRwnzO>OXPn)sDD~t3Hqx^uXWoDi^Gx;ibCu& zgy28I>(7SFwWa39V)<1sHjePClz7Hjm|pew^N5pXPe`Pvwx=-8fS@|QvI&|$AfjiA zfS&mJ>APNk@m73N5jIeUz4ApAFFqOcH?wrS3^F_IUq39M+)Q}|UsAmB5YpyW{b`Yk zcxrny-zn&|&v#fFU$rk}!o(J~tnuGtLk~YkV(0i6UsTz5GOrcJ83Zn>=AQOg;jhu| zoG0tCr0@O>zB9d*{ZQA>-q*?Q>)pxOePo~G&h8)v{bGfutL%yYV{R5wO+smG&T&}zzl!A$=2K$+ zf!WHBlt(hCjkiIcQVO{>mV1TmAx!ttn$UYU^={uG2-tkhC_T}9_InS2Q@7o4)+biD z`6JnOkE}!6twbfF0D$$pK?zMCz)GYeSLII+#GI7EcUvKIGwd4UWK$8A2r3RWF*YhC zWIoGpSTkw$@jFl_SNq_7#Gd?h3I0CubAJLq{U`9Vega?S!sBbU9(Lql%0>gv@Y|s_ z)TRfmaQTtlXF2Oyg0P)~pUCuRg2j1!LIiGCZ{yDdumnGN+_**JLAk4YgI?Dlf$^x% z`Zgux*+ZY*2qylNKHC!fedfRFC-5tN0>APn@U02_IR7JQDNiTtAHjGe6KTUr<%VWg zd~u+vGPmLnq+KQT?;YbX{v!uHzI^3Ld-I3ClvLh0|LP=pSQuXP9fMa3!oJ1$fsgv} zI(72I*kRbV!k3xoi~1`^``-KM53fgO(-|BJx*WCI;dZ{}tcwO{+@Jj-xy{2Lm5aXG6n^($r!19bD;Y^`p zU}Jd>mdM;1Wp0b5MKN>m^Y=9S<_?ZzZl(RqsGO3Uc6Mwm_K={~b{ncIJ_oQ5mgurKuqEx&@q zwnkUhwup!i#1|oknLaNF?~|d&E(WFfdEf@9u=nOwqMGRwHToYwpTn-HG}DtaM1fgq;|=0rH5gd zW>~Bl3KuC7(Y}{!rIf)I_iWz_HM#(3*}jv=9?L2hxzHFA?fbhD)gGHgW^CWJQo8-6 zbfufUX@(4GLC0!_XB>tllA%p9#F?Uo6~mXsN~FI#3|*4p zYRRxwGboeG*9;Fj48tYEB*`EIWC@Dl+wrP|-)n}#yLpS!p z30Aw5L+W`no+qg1B0LAwvw`O#^<0MM)70~HJdaY(v+z7pJ|{S zl-_FV2!B)L|H*4I|1E^y3HYFY3iuWUe~j>DfUlMG zk16<{0FTdy^QzY;6|$V$75-Y{&vxOjk@zbV{$k>fbK&C>f1$#UBcA(9bpBt$3ZReE z6#gjUyTy)|tViAus3R0=AW>aFVf`G-3d;JVD&!~s0P;Z~b-w?U`1f#L3-$ag@wdo) z50q8zRH*xjS}akgOVoV|wVbG#6182TZc?briQ?`=$WkIvmnqb*i28*@T_RC46ly$C zLnNwGqKXtMkEk!PwFMcby(MaXj6w}1>NSZPAyI=BYDXJTk4w~@__Zde@{3nwm0u@{ zr=cUmze?0A3iSk0QHkDhbr&@f}fWhlj1@IpF-JK zp~wFrQMua%>cf|DL*u_d)Ndr}C0X5P73u+^u2dOH)cp!|8&R_)>U*h)L_z2<@J{z*&YXit7cD*JBX#8PN*`nm)i{@eWS;iOV$mXPL~`%RWb=Hw!e`gKqXS zHU98EOV82hUWff?H~ZW%O89U4EIp{vTOIaq_-aSj{a}s%OP}n!N2}a_?XYifvv1M( zXrJs4YIL>3eutag()jcGWM81sCp+wQZuVl0KcY|eV>FsS+$SCU32ycuPF0or)ZbUb zy`#}D2s9h+5I6fH8vkgY?6+z3?GF1F9MZJrYc+mZpX_BCJ;z~R?`F@__+R!}_dyyh zRYT!_=Vo84@dx$E{wIy*PFv|EFA(E#)*_J$z2s7>tp%>WtA;&9e2h#k5&xt! zN$azWh(>?7A8SZ0-XJnpU4Mqgr}W7_R-@koyVT;(rHBRni#U)>E)l;dRQ3G5kik2= zbZPWbN5tJ2WRZhh#jeu$^ZH~rHTq;n#0c4ASvQDW`fTVaWO9i(R%h}#cB=0=0{%>+ zUleF+alTu`Z$_ya{<%-~*EG5rXsN~R9|;k=dF#8O|41g6h^Efu%s$JwM5A*Y5x0uW zl@`Zo{MXnTzGn@O*63G(mLi@ZMKt;s@pF7Z>=yAgra8~F_F2YijlNW%sl~J0BHpO+ zxAs}XI*q=}(c)Y<4^6AeXvGnJ0ebz zB9`;kw=KR}kgUZgnWh?E-nTDfx>KVc5@4Dv8sqZuL&!`~#sK*_L`t3_9(4b3pYVYo&zT*lhPvhMLP_%*{IhheH@D3lDB zc^F!hFe)>0$U(mvElLuv5OSxS3hS zvOZ(VRlS7?T<8M+RSN``#N(B@$XXofo+hTlqtUSeWdWn6f> zM9R<%w++-K)JTTSlHmvs!;2>APIFv}8VONR3$!#odzPcuC3Fq|hD4wnokc^Ep6Q}!_^*!4Vq!QBa&G4TZL~)hBC(F zD3W^j@J*g7;m;1kd9s8%B*PINh9@*bwZm|N!QgwWQ-cX3BBLKQeOc_&T}7QY;T6_?meZx!Bhw5TLb z+`(}G$&m+fE)$Mue4M|1V*mQ*$PZgQ>)|IM`{N)>_c_?7l>Y8fIA`%h9|Gv81WG=m z^#yVEq&n$$-1RaZiwINFfs#(V+n^R>g%5xWx!r=?cu1o@I^sdFkpE zbL8{BgwK%Aj795%t}BsGe7uA=BdhqS^7$~*Fu;v`4n^7VBjoc5d_F)vpUFH2%IAyt z{Jo6$hWY%3e7=>>JbnxLwDb9O`TQoIpOeo!`23W7-jDJyzzzBn`FxLjp2BAyD+BmE zKHn&xO+H8D^F4fCB%hz+^L+XI5ueND^U18&WIkKzGmnJTbVBz4ZIwaYE>>@pBt4$b zdGh&x(8uD3$>#{+{JBqvvYDc%$>)Qa^)8uD7HK|}&qtAWyL>L-^DFXs9G^GJ=c#<= zDZ9v1JoD#1@yzIQ{4V*-Aas0%eEt)kub0o)vwBVP`7eA9%jZpeo-3c(o#GeC=Q)&_ zKlh3J`Q9L2ET4}h{b>1o9_f#l&pY^>BcESqxx?jiBY89Av&i#bGUj^^^Y4+*pKZbO z2lDx7(r=Z|r!r3lxZ~N-{vIFxD*%4|r*JwN>Crq8A@D1XpOt6y%iY6#rL+p#=RSRL zt_u&Jss*mxbrfHwId0&0_o!Rt@pDIm`;Ts%E4p5@?)UyjVq&vg@^D^4Z zk}~^t`7qtSNjyB+f}HIrp1J9J3GZXx_eZBdm8dY!u)*HFY@Qi{x1rek2PZZG&%~zA z4!X6_UtXf&d$-4j_tOe;zg&>#g_N2bE zMZCQ)$|16dN01JO`-T0WnIPl64D+$CvssYx5xyU6LKTbbrDb61E&&+AwjoPj_+W>O zX%`tj3=8Mx@uv;Ypg4P4{PdXkoB%oTvr6%C0gf7pT$P)N10QcfZc0%0ZM^)?`UvBQ z+WO6{q#rl4rrhv-#>1j0d;M}Y*HUaUxG#4liZ6K6egH%OO}GCdsK2%Uh&$~yUlZ9k zD#&O%$|p3!Vj+4T=SLfrP;p7i-iCpcI?v(Nt ze5OiS#-4zqDQ@6lRYCJJd<}_Xl(e!+%~wjzAK^D$UKWfsrk-OR_9>3Q`-als0~-F$ zMr?6U$UN*@!@ATs+Zv1Qu~+TW6JBUof$8I|QLDz8qgIW#(pE+6gK(_pA{?n4z$kzb z@nV=5e>hW$ze_X#KRRKJLb*91=s7!NwXZ@!>R?_P_=Z65>+F)CBM~%*&r?!^k@Yy; zVnOZ@|BaK;fpKa}srepu@?r}w?GBw-_8sX=>Z&Pqv1+SWo@r*=&^WD$_f5Ro}$0QswziN#iiwAzUWNx-+{VsU` zC_~yc0Niv9It0RWG1c=SBnJbGz(=V^95{pSkMN$@(p(m?Q=$CKcorsYJv>Mf?+Br} z;-6p;6Z;fUMT0-Mf?9UTS`J|??fY_7<<5eepekqoMpQ0(H0Q=xICt(-qalG^FcuWF zkPnbz3(uuFB9|hJ2(PSW-8$_di^Y!=HeUrAfOD8@tnjT3B6Iuit(Xu$fK07a@ANep zq;jmP*u+K}`x2-nVMMZb2I}JTpjV|9D8bQ1&u&(*2(6FrieCmz{LlCY{yOBt&N+0! zFh+13Zd~`MSXF?xj=0fmh{b%G%biF~6okK@@D+a02Nn~r^qU@Kg>83rq>9AH%c7Ic9d` zYOGJ9ABY2vsoac^`CY(Y@^vv!r|QJlR(8tKY$^f^#st12hvCi>3q&3W`vI0)Vo!dj{T;L-`cFQD=5*15$pN2DhZ8>xF!O0N z73PQ=mXeFVWi<@6sAuc})N}C-lvGaw+~{6}#u7hGprn`nqs(I>@<0aGiz0)uZL-W! z$fNrP+66vM9XJI8fF*t$LFMDlU=TBK6_?~MQJ>iSAh#dzmA~}>hiS#n%_@X|;dAd>J!0c(!xD|G`HpefdxY>TCKfIOl8&G?t2W zHK=mTF16vm$j9vv&ldR~*%_}tkmto{f97@Zdv4HRO?~ z9~}xO)($O~>{qH1k2Y49R&ewx4ednhOH6g(Jw^8T|sF`VL{q#vx|ZIRtS z^I!J|AgTUD`c&=ZdA=m4V1E zkzdfdVFk)AHg}a;%Q#dT){=Jr8hn(6GuMYdV~50sq$Tae3}+#-^*PTF3t0mZ*s4Pj zJbG*qevD>GI}X$fTEAh1VQN+*j#48{CMrNI59Xs-(qR3~L)i@g7o82$OoBo-7x>|y z0BoO)$()m_B{;^bAI#lP{b25rHv2-XB8@%IcgGO`s8qO zn9)jC8P@O~ten3Vg;Zn2H7o#4)NSTUm%0M6vwmci$Ba+AY%*KV)jZ2;e8S-Kd9mi>U*A@@9-#*i7ZmbCHEk<(NC*ENcW zXi0l8wsbT!j9Le+WvfIpjlzL=yX^%v2}Nq{Mn8se1*~a{BfAguU-vGXAeR0(21|^q zl|x#@(bTLq^n>H|sgks>g`&){#IBhuE`5o#rb2%7eju~8iH@JaPnZOb%U@%o<1n70u%bol{8Ni zlJfM4kdTs%hlaF9_1Giu{Z|O$Qq~|2ehNaC)_&#Hr+V)25A($j2dRon_FKO-tlu|q znv(0MOiAiZn^PLhDc@cp>giZhydBJ{7C~zd6BIo0Mo~Z!RQem%Uo?#%C7Qhpc5K06 zD_v!O7o!>z*VuG$AX`J^^GXr;U4q@wNO?R3`pg$?_OQQi01Dz!&Ev)*bFR-B`I|Ydh)C&QUl1yP7E(ZvJVQMd zK6RHQIiDmyS0oc9$#h9FROK>?Bm+P~ZMnt^$6uC#ZSm8yLGyXBr5BUMZ<`=}2+m)& zfi-nDt74dAbEOw*m~aXa;S&LF|8g80zCIB7CWW5Xtsn$Leky#!z)q~@Qpi7A@Z+I~ z4+t7y9z#|P5@LnQ#h0SeVx5hE9Xx}pAPt?3HbIg8 zmw_o5Pj^5c=R8cc=&^Av+29mUcR9sa|A5=(4u6Cm@HGxLK&f)FlYamX!$}LiID!Z` zWkDRNH5^}^d?ffWnA(-!;Jlxvr3d@MgK*3C1^mk~HX-xm-+=Q+A7lO=F}Mz+9Fri2 zSS@S_pUi?rVM)kK)EpexFd3KXk(GNqaxNadWZn0FWCn@?pR2q$F%T6lunN2+iUk!m|AZz= zNQkU6xdWNRhtSZ}e40LpU~Un!DLCIs)KZ*#W;=Q)OuJ+) zz=p6W86SaJV|75Q0|%6W-Z&iq5n6$o?S~h#kv5132}V=<8?+r4O6WmS4d{OQw|jJa zg}-MJtb7LuPVb>u?%TLQ<(T~88{>SOC|4hB>*;uI!m~XI#UR`1f!L%0kUkW|RGwXo zVJY7}S)yiPP(e?(b44;rEh3`*d)$0>rX;CD<`}OR3)sIFP%EL_GsT;hhjLdzJ1Ajp zpe42v1o*X<^9C?*J_%v)NjQLFKl=*2>4F*NLnby01J*agt1o!7)chPqJDSPWyDAb@YU!a=p9VFVbDFaSD&4(kVFC3Y!R34mn>&2?BRkiK-l zq*(Jnk~F?u&xQ2$YU zJ39-bY7p~=b8gSJQu7=87xOv0^5^pGPwAPMZ(uU1zk{vRYwx~-r|if7ij3h8t9HwZ z3Hz3!+u!0XojBNWsZgnIMG?g{tTS@Z`e;s60MWM@T?t$y_FsE;v_; z%_Cy{b5SmJf^e^$i8q#L=75RVAUm;tO_zKfZT}BG8oR5+n;k|EGrdQnE7$P zlcF53SeW@UG^p6mo;PB&lPA?@hdDr{A?#GhwWcH1Ai8RUZYW5sMPFvjPWfI z(6zgD9nS|Fjp_C!vX1*|O`=i+y?TpV#q`R0dOQfeWU6e={F`bVz>=W;CDDLU7ec@ij4}hLRR^i1z zU_g;lVIhH`9OQ}knp|&)pO4{}aQsmyewWwy6>@=7FsxU#(X69)HCt#ji>kU{24F`Kr(VlI62M-XWzK zj2}Hv>mB^eyLgYl{VhDYQik;rAwPrjo2*369Kc0v{6j8D=$*rw)5nkibvxd(%?+r- z6>6A}20#D7LYiP3vYs&5k@urdio@}m3C4g>7*Az>JHxqn2a7Q7htlb?&UgXvnMP;& zdffRJUs;XO>x@T`jNgh!F`vq!$E*d$KW?KDL54u% z-)i%0JU9jWn~z7i1VD)Edt@=oCqAw41zpCaHinDC^xTQ zQ}_AW55EXR5gYQK?q7X=hWQ%nQn|$#)Wz4MUkeWf;DqMzE`LxdwfX^A3pSOS&v&1vq`^XY%Gl;U@7_`B_rl_Eq9~@9nJ!ZFm-0MbUE!^b%kTn87NmhoUk=>!9Xv_gH z@JC9ms!So!`6xMvpOu9&7(5Y{_8U<0MBfi-+-0Tw6#=;a-Q(~+3%>?|`Um{0FUT;r zMY>_kFSBKEWFTT>LJ2?m$9-!=)}wf==Vqct<|1ev_B#h9aqxout`1rAQ9Oj=j9zLr z`l>s}i4URo`mXH#|RxpVUNMCy2V@O%c9lf&q4q6YWhT>&N2!t6iN{)NdcB zpYouLpt+CCi%pG1RmM11m-W><*cL_N1lw zn@<5DM}-``Ch@N}GU6XIgx!h34wJ*dK*j<>CNc^`U=JYU50FHS22f})BYrqNn@&0{ z*cJ<F%8k8~B!VH2(|3=+{eRv7c3n2Hy>nw=WV)O(KKr!NRV zo(60Ipe?0ajJ~5a@JRsGJQ$cJLjnk~7RRc8oCf{`(p(0(u* z5t_p*o?7&o61-|XC$sY`diz*$42HR*rK4Mdv5VGcG#(It7vNU*p5U0bv6lrY3=QZu zY5bAb#{b9OyMRYoU5nq7%Y*uUkd+oK?UVH5uQwRQB8qN{_&0gyVF@Y)UehrQ02f$9;Tu33- zugR6`a@x8|1DT>4%!R7KJVb-}FKo+Xc9!G{L*n1@@3E>BUqS!RO+|kb`u{g05~}}hk5#QJ z;7MB7N!N$FFY|;h?b60It%HW$fn&)GcQ5=%I@0l~vX*3t1m4nBd=CL`GH4o*$wluxR#yQN6a#0ux;){4;F=h7PobzI1OfV7i}!ngdRb;pFf ze;@Y!;8P{$^448XQ4|D2)yxSP2kudztdNA(b6-0g}a#lSBb2E4R@sz zwQ;}3NF;6o5s(wY_if?x72Za(hiGoAEVaU_O^@{Fqs1HSjBK4QJgVDI;KO82ln+Fc zHC@wV*$)A1+)195z#d?efDhkF3->b@PKEi<3UOq4Zlvv!u`s+fKgng zdpyB6!v`dO58Ol`#Me^^T|2|OBoIR9G+8PKBt4Q!3Q~S`JgNfih=>iS_ey*apM!Y=|3oRlIv^(Pcz!;s*!vk0f^|)na~f}Eu)3AShbGHyFuOx(Z)mc? z90;V4Rjv74D5;J%|H);ne{Ft$<-bKf7c?sJyO8BIEyitPgd){XJ|)${qY#thER4xq z4RS0TolQqq*tUX!TC0%Aw5nU7MQkOU`bqk7k)w*>?~j$b!2)a>2MaH27JoJd5T3=TKgMR&eM`Fn%Okkq`Z7Of-7B<-D3G z=t7*MAupY2{&WfayNTuNASjJQEKHr}mrE&vLNCNgS&J|eHFd#veM zvbKO(hA4!|vYe%akqIuL4sU#GlGnB5WjP0nr{4V3*lras z5}(=l_-N;%%GgG{S%Mg=jI81*65@On;$S3-BGR)zRDGUm1cBmn2paVdH3P+e`xun` zjf(G8SF1J*NqY$2G+GP2fFND`-Umc!e6kd4K-8_;%KhKMn}9T zBhH0SbM&Nv`QI` z&&n|i_ZUrgKw#Ew^~Y?L@q}5}?(gp{Y&F*YwtBFxH)gr10Ikcw zY$cc-L*R8|=6sFD2HUrrh1-qhhsC3)_)TfrtDOq98*_ZYRx?O!rk?|kCz*whn!#Dr ztY3&Fb3SHp+;52xY2;n$k8sEPG=Xr_OzEkOrX@;7%*`F&qg#Ye0|cOd#QrrBv7)43 zsCX)`K)$U6jmy#I9LAgQYIrQcsqFVi8|r@`r3w|FgKzj;d>+Y@FncZ$j&$xNj0QX+ z04uu{fDCk+e=X61+3MhG8H6?OX*>rpzft^AK+NXtMw2XdO6U*52S`Kk3?7EI?ET@s z$3Qy+;!;fpI_o&G*Zd4DAQdZ^9IMQ2@1}1(M*z1n90KVzKZHrF>^d4TsLvHASjrHj zRB)%%r7m%sv5L0~VFRt@{U0}jBk1o^+k)BSb@&1^xCnk(6z7*C&gC#U?EJGBp-KFD zK;86?q*DZI+{ZLumZ%9!4&I@3r+o*T4;#(zl3V}7LFY5#Pp1*Qj_RIICa`mGR!+w+ zb-haALqm&6nN32OF4~2LV?Ql?w0@A8G6_Y9Huk0z#<~Jy%Ynu>M0G$!%tPlA`cI>p zefh(;!nA5btkBge#X>c!K;kwnQp`FlOSO*GQlMZ7h)ic#gj9ZA1dUa}8iI?<&7u+e zjHWkK`0N5O_ZB|G?VCjL7G39J--cLVf+cxsd@C!#q*bn#)i$Ky@&#pq^kb+Kg?o+F zXQ_-#ml<>y;7c5=WquV0-+#gnX3)$N-^kTKZF?ij^iyu4NquQFohilQ^3_!rYo1Tz zJ5oies(g30$?gsI!UQ_c;RL`BKk#b51a(qrbf@I};os%cHw zt|Z?$qME1~Rtao(@C&o>uCU$Oy;|+sN6%@buf0sfTIQ0~^FgC~-!!FUo#a0rt4z7 zibI-p+E>+uX*nm7SEN#=`Ok5L9uOI_?Zd3VJL$pC_q`EX?KsSLI4f{4HMlLQ^AXT< z0RE8!O>0{uEAV>qzBiJ#ZF?6(_Tl`;L*?0_nWqmi_k~#-z+_W!+rD>#pGPXlFlJw7 z%o*GsU4U&y&!!pLg)R62&m>55FZ0E>JI z`C(F5)XowfdFCkh!`tEW04}C8LKPFt-_is29ZOJ2TUt>KQ z8+=TJpfx?u4AJPZpuW!sg@7mdI9NeLTPydIdfSAfH2=|fNM^E1CL?ks$uO>KKWRF? zLg#X@FxLJ;r6ox@BENy%9#BYbAC_GrNTMpU9KdI_`mMijW zX&Ob@!zIntlSxxGXMjH zWgKpeW+!QdHJRNJ2!v2+*5-alv7`H`N_XH;KX1}|Vz^3oAK8V9vVxSoC{!?lbL*5) z##pm(o3VPn@JP|{JHp`BR7=f@4wh3PlgHjdl=>wKB$F9MgXT)0)oY|~f^KBLDYDab3?hWDEAXOJ7Gp^&;eGu&hA5sl1q1y7ofMl+hu8YT^K~7yq)h*DQQe`afoE3ZFDquM(PC z6Y2k2rT^PS|EG3&i(x-KeAN`{lO)q+Q5U-@jpio8`|_QeU{YTahDugjW+mt!FdbVr zjAlPoQJHI_-W`{fuL@Ya1KMK5#lx-I&YYn>UZK*OQCB5*Z;yihAk+-Ay zX`}f!!mVgSOyrIxWdF}8qA!?>_ zg|ruiC-+@7n}uv;WrKkI*8$4&M{-X+Qvw@zzLJ_*!s}-SI$r3g|rnDMO>SSPRTqQ_)^WhD; z(M7zQ`A&5LWIjbLr$awH=E#w4{rvsSqc5^0G4OUjGykxe^f4{_KH33_1iJ4~i5GYb z{WSYrd1YEtj2CU5bYXdVb2k^3)oU=O04oHs|T! zCO=+36a|%E`57a2 zqxt9Z-sW@zBNxhBd@Zj)E>Nvh#(Lv;Q)4}|i~xaRhxiyx>VBP2@p*iOe}IqHkPej+ zCg^+%Hr9ZB0#?5EoR`Lj<=moRdc4;>7}yc#SU~h&Kb8LL z)-M>*QnP#c=lK0IjWrjg$lFdfQ5@>@acA+ia0-i_iZ{M4X^;CkPVk@mI*#l}VcY=k zAKe#t6Y;j6Mc)nt#Ng*m>+&qqngaVi&bn{PNJn=Qv5eI}b@CCE)_rjf`|(%~$$abM zIG4cxzLfMN)+uBiaoz?0f?m52Q6_kwmzR+9e*nkqSt5X5T51cRea7lZl5(sc5(P@k z;>JKqw-WjrYj^PJv784Fb{CDZJjrLNkne!BVJrJl@h*JUSTj?7JO9DAukfI;M(n1h zHB|h4`3I?q-|;5uZ+U3&x4nVg6d`-FfPy6~|EZ!hQjRC#JjSZyH3#hED||gRaVb;t z2!J&$a-`E-+jtb8OptL)sQ4}kK7!yPGcQHPcRVBGmh53<<89kXbhzdi=Ay&YBJjP0 zhmGVKBiQJ5$bQ7>;{%(@H-!;cCVcdOuW zt(Mm$H`c(kdCmwA1RHq~9&G4Jg+@uNHr?hv`l6?Q z`>-9s{YUo?+iyJ7YLvE{Rol4q)R8_ll<}0;`XT%HF3GB3DA#noAt+@(#%f4o^`T_w z6udaAB3Q0C7SmHj{y{H;TVrjKB5N-WjwFF)%l)Y!rA}FKKjh*yB9d8+M~5yaU36t( zBS~e=iz=0E!bsAUX=ZX3sZiNI<;Y^4)MVi+bsT_BXN*n8xO zPm0_C>6qS-hJ5rj+A=!Nm0)KUYctJ_@@_c?(mpX+K5&ktBOLbu2v-E(#E$9R_vV-b z9qFnct4LyWLZ2Ua;|pK@8(7#H3={;;ch4Z2oE^YT!DgP1xsz@0Uc}5bL;KV{XvO}1 zbR#~a^fA(Id_XmTr-|YT?$yHgpl2A<4xANpKTyeD#=gPrM_(GY=K-ljsWQ)Ytp8S% z?r$ZG>@@|mG@*wY#3bUyVi}DNqZw3~R}l`X*W0foN)}Ol%!|A%OvseaOHv($oyM9j zx)fFk?#9Dg_^Pq?5cHTTZM^U$W6cXf9BUGmgX;^-!W~AF9PMnvH{QbMjkRJ2>$V!a z@4pTiXj=@1JdYBu!e-a2lu?;}L~w7>RW74Rj-O_Yv!Lf6hqa)-o|rCH87fP`Agmts za~K-9w8dtMbkBYTEst4h%s>==986>LCB0*EVSJz+iKJMdNDEb?d5aV|_)=sDB}9%~ zD&~&2m>xC)vc5h1#zlI3{F+jwgSR-sE<~u1`Env~>+NsOqIfbWV+ix`ASr5Fc;paq zOQGSnBj*v!V+~qClI%8M?t*^Q6Kq$F@aTTeuy$34+q&z}$MQuO#*3kcCe<-i^m)^A zQ;7#*u7ke9)`m2io5}f6N;RP#ACr2Naq44@&!M4xhrGOnuNiB90EGEuyJs-_kfz`Xv`yoHY$tN$!eYKw6y%cQ2{b^C14K8$(X1xkXf z`5|mP^l3ql)8Tj1ILp9BPFWBGd}wBWI@z($tNnWW-e^8%G`~tJ z{bBZxgT-IGC?U}tZR?d8T#QlM`95HTYnS+naBcpbC^-XBrsy~}Fa*-QgE!@|n& zkRz(rRv`JpLnd%Gi-R_aD2~Fph+;3X#vF)z52vOjxGgq-e<4`BlH?=9Wu#^~cL*Dd zc}5xkM4mKn5%vhSVmyirffH>x?qT>ug}5)v3t=G#RC2o(QHe2k_SL{$tG@8v7}GCsQWt0hd(833vQHweJWt_Pqh@HVYs z>U(+AY=Y6O#zK~Js)PxdC^p-SrZpfI`LBOU6;GrbdxSKRONG@~GC$;yU?SSZZ)8wK zKarBXL2-BX9pV&a{}^WpdO~(kzOCXJ2wjG&e~~Yl9NSBGnP4zD&OY&d(<)E*g;Fz- zADDJk@1{qwZ!$&pDXOFhmH`ws%)7{!(BK?Y!5L(LepPrT(hbfxEf#q+A8F*gkL(NZ zit8N%%)-x?_BT`LlX*EKHbiD8Vd`)pyfF}i{Wj5=nnz3KZQK7O)sytN8CvRMWbF$r z#;(+`)nqXx8xJug_49=;&+>)J^ZWz6M_+X_(v{s0`5(~j(356gtkC=nTjgkZ3p|-r z5xg#)G2i#?G48l`RHGkPfH&)}gYwl@lKwrAF@yDJ;^S7R0QEV%-lSh$ye`wP*nNgx zrCyy=#R++GI@R9wahJ(w(bxhcj8tEn6p!hU8RLDyq37Y}3;nR48M=H-MgBhTwhksn zo^by|xZE3Rz#cmJGhgVs0yYAm=uc3V{=!|RQN|2pR~oa6LL0FlcT$`P_!fw!=)VL& zas{HUtz^6clmJM9I1_*DDJ=3!k_jU&pl2h|_(>E?Js|FZi$XQiQ;@>0RFZ&pAoSNB^ZJPsLm=i@82O z#}^!6tQ{(h7IIF0RvhHIHEx1>9lR%D!s>o{XPK}XCSEkB;EhOktX(WAVy==pb$$aM z8KtbfoRM_IM6{dMRLo(%oKB;8g0!=cnPnzjO>dhv=0MD-5_p|f_JkRNLy~rkX`Kio zVr-#Gj_wZ}>}MiG5n`0XoBv`({_CQas)0@63r5qUNEMo@y6-78ZrwhSD(eJB(_`+o zky?EH3vcl8_*CKALC9dA8W!G|<1PFrdzB=(J!8y#^Akq%HXuHxV^-gQ0PzNILf~DK zAi78hsEw{NEgdm25%?@7feUXI!aaouGYgLzs~=S_ZARcBc}e0pD4{$U zr40Qg>5O>>6ERjQMK#8=!57UWI^*J>OCI?gjWlSf%B@{>?Na@gbDT8QHjklOaM0OE zh{*R*4;W$v-ss0l+-Z@=$c<78pB^{6y7Y2Rl0vKm_iPh;WP0!;I!D!O(rMCX2KQnp z3qIAm$4uIbb+VUfQtcd$+b7=+eq?1oh$#SxxJ8`cWaexq=SgG1b&1QIZPT}uh`dwj z;>XYR3BT*PzTq*s2_(_}HJ154Oefir43q7ap6~M(ZWH@g37Q(~1r}f^5+S~NjHbYQ0#g$clR~B(Z{Y z!iC1#KOnoT><9Kp;nlox>Mli`eQ!5EZ!}xd3_lKL-!1;K26j5V^9*|D8T8I2Igy`X zokP{5=tlDm(hl$B4?VjiOUe+ClQaaMI|^>^=xIq$1&&*H@!R&E-D~T0fFx6XrDfKb1jTIo+X#$_SOv=EM z)%cdlP@khr&4S11yo8|{?C%vJk^cm?Yfx%`T5th6^grZHS|2SBHqMC|TlIZy&fnu5 znJbOOp4FeAt=HMtsuU&{m_b%&1XpH7E+UW{jW4+E=-y#Z2aAg(=3OX%7znp}W$tON zq7Ku;!OOa%rAykuPP=J@)AguT4QQ9nq8-pOR69ry?xhLzwA+8I>$I0Hsaggz&k2p= zT~tY=AW2B0aQ??rlsPo+7dX^8#`_Z0C~$S7*hVi&uD@?f*9K)b7E@i zmC|n8od1QR)YhEui`x1)pF!sY{v%P3jDlRu`W_uo~jw1~Mbb04c8KI-@;C%CD$9y0)p-J{z28mJ@P zi)}ujn^d;$Yb9eAqRG_R+9i^Q%y2E@MVgm1vse>@)CHu$DbflPq7-H4659xfu^@6G z&=ys>j7};kRYek-xeTaOlP_2H#mJXJ{!qpqtk2=y#9f=BavHp+MsGZs+n9<8s{k`wCN-!#yGdgXR`BFxsTZrw|+Yx-hJ&~W1gKc7V z4{P-VomY@?#4CYXpwvnt-va>dW)dISi9+b#)6SN z{^Th3=)SgL#aBu|nV~hMqqQz)fX*VVt*W@?NJ^$p>s0}kBOs6GeJfci^dKK2vt9maK@#vKz zZkCF;Su29%^(3`vo9S`OMLuV6%{B%#zBc!L$e?vd;2mV*(;VfggtnF4@)+C`nq2PV zeoS7=Lt~i%UWUprQAx*U6#0oRY8M4@HUCqr!P`Z4M3wCLP|FT9dITHy;!v8FKN!OJ&dYsXk~uI2$c)gD_=`!c+d zArdlWu*ncAWfEgl0w`aPl$`x*8W7o>4!ZVwdN~#|JWoaRqi7a|AH+@=>-j$;P~2N#|1KJO&yZ-GOTRPD*%|gpesjY^txT&d2#U{>QE3MF%6& zSVws0xkSK@!0{4B)9;CaWn~>P?mwLn%=7qpSF5n$lvR=w#>$p>92T*cGQ%*7cqB09 z8Eeo&!Z0Z@3y0(4!8CXkQe#mL2lce+6^ob~FT&SeU$!zgLQm>wakD+LBI%2Yq%X0n z<+-sKhD0kdtyy_iDQt7S%%3xEq9eQBrKY)bzu>1&I-5-LWEpyCft(1?w1J2y!1r3t z4`|(hR{VyAMM395zD(I-j|8JmWkD*HBQ8rtNQXI>k$)9=(6mcseTxkFEdb$Hg{;=N zl_~*_w0uf^-{J~?_Fbuw55w={T7&BFkDw%A&r{=0o3VP#M_JhyODb)pWFh2?=YB3r zl+MR1;JkqUq{xM&!w6>}A515y%DRkfV0MRQ>SkF4}!kGkvvW+I6d9qgrupiEu{DzX$In3@ZIt;7#>_G|ow$vib3BW0<- zUAFz@nMEuIY6ys8#SzdcGugVHk4BvRQv_A`=R^~hl`>sL5z##X(>DBPTh2|qHn*<$ z?t`$pqhnx;;6Gm?Fz>i2H|#e!)K_e~&mseP5h5=f^nm zYjYm9-@?Ch;S+Q|#J3!m=X{VCwaLLeI_^H+gUHs|dG5qfB%JR##n{o2~n=B&pPh1}8cCpC`TI@-Tz@o4mD z_Pwyof{i}21>+n-$xo^-d~~=_JqeIdy;2T6{uB@zv=bN&@CXglDn7`6_@ClYrZA9j1OivJ+7J|k9}^B25IEwnkG^(7U3kk;_$-;w!{ybI!q$Uz_u4`z?GLdIdFd zF5jv~@_C8X$Z+0+&hPRczEBr%An!}g0+l~J3a}C2m{#fEVUrv>vG93{(b&hJGWn7~ zQX?#)-fcNQzX`NQ6$?yYkC12m6-yfUlw`TfIPfGyDVu#9%hg}(i@`ZnozAStp=c@H;e^&92B=&Q}ZgbwmtMEsHyliv+7ND>SWH?8H zzDrQfKa!J%l)kk%FY>&4UnO{li*#O1CE7o7v&u6r|I`8BB=zhLhl9O3pULTC;k6{G z*4E(hARerzhl*6Q`^v~o_$A<@kL@nLi%{X|c%lBF*c3O%ziF^sc^=PC_52qIEeeOcO% z4sYqknoCpFTZXY#_S;Ar(s;KDo5{%+a#*%4KQU$+%~8H35Mgyj9e1^*^^3)5D@#r1 zU7BWh=B6c?x{S3`h--!xX7w|-z0Z0hAMDNME-}tG5y`f8b@L0^tQT;|#K!+5^TtaV zcP+()z;U=gU?^cU{S8zT7_3)&k~2#fa4|moK#on=U}Db+&Bo9rX4&wsXbh@{mqf%A z#>Ad1e_S(}Z;NZ4LK$~S?iHcb47WAD@Th;ZSUHd4%P8jCf#?8#|3KFOqxmXI_3!{f zw)D#19hB(}&gha|UfAX-#b?HvFdwW?OsxocGE#NI=S#wJ3L6QZD+v!IVfJ4(i)B_N z-RJy;PM=v^!ISipRr>36`stE>dP4f#mwjCNtATx0`YHZGtYAW#B?ag*+*#wx$goZ; z-0nXIIo2f0DB$)p0>^|NozPOF@x>7jhg?96j(OfNr4}8h(J)aQL z9hi`t(m2-5g+8fijUyhQ#G3g!j8F*_VNdt%t0-j zZI`k1LQbEM*74yIXc9YdTa&LX%}L^#H&q+4btPKIDm)zFKYmlm(O_5PH{jTUxu*~o zZQ=VP_1zW87gyMy=A+p^@)J5goxZ>o`XK9w+!Tigj-@hxh>x7h*8Ck_RO^bLcN~b~ zFa2Yn3;!Bk)Nx%(T#MeK{4bLD;(tCb+W#I3ANOAwi!b{Jwg1bue_LArIJR$LZH4<) z@!Qg*6wJZ`qv1-6-jW>I!Iw?1GC8A3Bdci_`42o>VCpPm?FYOn0YQE-aiL{LWEEcY zM?w$fDSwgcTPGEr$+ zO9ljvrTJZfW9j~Z!EG%aPucYln6Ms6^$NvjhtmBmZfo#*Hoo{@me4QgQsc3ao$8G= z;Z#;UkshZvMBRbUX|95_Cp69eI#Yd3{am#Z0>HO#Qni5(`vsyYXB}aNH?S)uvPVGi z^rWNhPET5VcY4e}^VK6g(Vu#y=MvB{!4GNOBqu?K7Dv^wj+=-ds@Lx_%H+I9)!KwP z)&wQmyZtEbS*=X`1zJ-gO8{xQbmR9B>BaC{QWC>$$@pG)6;Wlq@X7;(4I}bPCXtnW zHu5|W{gx4byOuY_Vv$bXB4>bRAA4lc2i}q~AZIP;TR`YFHqa@XoV0FD8)60<*D*G2 z307<}npVN8*5p*{8Vrpq(}SfCwUvsYk=7jO5~ur42)PGLR=XZPTDr*rPy2$^8@!>W z+*X2BaGj2_PX>3%O`xS4*fZBcO2VLKFx@JBNK{qfdbxruRLVH4aTP|O=j+8-u#r&o zxSye+Y^eyo&BawmMR{)A!X+HxX<`QzyK1X{@|b7Fs8L+t=H}M^##%8qTOLNI+#bQw zTDd)9l_bU028cK|*COJU?n`OhDwFBA{Rcj}zNT0#!sg%|%=7ePB~iWe+3;=ma-tJ= zGG@B{?W?STa0?jSm^OUxonlSrnqGbfeq48g=HD4cw>G zN+v$5be)vnnzhLcu9X3Bcz{bN+G7(x?HDBG;3$Qsv6j(TR9z3+l5z`8DBZYyCJ;d+ za7%02RIYYdlBw|zUvZX_16FA(7p**S18G;K-m0!ZPD3T7=_*hSG1e^mXpbjoh>U|e)v3d^+oqI4|YBrRE)gsM0J%!(K`F0~HJ8|uDfFG*M zi3)s;Dv2)dapAht8=pv*8H{&(q}Lrz?f~e{w|6+RJK$w1Ag6Xjo0`uEEk7im;gf;+Sg4&J8vQw200Nq{~8<#bT0 z-wyeW7>wIc;=trM@ZoNt+JGV%84pYC*zsj>Y-isM92oWID>(|a$Oz!2}g7Djs+ zGLl1WlNOEMG?^QWjN5h-p8*sL+{PT>=y@X&F zc%oluQW7@T40mvH&KUL%OdArBQ=?+>!MEHRoM+ruPaoSwA6piclO(vY&d18OjwG`` zE8vqkKshC;!l)X(JVRO6^|)AEih5vW2wn~B?3dd#bIaL zFz6PCrP(kx6bj~NsWeDHF5Plb*RBUUTrzwl@qAIYKUi6vwIb<;_ zHco$B`jocNe8%Uj*60`Rk_lC*)luXDQ?bL7 zLFgyOnzBcUo#_4kGovB!GpRwjkk|vv6NuF_fkL2YZQkDF)Sn_4DV9(@J{L?wlO9-@N zGMjTpObPj&8CF{19{>4_&c82oJSb_1e)XhEY(R_Av_pX)%2*-FF|hs~FG{e;xs~06 z_dGRt4;#&8aEBTAC)Z=`7EHK&fds`$BS$>3hcUDm2KaCIReYXh#Z5}3rx2jPw?QjgBgK#gPE9& z$PcA|(j$#e=|%!KH)Zwn|EEM3)t@M2 zG$0$o1IWbDfHEE8A{;gTiK4wHZl7#IM=`%EIEJ=|L`n&FgF6fa^T91S}$vKwLXOg`h+Qt$}B5HR+1ti;z zk*vDu#^{cOg#K}SLdReL;pG&?N=Ea8B;1iHeN~##{NIubGrOEgv2SUII(i)YT~_2p zfp^Lf9s5pG8@Y#W#0HSGWK3w8a`P3s&_Ku-2={}w@lp9N39fd1L{^Zu=3p7TYnUt- z)o*edYRo*!RVW{M|3jGCxJ}B> z?y5|sXEVFa-6k)uSXp5WdDUyBxCr2}Q*rU8Q*-!!W_)nz+$;xZKC@^0 z7+1e-@dYcmxQ>BSu!1$v>IOz1eF3BGH<<)|E&R{^2nS9><4_#N#1&haIC)Vr^Dx*V z!ld3$@%8c6!;Hnm4*->VU z70KFr+-mhB14%zEatCR)M2ZNOU~UqlgI26MJyxbWwBXCYm+*3k9K_C(pkJ|6L#9>j zdpnPm4P}+-ZVr}b(u6e{{xicjysBn(nuN~GzdeJ+qvPA*D-_f}jqW;=wG7N+d&Mb~ z3!5Jyi2qqubzl<>*S}4XSyUi2xSo9iRRyBe#d1w}kE}aS(s3q9I#%}GRX9PGlW@kd zm3^oBVqU|_zC*ueb^Istj_|ji20~BuvaiMLFUt7hljY;sU#!VPgp0)Z5-hz}&VaaO zBa6r1`lggDIEy77`u0smzG?*Ca}=;8c=*UpTEk;BW3PqHF8rkb~Xq2fEPC7s~TDb~$zsfo(Qt>L$YR!rr% zMW~0L^CcaNz9IINxIXz($}OAGCym>FO0bxH#q*Y^Q^y-v z;>us?7UAFW;E;n;ECOT2W-oP8Md91*@PP#>$~qW+6$?1FFzjlB-Dv7@5R-}w!RkX$g_YR66mn@3) zsbUM~zvUWM<7Sg<@@Q%I#BCrITg!tJUUyp=cLX9SfzMNxuJBg1mxducvbJ zBYS5U!Lf-V(8d~}?TvebjhkuW_e2JRf~_ah+%b}ECR0a54m^%yvXn-C59QvHI9^Xt zd{G`ueLL8$7R2164z&W6ggJo>Rz4UV!4|7xr(L2ovNXi3nzh-q{-(H!9T%)= zPTD4kV+|}14t_hbnyiI`DaVS3oVyuask7h= z&o(pIZ;Y&Qn&^leDCi*%%(gUHU}9QMxnG8Z5nPmbN8~dU`d%ZmQ=g|OaEbz_C~%4b zrzmiW0;ec&iUOx7aEbz_DDZz41$@?ER>7rSUR$*x(eoI^27ToqP%p4(czg@F?$=Vn>!;tl;)mRz_mcSrXzESA|chFG^?rC5}; z>T|Pz0==Ut5lPx+ss&nf;m)jm$ihg0(LRR3_Qe>|lhoYId@ z>4&HE<5S~ za&Tk?GsAkmKDNK4j%yS+-IHxo=_@*92jif|LuS=6wY@|(m#}>O=n?L(e8r*mm&oe> z3MTOT-dpnP7rHng(nDAlk)ntyg%WkWc%+h{6cw}IPQ!U0j2 z%6%QF=C(Id&HQK0sy%A0yeyPAle)Mh-WOcKg!!e+mrr#WO&?Q&F|CwlMRkR>gnK74 z+Smk^4Bs2ba&y-6JDBlzB3;}`=#||6 zg8u#4Ov=K4zq#am+|&oy665ec!fW~ivY*iZ*N)8^52?O8TUpz{0+P_oXHq-vj`8W| zNe^U6&|u_73jY9`WE|>E`?Dn}(wVUTN^M+A+<%bf2~GS`l~HYDV$F%>LmJL=a=6-V zcG$?NYMkOHC$0MaAe)35O*R-L6;Og;3UATT^2S9omh%h!n#G2N(^3?awKgZ(6a-)B z%|q-ZDk5}v)Sub2_H>W^5osY)qi54!Z-xl5FH>!XP#Yl<$`jiUD&*cKU-qyC{K}3k|=I0pV)W! z3Z1Tdo%XMBOF;I~+u_4S_=ATmS<;J_WZe?Nn2q8rC{qi|D%c|Y-S|GFKEn}qMjngL z3eY!Ze{7t_ZM^)moQnUMxhDU@gVcFmSni9L16lJR9sjJ|nGHVO>v-;_QXrcBE1&AV z&^4*D_Pc&prQcs!HQ!ZR>0hv<+U1{LUtKxZ<*##HTH&gxt9Ml`tZQhjuXfF^tev~C zx?Xnh@>B7rHTqq3HLgX~i|Xo^$CF#Qu&%1oU+ofSp{u68a#6J%p6`X%R#si_s=M~O z>MB1;Hn^^>uC8^>tE|7aa$dEos&3&zJgVo~xy0htH7=a%s;%?8=2k0QZ6qse{M8^B zOI2aGpmv@_9^P=F%Uipoa^ZrxDgat9e;>&h!D^IlWp_FUsG@#af;uB%*9 zIeKAb?Yz-mP?%S_&|N>TaS@?QmsM3SmN*isXSxmk`o=21&}MFR&4StmI^`H0moKgc z?5eJ>ud5&7isd)+hto?vrDZD5-sx4^S-SkhO+azRWQnDt*i{yzal_)us%l+LQn>mC zDI9fIPH|k9`0FXQLR;5YP33}x)v9i*sAQ@rUV@(V{(5{7BfDK0iU832M);_%HkP2` zld7**f09BhUUz-Ws%>aoytu9&u9&vCx?ZK;T`82&t^-B11+_JGic#X&P~J*-JVx5# zb2UA>)3@vU5VE|spu^$QkJ^}Gvf z3!hci*H?0WrTR^Z7rpLUf8~N&yRoP$ONgm*lL)hFNxp7g?SdOYTsXTY<6h{p3jkZW zii=$ZuJ3%u^)0GVvDr2CdHI^lH6uv-(slkVjg7-mRkvt#Lu2je>uM`U`zjm!rNW-w z#dhebS1qO9uNe_`(mXvPCLnJvYP%UpN9n5?GzPFjt=~1F*tL*muW+x0 z47L6mFt2O$FGixor2swHJy()mi#@nWG)xye#1_-fe z6h$3HQAbU$BaiC(-r?$Mbqk<2wW#}g@fKIZ8X$mB@_N21rY^UptBv2hg>}~=8tbcJ zvFh4b13nJvsY)XE5x)UmT~Gxp`RCWog(Djl*EJvo>gyIEt$P<7?fRhvk=Zd|{DehGZmNB*(O9O2TH`?a{QSAA6- z*NEcXjNm4}9`!q~+Ar1VmCna*lzoen?qrVVqw=23E*`Y#`21{ss;yq?MSxX`1o@iT z_QYFhdS5rDLZfH=rS%I?BL1ZWdeBWPj$b87LWR)%`?uQA|^n4HgbD^+`%(kB~K!e4>UaY>BomBhaqKUSXO`L9RA zw)@MX%X>0q44sqq1GI)%xD$z#n17ryz2r*FzjyvIw0gC_p7NFa%TOFd3$??4eKdOX zkG4eWMT2e}gqA(%e;giKTEl*##hpOd$I9AG3rV-9oQ;Jk>QPBCXgp?5{-Pi6?g?h9 zVcq2Vy2iyypqvEnZq->w`j-&zk?!&B)mvK2Y2iKO zk|s=UP0fO9s$z2MEAZ_riXv2xcnfNm)Lq}*A)HJf?U{Q|{QBtCd)CMC{L)vHxpN)Tghc~FI5?H5$#j!YDd~bMSCc@pg|2x@^Jj7uHqEz${U|>o(Uz z9M~W`-Rbpp*DkDH)Sx2k0VhTcuLm2-@CH31SL68O!^La>@vtWy?}MH#Tw-WyyTsQi zS7WVRGEI@M$|n{)QCp44%iLKED;wv{@3!rj+O|c~eG1qUMkqR=obDlc>y}nR-=h3W zPh`*192v8{lVonFF$4u(R7+stBr7&H0o`0mTmEY`SgHX(M1^pw82i*W~`Q~ zW45dj4305`)i&atP`};j*HXaQ(lGt z+IG$X*l*$uyxTBYuZ<+4uIkd!pNU;40fllg*kN(4kLil8rSq|nC_A0hq0J$2dEI9| zF>94rycX2XtzHI6>p#JawGuj4DYq#44Sp(p3)TnuxZbrcSgG zc#@cgC_sV%2Wn2MsZcg!ZM?CoITmHnuUbWq)Jqr_JA{T6>G7`m+tphmn$`K3`L@@rq zQSK1ik|Pg@ZPsxJi8Y!IT{F_tIASVLj}(#W+89a#Jp@@?XH6KXIueGD4v{!Bu=}f* z%Vb#BQxe7#&_gzdlt)HheP^E=8$2ZzHzs|1M2P|8Dy$=6^dRrOGs(OX&V zk$Ox^N<>4)wlz!0)XhRM14LrzM5RvEe2?@KwYbH$g|V-W_tGA*Y@IBxyj&_?>r7H% z-Iz>LhB?QQx&?C`HFSG^$K1L`dJxCLy4rd6E27&!-qE1`<$cWg>H|l<&ilUl!jZ4@ z$Eq)!IETbk71yE#4J0y;vm+N=Bei-BY<>QyF{8$+7CTC&L`G@KIJ7g$U#DrGm+$Z> zv_@&l2>v>}20O|G_2WEC>gpC&SJrBV&$}EF_$bwJ!4wS~UaglO=POYhljc`SldP^k z&ihiK+$HmwG_bu&XqkzjY8TXvlIU7DbJ#c~hEN5UXf~oHAgary9XLDcST0LZJ8_59 z{)w=Z0wq+;abX>be@BTlt6Q|Vu9lfZk=wKxJ4H9h()I(jtQK#nS!AgpMn^G_(Ov?V(s5lHdSc0kRAc7Q9S8FZteC}4Y) z;iJPMA*43kG7T2XUU5pCyJ7(;pp?2HPe)8ZRgmg|fDl86)uHrct(Fpkkp8xL(VjX` zm}(EZEvb-@Vsru(Myv05UF%2!>t?BLSZ@-l$laVBPd@?wI4qH0;=mp)Q$dAPqm=|Z zzDtma;Dl1eq)m4`AyGnrZi;sYQAJ1yWAjo1L`m1~2)Z$Le`}N-dBS#i()RL1Oymh` zD0!YItdJ*7tT+4j?B;Zx6rs!5wUd{BMTx{w_1xzW7JmXwB1)XY9C?Z<`A;C1owVSQ ze?XZ;y1BiyV>Jhd6s1$`d+h>hq*h{LmiI->%}F9X zp<10Si%xd0a z;*-g-LA6Zc_84tzh1$2^`s#(tDT6E?VLG_-de?lWL+v42_q=&5TXI!{F=5yGTBc8D z`YzF{D;yKZqOP`P!93PtuuepockJ;9Dz&V1VcNN`6(j>3YTUqJse2rhI9Q97$sQZ@ z{B-4gKi}c=mdMhMnHAGL-Wd+tk*jzeqnA|H>zBFJOGY=$U*s^SRg^kLFRrhv8a;gO zXlkv&QSTVOaM?&6*YLsyh9og}t_(0v_WR|5uIMd1SMmIVr`UGoeJIbDFKM`ZpXhnz zBw>c)cj#bObjTb3&pq#veg+rEj^L4eX471)2`zDW494|hcmeApG;$Rn_?^HkblClNRHTm&rk{!h}4 zJ)1tJPIx}$$^E1&`i1Svdn!)`kG!Ak8JyrhB;j4+NZKch`@i9LFL}KDPyM`SyN7T; z5E#*Aq*9GA?TVZQ-S{K?w8#79=~ z$}i>~9vzLgvm$d2*V~Wh+06H8e1DzCA>qeFqtg=}SHgQ=X>^kIe!`fup8FHR z+?((&VNJd@6&-T6x%y?vqVqm}a#bkorAko+^=}{+y3JTKat19cixZSFPUiJm|H5?NwD<9~%LvLAuBCp@1R-#o^e z?9W(K933t!Wv^q7)&7b6<@`nYCEYl~k+du;>EAN?rMJL5BY+LznSXCr^acsw&dgeq zTs|nRWPOS!kUDDs;YtQ3PsR~n2e+L#d^}qTUnDH4!cQ8M=D9uPw$#ZX=lV2HpkMOK z63;y_<&h-)qT)3Xy!B6AQMq`zk9c0(eOq zVhL`Fgp%<05^nZ=UC|@FcbE6}l+xQ$V-cQ-rSBog%Y@J4QaB|HRDG0W=B!U~XSxEZ zQ!+QECsz*0bh$HgN;0$DndzQ^$tyBlWwB4mgP-Ip7fl}|9l5NgJKx(?I|UhuG>;eGV?;tlFWkjY3|JNfqtc#)06)+AalGsvj87L z5K^O4rscAvw~2II4|YXA;oVU#d{_oub-hid8ZH~KqhxoP}=eVf|NOMKL5L*C0>5Q%^_TOKGeJ2u6A6y zyIdkjKKBrA9pPNO$9@w3ufSV?e;B7P^w9C=NL5UNbQ11m!p&x%)UZVU(&4U=aFYoq z`J|?TPit57-(s2I3VaCg`M`%Iz=a>i0$&CE7x8pWyPe0_@Q;$b;#)~)F5wSv>sbzg z-weDHcy>bi0>2ZuV|!10#saz*cslSZ39smjJo2qi38dBv2dGM#GSI!lqhBSIl3|C4 zH<5U3dFtk0>a`jqC0)6Lc{={0OUBckB_|-m)I}R18X+A@SzL<9Yjt z=O^A4;&q8?5tEZ8x2MbyK_Trv<(^a_pl4u8c}z|U-S3tBp6rUgM!hD+qy3l0CCrtY z1N;PenecggyP`kiz0Y=78gGYY=JX|fHp58+OM!bb3#RpKp5B4JzFMtg0$I&OWtmRC z_r~*uzq{q2$aAmeKgEXLftOKO3CnLV&z;0?C4PhOm;DoZ-wQl%Usv>QfhEwbNBOUl zluHL*Y$qx44iT^AUyLX89zs8w&V|VtveQZBH+fK+SIcu%O}DFRQhYAu_7OhJI8W}G za@-*DV{&FrAf-IhH7_&Avm-Ohvnw-wCDJzq$f35XkrgjVHVPyG3Bbw%?OME?!J z*#vwQaKTCAfn2H71r-0C__v(EU+8%V|Bd*cC-Lo{#QOqxJMfVLQ{^MwYrE4O0&4qD z;y(fZF#e+w<5zXZzY_mW{7;1U&G@IkZI?He|5ZA^;C&DNIWd39gP*|v3cLU~PW&f% z>;{krd`u6yB>hC0>ZdN%OsC>V_%8@Qp7FZe(iP_?)$Xe#BIX-l>Tf3e zgYodlT~BxV-Jvy}R}y|awuRnu)en3k@ZNH7J#e#+@SA{7?*sl2@Y#L9p9Mar5BPh) z=l20mMGh?nZf~iQ(rNh*yuFX`V}b7h-Wz`(@cn&+mxU?^`+(mJJlqHTPT+_8fZq$e zvk&-A;0|m#H1qgR_~d0fJYq2Z6Zofg`qItBegc=PK?R>PVnAGh=K>deXfE-ez$e(@ zd&6e}m-6<`cOh`0Pj7e=aG_^!{Oho$q?! zQop_7n}7@b6X`R8Bpw1T^|ef}RCEwM=(d#7P-^LVrw7e&jus|(3;ypBemwam$_;_1 zvd6oi7kn)6A;5(`7VnPbBG0{|KO@hz{(K$k@(%RX-3fAB@R~`yRm3Y5(?qO(%virM zh5r4l0a8iQaY+76gx|48FpTyruxK_d?be9VL zGVrav(v|R^0^b5WvA(1~;2K=VX5ioAUFWCu-^eVO>P+50_CaERpx|6ETn;jb_7UxvRg?oaTlHua*A-Yl>1J?W#wS2lq!nh-NTNMl9Vh7qagohiq4^49L9`-@3X`|cn z|2^Pg;627eN^W`s&Pttte0-uDuI z!}p@m%Hz|;-myqf@C>|CQn%}$@cRirEHOOwe+5AV&oA%~<1cMLp8r%yrflFOos23^ zE~i}_B;3+?IL2M1lRQ(xnggfuFKPG)H-1DkS|s5r>~VU9jAj`F0$PawZ2T8Uc^T#3 zp3G?fHn9tY#4fPji4|Z?%r3AcDP@S0c2<^I;7%BNlw^Wj$~)2m3Ej&FI!QjylFyv; zqS1QcOY*r*^ze}6vtHWs8YknPK$m;mO!f-hIF~0Dne+il5;VujnPP{cPbCTFTEAJE(E_&4f2Eh-y1ZX=c`p0h#IE zf#qsMG=U`U!GAvfe@<+N_Be8ilKX# z81*xI00S~*_3{o({!#qHJrKbUd}e04(0>RD?&b@l(Y44)$CN(!mHJ(iR;uk-i&OrP z1W)dODR;rIyQ$YFsqUw!?x!=;OEVV-`mIURcBtEvQ!W<=i+jYdv2lPRexlT5kX&~>6bt`P;6`D^ zM0tvaB>4HsZ*hGznvvM=ss6pfwtwBO%w9rTAK^C~T(?G}BjfZz@1cL67Sj)tUllnrdEi1VtH@Q!!x3k}U#p`%#=TeK=K<~l z{*eR(zmW6;%Fezf6$%8TznP;u5^Zg#IwucIUeF7K^yvjD)%4m#`mLn@mpEUck0jcm zu2kew?EsPIc$Vij!@Uedae-HRN;53u?FPArwFROkWjb1JENIp9w&9x>u*%T7& zN`6?gi7r3k{?43MiNsFsmwAxz!jB%{2Z0}p!;{SzLrT7L@qd6huf%u)zZv);;A7+Q zw7!2Q@WX$KMlXz)SL^RuPcBoYx2sfaZ z-v~T$93%2E14GRQ;CD*E%cUOSGt}Ws*Ba^115T5cJXz{!2lFPo^vDk>kLsY(^%1|5 z_^Mq>8vK;}1D^G_X!M3SzfpgR-<7?*x!>%ceX}V&(0}@dAya>TcG=xIWxqJH?4H4M ze|h?^vVNVpF(Y~3fP=5S`pU}(UV3r=3+^56o$g2Vd`HRC_Kb(tJ9m>$&bqE>tI#bo z3uU3)7`QZjmE)qbi_SeS_gjKb(!Zq?WgKF@k@wEEUD5r_7j9_51_vzXj;`oz+}S_H zwtzc+LszsNcQbA$?y{c~?k(m}?;vMah(++TG?!{YwBD_Zb2{WJ-A`qFm5NV<3sY@PkrL%;TGVI$2Da zqvVgf!ND2<+??cSH0u-cO=qnE?lRnExShD0am@kXg1Zhk{Zr_QTYwwJHF5JY2#>oJ zcO!25VA916<96caFy?kK)1Pw|>Em|duEU)@j5xUMxM6|grXL~Pw@4p%1MY0x{kY3; z&2vc?xBYv(AH{DZ_~C9H1rD9SaVO$-;?Bn1Kbm}Sa|#G2-`^)6anB?C=cG9aIPQk4 zh=ZGRHMBX#gF72{1MXtn&N;-vU3V?ax8kI&|tNKi!r70~Eb0F9K4F4f_LKYz9{(2&usDTqTaH0lI)WC@v_+M%uO>)Y~WD~v*KQAu})qtsM zAEq-smq@pFoGDCiPNY{YO?=e!SH-Wnvo>GD*Cg;}aiNQFO8{RVz&9muv)`A%?ORy? zb6OsGrI9dwib|Q{kg#rnN_nr7VBfS%=48SF!Rlu^6YSfjOnpeeaHU72|JDX+-x?3G zI`|swjBoFbgZ#*VPs%q3oV_*28jaK^_|tRDnbwZpyusk5V3_e2MZfJM1Vw(2Ycw z5!u)4GH@i@;W8V#H)rU?rX~{QYoky=30g#(M}^Klm+5Qk$KT$bd`n-8%d~Yw>d?A! z6m{gGDXF3kV#d3X@ug_#+SrOrdSC#5GTxrrSzj!=3>-~}hiR9AXX`d!PC3(ytgv#n z!63bDihJ{zcfivE_*ns5Cg~=!Be(U5aQU`B5&qc#zBGUXb`9<%XU{u{TWD|}aQ2)t zOk@N8+4HRypW^H}REtk__B^V^r#X8L)#4{Rdw$pA(<{&YTKp8(%=>OFj;Cf*9MR(j z`L&l{BvPB!;7)V>_a6IPXmB%K$4@50u`=T6iSW~1YJ%rW1AZMf_UuIbSV1v_7S08E6Hh;ztMld=ji#+MU8_=XDi0OUmFf5_uATd5}isKW34{M3Lbs9gHcsubdalghNXzV{Nhae$rIcg@aU)IbsSBB4ZnDq-i>p0Awv-=_ljivj$V z0RF1SalL<0t>4E?-Xxyhq`;4eze~JK+{!-{FN{*pIl18=+^@o&LwqcO&m~^^iV}ZG z{zBlsKKUMD^|plk!k`l8lE0StF!6cBzf8QGz#k&+zN*9p2><1xcLoJW3v_8U*qgyjK#E`ZDXw&Yv7P4#!v{+0m$jsU(ZfImq4 zx!cw70os2ez<-kb)Yp_~_rocefRJ)#iQE123gWp0e>?F)0zW`}D1pC2yp+HfVtz#O zEhq3^;$y_Sd0zJrZ{AT`{^y7f61VctK*tdK!wLQ};v)(CR^p=x{Bh#$>$Um5OFW&x z7vTj)@@*nMjpbiMJV$&g@h!xgiK83)@PNk|iHV2EFOWZ*X|EF>B7QRQQ!ym<^^m~l z5ib$9@?1-NB!O@8c&s2iERbI&-_CbefPWwPqvW4TJC6nU@|z1O{}}m0y!NH6HKRL{R9KX<1R{cjv__*#JfI&gWO+Wdq)PyZ9(H_TMN&EpyW4B!)$ z=WO!F662N&h!+z0r2#ul9>;xYkDeDEyFP3T@QdV^$X`MJ_X7MMkng^wL_WUU?*sh5 zk)I;p%JW8m|2Fw)@~uCg{lSUrE#q-KU&#MB%fBkXZzsQ*eCy|T8P0L3tOt|uAn^kE zc0YWQ_)r4>2k{bd|JxAQ`8V)cZnm@eTkGf3PS^fF$b7f@T(SO%`0$SvxPldZ9&yKk z#%IXC%y4-DM6l_ATg?8$iu>P&;P7_hqyJQV%12c0Haw#lzNxVLA@LD;kwloK!aZwz z;@gP7MZ9^x0{*ujIQ$qs2*~w5I8*K0_kb+%(l3<1kalh&K1TdH;&%{l;(!;`>qCk7 zNLk}8#19Zp^Lvlg|1-q1#OIO!D)3njRyiMH*Y7y-bOJvM6(anm1fBsd=Q}n_&)2WP zMIK9u56)KHueAlfnt1w5#m{4TI*8|p)12FC_W3=))LTsc;kBv-Qe>m2gq%y1Y+1csOor^A6{{-*Q*v zj$qEbLfjC?`+v17+org+s-w97a3m#)eMjET3qbw&BOULRMnfF zXx7H)vADvxBAeUjGGTWbV9iz`O+>I6A%w3^uN{UoCv zy^%<=!nO`@wTM#a4s+52TMGjY6Z%z8-c?81=mb%ZI$x~1+_nnx-Xi0!xKYj3K2a&5 z_tPXP{AtyaH!hC;(4e26mNEc}{N9z6AI%a(qy)f4&Ra$wHEa}6uzA<(DS)YTh zK1o*In`nI_-rH%2qI*?pg;{$z1*`j)6x98^^rd2<4NfcZ$T%#y7iiIGTnNuIGJv}Mx0EyUK`?e;vF)FE=Qz` zALC#fM~z_E(?On>J>k0=m{<9|X(dN>ZyoL)D}q^y8GX*yDRlh@15DI=Dj?ZE656pb zM!jOFpXzph8?958wb(ACI`%1S%-6N)HzQI1(jl98gk0~P5(f=|;hVKxZ_#xO`PMBK zTU)xrrwVJ<#O^Bk^EPyyk(|}RGX+WsgG)S)dv_`tN3bt2*zvy};jWtWxCh>k< z2(tMaP`9D|J3_Ex{3a`|I8YQf(7}9U)Qyd*To+s~R|k%r@HEenrM{?CMsqR}RB$AZ!3#y+>Ph#|S9x2(q=8Nm{4EQTz}_FAoV+y6QY=$HEOybW%&40ZM7 z)=<%tR|5d?SA#mWs{^_lj|hb_~*KlY{Oj$N{r z+pX;At+l4i)qWE)|I$)Pwtad+W!sD$y*F&c6%fA78-Y7$NLQ0KhBxy_@A-o0dlcb% z4+0@KO7ViV;2GXtqquhul{`+L{!w_WS7Iak` zw)g5-B^$Q;rpWuaFI&8lJRltiFNJu~k9z!GEye1Uj6rar6PHEo4j}nU_VTXg(|Bh% z2G`e356k37gsZBu@t5Y{;cD~wITGKWTkz%{qxfp98fxin?TDq~bfZE`Z}M+JI8k*-wE;Q0V}gK*t#u~`!Sy+LDFRg z%I58CKJF91WKP-iZGD$XTgMEF54E#c&Rqyp=73G#)`6Lv&Iw}Ev;6HlS|fC2&9Uj* zdNGq=&*bIMmo?26w{>MEZ5~?E#fIgJLm#fkUs(ff`nLYeWRueT`3Wbfe={-D zw{>bJZJoK5-_GCazn%J5G9O#VZqnAdi~cG2pVZ$3Ow#PTlC66)IbsC^M*^l_eK1)- zEUxdGG~MLU^R48>O_9+pkiw{BV-x3;{Wc*?ewxz6b{xd@ZM~hzG5Zis1v`J!-@ii8^NR=5H%vYo#Px06VE^N)Ur=H6O8+rnB%xnfUpS(QeF^<0Q=~iy zDP^+q+d9NPS+Iq0C{f@hk5>OYjYDB(tB^l3{kaL;ievIcq}w;HIQ84i->!d4OSZq< z9tC+viSnN#3)>K0;QiZ2 J@F$u7e*p#zvmO8d literal 0 HcmV?d00001 diff --git a/libs/classes.jar b/libs/classes.jar new file mode 100644 index 0000000000000000000000000000000000000000..ed040a91f39d73a948d521b22036994e3ab4659e GIT binary patch literal 41611 zcma&N1CS-#wk}+@ZQHhOtIM{nuIfUUZQHhO+qP}j-}mf$&ma4~c>BDJ$cz;kb4KJE z`UbwKAPo!x@UM#yf)MDhUw=LS>$Q=c4ZXtubp_miuW+`rx3n|2b#wWLwHW`$S|=BK z6Gs;(6UTqpMfeZ9ENq=k9BmD(|Ggao;J^L?-{^b^BPaksAPfKi)jw<(Hn6rfG%&J~ zv2b#xGqN^ta;j9baYGhG@gal!u|_9LNd_9*TqF=jM2P|_N!e7mqE8_qeG57$$<#1! z>w4a$audMc$KQ+2A0mE`0PnNl#PdZK?dIB?Zi6iK_(GW4&iuS}+&Z0^y2=0T^N9UR zy2dj`KuK6?n6ChH2urQ}Li4a|e9rl|jbpyI9$nRnKl^dZDofyG zjUEpo>~!M@kD}HgcE?5Q90fZ~{qAZL!{(j!iNfii&x85N;w*Tk!GViD^9I9NN3B|X z)Obu*Op!A=CLP*V41ZIF5cOc8?gMeYfdC<%@kk?X(OHz$eGs-j3G`qgo}rt>u$EMC z){>eMYx+E6U_Z>e=KKaC3^2Ff+YMWkrMvhrBaYe0c9+a)GU%rn^r9hxg}qa&cX1aL zmf(dXZZU(5Pt+Z^R*7p{Cz+$juti3$#Fn+vPaWc9u+n8vT+xJ)*(aHxS0NkvJ$2V( zhiPV6U3L&g&R{pzjAg|kjQ(1k^?@8vt)K)PDraeRZKXn;1;(lcne{vq8wl3Ntf*&) zRKmlN>%i^CvNdJ=la?^30NYgM$)gHF&A{S^D>5)vH$l4ejYn&#*v;9o(81L)l0vCT z4Oz{pg#yZ>PBopC@Gz`?{YguEqtJCF-r#sjH&HGavnMjDM+>jG;&ZJ~vF8MX@Pk~T z<0x__7vFfityT;X%4kHTwM1G)R`vp7$QQb159t}mY$n<9!>R@us$=)asTP~jqj;w1 zqw)JJgBKk`44-*F75(8hvs#(NkT~tH<(4CJ47x!;)caxa?@(pqLd1kZu2Ir z<#v4!SK*C>vSaZ5O5SRq(5D8IRw~=iCVTaYqIk)DMfxtVWrgKg0Nkftwh9*Z3RcCc z*98iHelme^pRt+cF#bM}A;=8nVu;%8 zss#|Gvrd2@$nR4u-ud-_3%TQm|BB?HE2Fn`s=2BLqxYtN8O{BaA<F(1>hb45&L}QSI$&p+e?dfC(E|KwaWOXHs*Smdam3e`VFV26A{`{rSTo~n@u044fnPw-7=fA; z1ttA{HUup#{;Z@hdHm9ck3V;b^5QM(0g5*-FQfw-;c4D85bF!@KgjS`;GM9~j*|ie z0O0?FB6R-{c;)Pr%}uPWMcs`|?42#_Z2t`5hB8n(1mPA_!5$*2% z_KayMG&q6w^!2M!O-at_FO590?l-_Mxe=EHQZWHGa~;>~$FGkz@2`(vfIDErCI>Oi ziX}M^;XB4cdL_t8yWH}n67B<6dc171Ehr#MwCFsihznX*)wVGdbRSoTBRNel#Ue9X zIU1!fz@%o*I7~M6wX!pJ#s?&hT(N#Bg>o5ksZvHo8C^IaG%)II6dR*vVhS#;`kyRV z%&aUV{%O>_SWzW=WO``Ol08P7(eK;9Z6L1{Pq0_hun-p^gN%=?EhmbX%XYQ5njnXM zGKs@@EvI>1K4XoHyFf>i6C-{p@H1ATX1tl{ZHreEf?IW;xSezh#hM;LI=HN8R4TQ} ziZmDbMVLXVnss=w%g8nBndO`pE-S5UtjrotgztW<93%Q5puDxHa2nmOC^b_TJp$(C|_Vw$w^dh2!N=Eb3k=@?zBk1 z0}F}-4PyshSZs!wjJemX-4G-BUXVY~--_rBSs3}=^CQfXK${Qe<_eh2rY4>ecM!ty~Y!H6Im2*+j`C*4{s98{UIkD5h$O+x6TNHtFrTd#J@<6Q&H4*kKC-N!(Zyu0==~zpe%S5D$W_JiCe5+_ zhTI>-Gt?U~CG-Z(_xl<87TqEf!iL`}C=nSc{~g45AL$us^H}>P3FswGKj_8()$=t* z(A6|#pU6>-YdN3gP)Fwd8J_}`M^*|?Eit>T{~iFb$Z9$QM@z6xQ?>jyBWpK|BROA} zJW@O;V%D|aLElPB@`{Y;4JR3eCl#fXWw&s<6q-6b$;F62;&vX=|D>6Jxztbjb~#{1 z1RU@dVsZBm09YwN*^L>+T!|bBMFEk#XC1YHU_0Db#1}RwzF*NowbeN}BWUf+?d7u* z)7Q)FzqB%vF(72H)<^Z(D|PP06ZmXLMCIk^mW?M~Vuj#~#!@s{uSwxR=`pn%*sFo|lSc&ISoSNQ+Snc{GR`Ps+Rd5-L2Xu{wWm<4GZxF` zOTV5R4LlIzV+0x-W?3I{U`^r<$yd{R%!g!bEfby(q%+7WJFApRz7?SX{eTaUC?62R z2F3@*ygB6be;I63XB7M(ycXx64&wg1^q`mKKI8vLZuk#6(Ems2N!nUCTNqedc>Yf; zid2-9R~AI!ZN}$$r%;;p2aQatF=usPyRZx?f|QIeLXy^~N$--oV7Z=*@wksU(R1t{ z8~DxNgKyGu4;7RGFtwNU?lQHJ`u;vWtq1UQLn8*d2+>G(ra{<%b|(|2k~Y1!@0^Py zui;S~`Sc!W+3be13z(a{BIEAWR^xPEh~VK+rX5R7PUe(Y)Glnh%(SSNNW1{%S_E-U zY_rfZQZ$A;YB81?87ph-Ns|e8t6Fz-%thXT`S}d{6K^IhhTx%{*ur$`ELEc7cFdg- z!u{&q4f@H8JdonY6B1YR7{Zwc0fO#EzQoF>3P$xGU@_xb{^$@sH7@FK#E+=e^|XQQ zetdm2ITrew46kL~&r1j;Z?X^Nn%VW;Q~I#cK+5H05o;Bsy4OBLZ2ECNl?qYL&IebG zaC_BX`m670m5l=_VnD<96WCcX$*3teB5auIebNMYAB%XGaH*9#X4^>xHe>DCH)UJu zXx!v646_3bFVPa!dWUhH{G$4I=vtw3JB>|}E3`bO(UjPUK6s?`-}O6%1^ICDN4Kqi zaEAOp>Q~Cn@V{#=Rh|FB7~c8>y2?XkQBg#M8K5zLeESqxr_T1s^ z%p>LelK2d_z4$xPOj{Pz_@KEM>CRW1t4`Astxne$(|X&!04YLru;8+ENSG|dqcgSR z&#mb8jhCIYS5SJ)B*z7Do>{q++;xd!&`NZ3nA*ZY20ifOS#Zy;{kV^#?zOAPv7w>| zsIDRiFqJ`~VbQX+G@<59>D?Gzgoel`qIe*pSH2^W)`c@cuIRX!&UxVmdv?1@7KoI1 z?$-xLawP7jtm=gVA~3Xj0xx302TzC8YY|C)$H8OliX;h7NtVAd5?%Yx+D2=#IzhZu zNnt_16G-2Fv!dC{F%jThf zoMuoM&}A|6bcHzwHO(W_=_*aK3kg13EK0)6{5w`Jy zHs;sy-J|+T(Cvw#02=W@Hi+{a?5Fq{N=8`R0R?xUSH+^w!cIlrs)v)1U0qpoUkqWO zi3R~DTWINQwK*>Lz@ujzkY>gtl%yM~2662c#ZX8Ko#?}7iR@BgfF+20NPb%9t#Td? z7b~}%ew;PDO&O>>W1@sX+C{iA6tl4za_RQpon>t?@#XnXFvR|onEg|a;;*Dk$;8RU z`oDV=kxG-c^8yH8Y!3TmvgVN8g(&9Oc}?KnyGv#MK?IrCZTuk*V|V6F8M6lWlMGF6bD>xklDnoT zMN%1JMcWV#Hml18pQj@;u$uR+l{2ZYAr7lPQWdA(gH;JGtsXd4j&eJ~R44te*TZKL z!vY+Nj$#UNg^Je1i3cQneo9vUVZ5s^p00` z4=8s_3>t<`1Xb8AvVcZp&yTF4;n5dzEI8Ek+olfHpVkN*KJ(7*@8;9eTetT0C&uRg zF(Ib^7;FDBpTFlJkqWZ%K!PwoMK??o5nH|X2|^v{wm2}6kdgeskgn^ci394HjrnG_ zkNy>Hr=<3J19F3(I-4x?{;IRk0&r;iTuiJGFIk9ravI zVPG38EV=g`%A_UMTRxH<*y&DGrixb%2VTvIso!?aZN!+4%jU=0bFl_(ib!9t9&eDbzr8{;`55UvMA2yh4)C3Lw&(A@IWc2GJandiRT>SPpfNKjCU(o@@K?TNO( zfc;&XTHoN6uzw_3_~)khkJ3~zaI*Rfx_&FP%K`m?F5nTUyu1arAR+AH+I%JvWF#Q5 zh|ijhk&1febPs}U(_Qcv#i3MMHZ{-icaG5hc#Dq_=l z(P5|1#pwN&;#;gu{3lNZsiXuwAIL6^FeVzvxK;Z&!-8SI@-B%ZIR}30W>540OJ27 z(!ZoxsQ$xEYZ3J;sz4%%h&w%_B$H&bsy%UmSdWZCAf8TV}cL^5LUc(wHD@rOs9&& zosLZwBME702DcY0orh|0N+&Uyf?$Cl^{#TbP*#@Fy{{qV*)8Xy3AZnXsk4*Hf~#nGV# z`>abfDkhTT&F)_rq=97gkKyk5y_xuZESus}U!UE`!M_51Yb{>7utKBqcf;n_qh%bwXuQl4eCYGOfurph@4 zHECr!+4Qhh*kGELRNmTVQUsm!;|3<+c)g&s%uaL7iDsaKmA^Xu-q|UL?G3DXouY=f zTXPDz&S1RkXbj6CZ`gV0g+rQ%l__(8a3fg&B-?0c19TIyg?gL)Ok`zG!cB;;l5wZf z5>NIKUCYA1Hr9e^J4BUT$09u~HfzC|`DB840u>ePP7-Gj7?KBK4KZD7)@O$-k(v2k z5WLn&iTJz?i;P>V5#DUvSdT>A4#P+?Vn48sk=Wk7v&Gr10Y69h+%kQR8oFu0viJu} zkY!fAYM`a;c@5m2Yzi=%0k2`j$sV{v@|3riuM} zY6i_`E<;hcyc@uBJLWO2x;C8kyRAGinxABmxYCo}))pp(Dr$VR2y?PzdE_YX>72|D zqNmYisX1Ng%VU0zYf`nZl`I>5bf)yGCc=yIFbi#Volo%Es*ftBn;ubi;!&w{xT=DZ zJbya;2pZC(;b~V<>8#(e1om!cGS^vUCoY_Wx7!6&($`ujiBe zy6``54PT(>r~BM3G+1~fP8&h9iV8Zq;gMzrRibv%@8sqR&E6@7 z{J^jyWAvZp$iKTMxf(QET&4#FV@^74$zYgI+pUGNrS~Wrw?Cc`-JIpD8qz|J_##uY zGx5s=Ja>j4k{BvOv{BY&H|Z^(mTb>6&F)$17+mG5Ty4t>nC#q?nzn3GOQxxoyR2j) zW*m8|(oI>w0u*C1OPia*NZXef)N0~sr;ue6+S{Az||CtjhrwE)c^Tu9~uYJ9XrG9(Vh73N6Z9CdksO$1#)02AC zmPw7z{_%-h%A(%u(tqN`;XR1{_Ok~jVi*2HmJRdeyAR!4^m*S;;Kg3r9dlUix2o2! zMU38aTmZcR2mBpE9vL`L`(R%nktD|3sP7ML)vsZ?Z#*Hkn6~WlJ&cwx@sie=%2z7G zmZc&O0MU#`M86bs;0f?w!>v@kCnvnTJ6px4-#5EW?5zvfz3kj)z|i>a3abw#*TTqM z3*+OZw)Fhw9*bQNR7HR_c}F1bAasto$%HoJ3MqR<$hcSNeR(B^=+$JLizP0t;6Yq@V|vaOkqud z#)HJRf1`Uq*Nz|&?KZa}4`qMUL6hR!hG%ljnO-Q=mq1QP;;$ebTJHYXPIE6wR2X#B zi@}l%SR9R-wQ;|q()C8)c1NBiB}uCCP0?%n+NS1vAqaw;Zy=Hjl_fWHXS>jw$Ih!KANLT_{fwy7pxf zb?{UnQ-g6I|KqO>puRIsUTB`a$Fk+ub(jZ$4FsPWi&r{WWo&kK=LHBrCG_KoHn#j+5SE zOV*iq&TzWkVtd>ca4zS#J@p8e^*tCZeDVEvz!jx`gWmcBxY6+c4>^pgvxT+OU#Od@ zzOIFGjQRbu9T@mLXGBtQHD|DfW~lK`g`*go^SfdOK*-vx80V9eE0Kv z=QaDs{yaQCPz|&%G~urtC{ba=Fwimiv*Je$spcWWPGa)NKx&1K=>{3MZUKflmKG>d z3RT3p8mP1$OL^Y&v{H=?=aLGJqx+!ZHBwb|D~$k0Kcsn9rZy`x5KAK2+jljct_~Yi zDuvQKDPdMi5Gu5Ch?4!bc_W#&tZxI0T3umUHinAB&~nqC)4(%DhVww$pSD8{LqZVy zgs6{LY(~)abCt$JZ1IfAQHH=DoI@*cMrm@#B5Syt9Cerq;_BC{2S z=|SQ(I{M?ljb`An3{)P=u>sC3WRZaa#;b1+m~w1~b4|#>syMSZpw5si6$2s10%(t> zt)&_>O1eXFEb^8i`y~RYr!Z%f%#xc8D%IM7>TJr8o~+)8^0jXd)C#z#nt7>GGxPwb zpD96tFthFQjjI)!{Ls5WCXZRM!Aut(FEWqU1NB&4B*o&Trw?Yxko^uq9Dda$GprH6 z$m?92CT+;knr%D@0s45$(w8orSj$@_DrLEtSRxD~t}GZCvKSwXcSlC0ORe_`!j!R; zl)5_<`~;C#Wrk|J*$FjjR0~*n$crtr+KO<`>)iD#VsqsgsD7JD;Y(sFZO-lxQG;Jm zGEb7uZ7x*qO?dBbO^pV|y2`FA^}aS%)m#MEnNnF83#BB;GKN-LzF_GRR6jVc)ubF0 zNeu5P?r0(Ii*=Oj`pdL%m}nSXwV-rX>=NnbQuIQu(%FIBmE?m|J1oCg8|A||OM<<% z^$^DUdXTa|yoWKbr z-lU1uo=N(tp)28}D-0cwB|8mfOEI(AymINYmi*Z6xwoLmdzqg2lx~!CWy-w4j@Gmo z$CnmVcr-=_x`ru*kWJ0@~!O&JQUu49T?K)nkqk|O3=2HI$Qs$2jYVp?3 zMja&}P&i+q?{ZCsLO=2}91CKAn@F*}nvTbiDTSNS9u_>i$)LR|D=lKw11ucSXkOD? z*fIjM*y8F*yxm1*k`i_hkjTC{j)38++t$9v-R1&qNQWK#N6|&wR(DxQ90Ijh-}f5S z$($1E6uL7fNh11-c%E3FEn0Ni%qZJv#z&kBoQD}TK62b9(T>g7>>eq)fdKtr-I2GC z-f;OqZ*)egg?-SK?$AgXt1`YQ!nXWdPD{0q>}ts)cDz0o@1S1ze0myHbOjdThOTcT zFItyQwr7z#VO4ja(po5yL)f>6G+(vHC%OEhYj01QEEYN=i4NBmCdhlOSX4Unu`%FQ z{FoYUw5-UR?)O%DUC3R%AQu?4iGjakwn&Q9IwJ8MK5w-hmG0n{%d<$_;Z>=q*tQCv zcjc!-ocT2eE50Y?N=1&9A4og|?5rTM z#ykmkeIiI zMi3J14d*@wjZncyq3s3L=dvBcWU(M#^N7^}^iZ+2D^B?6v^4d5M){-2Oz*b~hpg&p ztv@(GLGE2ql@QqZ?gX%2xK+!Qa(O~pVz7#)<2&}pQuJ%+vZBBT9GS<{^l)s-cE`FL&NvmdT8G3-BTTPc%P*DC+8?c-JIz3Dj6|i6SHA0Pk163;LkjX*mk##uzbLTzy%E zYs&)@cH5U{XLC?BxEuBE8+7jdor8@Ar(2FL=dzC?VT*6@{aj3f+~h~|mTv--H_WnL zSD5N!G}`ZVt1!5Q;7Hd~$0NO6%fpMuR!y8C&9AP(3Htnu{gNmNHso=&tMpZ5?p0*3 z&|QT0uJF$->A1&OQ{amLLZxet(c*afIzC0i6XGh1jVC!f6nsZ;JGbC=0Lry!{#vTA zZFS2~Pk6vu+s8AeuFrb$(dZlWXd%F*c0hdU!VhDc9=*i5@BcLkMuYllb%z81a7X>; zuF=1FRsTHy7pYQd7^nut4mZ*=m=cbSYK=sWrzNtMMRM+7q_U@y z|3-cujx>mhk#~kh<4+qmSY$;7N`~taS8eEFRwsfDoMoj_04n?HFq0A_4VIjB?ne!6 z>9fztq}00j(-a#@Qnb{QYvzh{l5;9OmGW3i&4=WeZfg$81&nlCm@r_L8=klq;*8DWv}&|^ z@lY#Qs1e${RF|@1&+2H_OL@5#$PhAQI{O_T?ufQ>^OTXwBL)cLqcBDTx99=$n@rDr zcqoJ!Ps9^YSDkYjZH}wxK*2%KH8|zA=4aUA`Z21 z5X>cx)f;^%0$j0b{TF=c3j<%=C_lCGoOX`?D?YK03|!h27y)_eD3iC$;P5SEQkuya zuQUNthKkP_FTK*wk!bnYn|;Z+bW<`xCjRVLKOaG*I)K@XruQF`<zSr&yqY|;>@B9{+0h<0L8Wd5$ia`s%|{pBg*Iw3;m_uh!anT*x?VH- z;U(FatR=%&z7gT|ltWRCR>E`{*_5u&@d(fH7I270pAnN65X`4&cI6%$_H5^3^9|N! z^CTN{wH2_zjTao)EvvCn(g8r)tzH+uP@t|dd)HJn1THg(WvS}p6HvLb9uxvDjaONQ zQKUw%5{MHky*bSoifdmeO%JC?SH|v@T^!A6bS{;v+X_v=hMR1(&4+QzrxApDpNH49 zy5eb`OBnSnUAZLN)2|~AV9?_no(q{G3tUYH^I=TZ&|zt3=IbQK?51&^of1KQkfTCh z;L-r~pHeV8h!kz&nHFa^KvKSVY&9LPmF^_-7|G-nj1YeWrCzG0W|1wLI%{AnYw?Lf zW5+{^R5~*iHDkoJ-8*4PbZRT)lH~y1T=H!b-{A9Po$~x zx8rEpc3$=kmG?`&t)!_LzEy&xjR#DD@aFf!PXT#ZXYnMuakHNV{bKJYyPwA>YxRLO zNv>U~Z>&3d^I%y}VRNs_H3X67KIjz093o<9EL9MTeWad@~6{8VHHgQ!f!%VMa#YWXgT|YS8q8A zyx}tQKo|uPra5R?;HF#?a(=}8eDaiT78jpcQDgSkIo?6G zDUkP?#Man9+rYky8bw3WIt!!BQf!D0y_yagt4PSp5?LMoh0`O*$6R3TXjvF}pPAqu zqy9y=KqR$l3wB3P2=CAoO$@Jmt@yCVfVb-+AC+3HyAhhCw_#~OQ7nx4X} z>h?vSKxh)W?WRkT%odOJao_B-Oku~|2j`}CtC_0uM<086Ry)wM)cW+ptS!T@PLg@39~!$Dn!h|jv8|< zM=*3_Bfj4}RbpM;z5jjv?mjn&e~@Xfb>%Dh6XLYLx17xJGgy2jeGe0zlJMq zU>BBIf0!0Qe+c^j#6$Xb9w%&PV`E@ztZe7vXk_wNCYPwvt2D2G!dop18AzaEUP}R$ z7t%nVE%agiCWdqD*I~sl09@_1u%9m_`Eh;I@PFn~z6PQDME`1-8Gw)91 zFXU{K1<3_JJEt<1Tx--tDGX*5Fc@YCDapzZ41<)m2AjjQLk?2*gGjFBsCJUZ5APq# z&C*s#LpdP|CdRQw7@Y~W@#}D?7;Zde?lPR)KR+)zj~AhYHCuP`go0}hR7;0!H(e_m zbGnRPv4^6BlM1dd|Kqis_@wPV#D*^eV6-CCV}28XG?58n8B=Hi|+()0Vb#3m=u?b2W+7FKYT0R^QVg zj2J|!Nx_2F8{&4ubJ@}J6MHy*T+LPq9M2G$@CTSUWfOEoGax_M5W~a-i)}kY|CW>i zoTW21dPpDodG}k}4xt!BpY0uS@9ag9kdL?^u(w%ZNV)I9nc6FzqIQ6jh{ zu_Ip6yZ6M(uk-ztR|Y2<30{OH{J}U&A4EPkPa+hZF~BS~g|P3DSlAaIz7$2uoysbS zp`OGt>PaUAH(z;xc*tiaV=7*fKs{?|zhtQIe;w#hZ$w^G`(y8hf9##(ABKtlws%Qe zdlzSAXGaqQo4?t-^7?;KCwSA-k@p)2ZRXI-DVo{PLoCvPl*&4C@`{)y0|@tEIfta! zIGPSuXnhBqNGVX^y#erlV{oe>sS2XjEwyudnNFpqZvNp-d;!h$P$M!9fRd1q#2Svk zHZseyv6j=jc?t_ZV<-i0?CFLR>Q*^3OAr$aB-leE^=H{e8&A6iJ@s%sAJ$%DRPnQE zr)p*GYNk;((+)qYOtUt`TE17=G7Pi~Dkun%*-jIu5hc~o5RmV(-NCH02l-w*I8DUZ zf5i4H(9=h7vATNOnO0_mXow05DE4W_ypI9t1r^w?83_-7T|u-^@8Ypxs-cULT(pk! zjxi|Os<#1MF<)$@_;E4FIQ3yHkw=2VSUkQ+DDpMQ&$|!A@Mb74Kv_PB^s%{?kQQy4 zt%T0Ot&36EKBkX7im?HUCce^ei|aY}-?qqJYUpXVN+rj6*!bpUB%#`37JrqPwOlu3 zNhWm|o~NxYYU5oH8}(t^TS^GOey7hC03q!~(C|Yw;TLZlg6_1EoKi1PLNQSv^ z21!gCu(YhMrzq;%`G5#XVNv=}g`hd;66^=GX+w=9U11~%V(j&PZ;{i=YC+}Z(jpLC zJ#R&*wU=}A`#eo`?0BKvsmVmnsb5aazJ_k=Nxq zyAC+lShv4pO4q35Sdd*nQX6q4#lpl2u&~CV%*?qig{E8hjjfQlhH_NwLiz1vlsu)M zS~C&Xf-0QWITl7)E+_zrUqUtvqWvPHQLLCzq!y*TK0gb6zze(BrdL0A>5!^QN{J#q zrgo|5v-F#5P)Qd!-*8Uw=Vh|GwSUfM(E7wP1b;2=ZXIE;rQlO|{6rf0|bxROU)Lef~za_iY3 zIlL+`&(_+2>`Gx7Y0P}JYhQbr_D*fjS~XjvINbQa&oUKkq)C&BD?@5tdnr3VzG6TU z4~7ZLF5lhFoj%H?^l*+w*T&>XdIr1_*aX!OVBU1L0yfkWCj@8_1>%5lpp#QXnVnkF zj91kIyHaY990j*Qp}>k7c*SpB&^{55iE7+DirnsU(W?vCU6@pz(UZwiL7aQln{TQu zHY5wQJTbqRkf;))N)VRvhw3gFT{u>HvnJ`~YQ`K7W^fUqqK?#B8&&fI*;LE$8Sx@*kFtL?Y$5F8}TrAt0o znLU`MGKI*T@5gx>CaEJrd4xC*+0zpPha6leTOrdOJCP=psc11_fD4IJ$hH53kSwYK zY-I6Cvz1a)IWpS9Nd%5XTvHtEM-ghO zCO6DlxMV)%9|}x=)K8-A-f*=-Rn0L6q+*th!u?100Dt4S z=%DTSTe6Mu4kq$EzOsl`Pf4Opr?IejYHEjMPL@+KsX|;=CP|KYTDuq)O~l|H7sOp&%d{8RVr3aLY0FXGx18cL;4 z{20>1hMqrM+8jMR-u(UO!&%YUs;sR{9bClG*I?%lNIsdV!X0(!z!WMpr6U$^5&cCP z2l7Uh!KYm#F1{#nV;IajgiW4$*YNXqnIBxqgTU?>*)fHO@uE*zY=_U%!`}^2(<7dx z7qsEQ6)e6*`}OV#p<_D6Dzwwf$6pmD9u#E+_Hx7f9MdGb#+cjJ+P`?}(YJ1JY>%n* z#+c0qqESWMql96N?n|{j2A5^803-JN7Y+s`^{A-k>L{A`^!qbpoL5Q{3}9s$Zb-TaFkkX2Lcs55;=KE~@6h@2iv;dcs*+PjTAFEJ z`QhyScVix-b|qAGb9uGYqHm#Ly}fGV{h_^6gG{Ja2;cqz$CH^aiR-2zTmxo4>*}tv zw4sLs^T#I_?9CtfK+tBD4XHmWhX7%HhYx;O_W^tXewXZKJAW|yXw>lcx~v+kOJYvc zX{8n2Ioo4pr^pTY4o0(lq(>d!UUw)_0EF|QvI8p1m-Bg{K*Ld*z zC9NjP!lubpk2|SOn0`E9%Lz?&iu#&z2>xuG;4dB>tBvuJcH-9pk2^`pb;&sNXCNhH zge}Z$>1nJMU_bD`Q#-~aSGVo2>z#xlP1&?6GE>rJWfJb#IAB1zJp!spHZmkgaTDKR zwOR54_wyhD*H$*qRVGGOTjRh^^7g%N(>{dAeZ@O#MixFBFbvZF&?sii8?nrD%xk4o zL2sRkv{(^C%a^9DmD)E!ZA8aPpXKA`-8?-rJb7LtZ`yrcY%_4= zvV|`X%V?P-8}Mt8!lD2yI6~~%NtW0SKfGxue(SmKS9*>cxiQMQZt5yQZ4K(G$$F~u z5Th8^w(jbVh}2&*Lu4H zH^wc((4PZow)jJo2)i!qYCm^#{R^Wc9ni`YEd21 zu_Q}FbUUv+Vf5vZq#x0mNvF;vktTJ$d~gYla`JH79)q_oPnBnCh< z(c@rOM9}HX^U@EE_$V+=uLO__d>1<+ibJ>js~PA0kgB007Oi`Kemo<0{Bm3voo?-T z`(*cG#@8KyT1|KA5c1hky3XFGc>Wf@{H8BENWQ@N_;%2Z#ESa7t2f8P>;53nX3!HI z+p!p-zub5Q6|WYB6z^F}atKM1o&g(D4`PQ+1*H2g8nNrNxCX#_P*|5~x@p%2Yy zOY+-_ve!kU|FycMtbOGM$p@A~s{58=wcudi>oX%k(?8!lg+yK4)XyHVwNfM4Cd@c8 zoK4)3*GF1#HQ<)&lPNT=r6o9mW^XIiNy7>999&at%4pStB|86+NQkfDeQ~JBkIl{K zo}K8GR+>fIT}GWOp=qn>~NqEdsANl_|gI)$TQuY@S2062z|Z7*D+N z5~d>2$C#;^NGXMK84oc_l$6A^ZmmQD^&q*(uY4uNUCNEkc_**%)@G5*SG^%N*yi$Y z_Kv_sNLiXrA?;iql^sCB?sb~{`A`#_1OA_P`LX9Tq~o0V@u9dXM!21Oh~{7QH_6tC zmWS6b6lsSwUo0#RXlWqWgQjxo_AB;y~|9%5dK?zpc|hEYDZ+< z+mykTc&UHN2H|mxt)Xphv*SIQt+~}VP#$-zhhYATw0D8^{7UqjFDG|%-I~j<)?CX^ z`O!u3(t-C|f}z`A-qfto`J*4!oq8thTV>fMzyB( z$8;b0b%T7Hn09&y=@r={%Y+^#rUfXuzVN$Ey9T(7qPid5n>+ zQd#NMrfmzi!N&zZA4!%To1f>Ypn7o58J8R&HMBn)+D04f-XhZ|eIARH4^Lmt(VL7T zdaIT4+e799_c}!A4IuO3r1Y&@^zn;Wn%zCr5e=_H&61HMlAa}$jFOi}t!NVTp(!Cr6lVNx$G2K1DMDR@Toe@6_M$7Go<1i{~^Nvb?m6MZ>Wptld|Hc_U(MBRNN zS8c1P(b*og$6wQRJ-?0*(&mXj2DDuuq06q3(b=F%q5N0?m;)%iWbpbuYhaq033~aS z%JQ>&z2NlSU|aPfbSR6B_YucQPX0SDtLgN3Dr)@yp9A7*K)ak5IYZ%Tb3U=PS{->4ca*gD$wIlqHITi3DN3`{C6jz90zU)CFRF6SJss0+txyCZ~Zg=ITpWswxV1aQQocQEOR-0 zYL!27+c%PfId?GTuFAU4amE2n)Fv<*II+OV0JR(H<*;3ldSgpvXR-+#;lV0{UNUF& zCeq0FM7a0>+IvWtodOCsK_KeIpX_HINsRsQGjvojRgf&omtDXHuKPQB|VZqa>^4iAr6LVMwoAl|Su=N-9jEl3{3R@4Zy&sM_v>weapF*m{_Sh>51iY*pJhKxvgE3{=%WtQGn%v7Dx7fm34u5k_}F%{J1jwf@jI zZpq#42hvc604wseE9D=l(0)**BV6BDT_B#i~ZBXI64|4}VnbFBIMKD9gWKv8D%}hVCSpMV{TL*kD;^xW`|Z$u}Hcs4s+CT6-GZs^-L9 z?N#8g;0PTD-ngN=JyIJB={(?7b?&fD1|#XSG>OK#yuS|V3BJ8}!;MqDqqM~)dDNKU z_MA6y0Q45q$7YivKE;xsIlnr5j2=uX71S4u%Y~7ix{{M^~NxYf$G(ued^9E{{mrr9&~;KZfm3a!HAI zdf0il1eTZQF9y9N5tn(R)nHL>JawV>VkO(TZ>+{Gz1g{mERJ;tI=H+ht%hzk1HX^W8ke1**JRJAFCw>khJ=|qgCc!I#s>fm zTYN~}I>Sx59M*+Zpkrh|JxVcGjz#Ta>S5HLa#Eq1cj>V9NbP=Hmq0}8|32Ee89zi4 zJ=iv#w`JnTdCiB{Cy%ki8E#@KbSd8InS!Xwquh}i`&A^==!>8ZNzmuG{mPccE4j%P=h_R_t`SdUzLax0Siz-jo2cF$Y6B_s6lRE_ z2S*cp>gEI6EjlML(u8x!b2rfnbC@*X6K8f@!>9m$cl5_pf_R7P6 z7npmj-i!Jd>}MU4cC=+(7iV@(4-{wG-Fx^*NTqZ^XYZ5LA`Mrbi0u{IXKK$K_a3q# zy!5}kwAjaM;#FBWcL9c6iecKBV;(O8cnzh9!vH;O%KaG}LCm5&p`LaEp&3aaVMNKo z02poUSod21CAB@QQy@#D;Y@dMb-Cv^3 z`_T(KU`=1Y{=FTYwgJQt|2;a{|K?%+e<4}^yB#epBl|x*tVo6bx|^+`-bN*hP$n-V z8{0(iN8sV7P*|9FJQN8t5s!6qJVVQ-)e~@J_bB)*2qGZDhUAlMKl4=GLPILu(fBIU zW2)7`{q24^+2%)6O=1ujcR)fIeE~*z)OH;~x7WpQHyZ=Wh;^<|L0r|oKdw8gRgFG3 zbrgM}_=jbcnm8zRRxwL0VN@`|4^cNl4dXdzk|AJH5hiq_-)t)xOUkG~i_67+vYJdW z0b=_#A-H4+jGH8%K8&OAM0usf_NL_qb2+?@BWJK=4Eql=aXJ%%XvzXpR?Gs$3K)MQ zO3bR%6j47YLFr$bIO=REaUs7BH53IwF4m9Bb*9&nBA%2bioD64N1Ht^q?QQQ-R}FWNpGbVT`Zqx-~uD!4JkMf@qv*ZDrM2bpEcb8y(u@C?>K_N z?Hz-iV)8H@#ywjBf9YKe4Zbjw3nW86L_S*4MPe(%icCc9@T^MVzD8ZZJl2Ry0 zAbSPZj4GG%Gn5x8p;E6mNW|RQ0+fi=g~)eRCDUdeetBUZ;yP89OEYHTWzSXpQffxF zFLhYY*H%8eb4+$*=9t$Z*Yn83CJ376shh-QSRyvi`u-V@)SbwOj*zo#<=$&awzZU1 zRxTV5Vw8HkpLk4(zq*0vh<*g=6#f{CinQ0eO6em7FZ z{`3z*(B^x_GVhEAH{X7T{a?!)lq|~@?0Yf@|KkV!KPif3jjRpLtWEx#J}{|b;ee!s z?k%0>yzc0ci-(6sE{g}42ZM))$ZJFM)~yW?5c=*O^J(1)>_F9-wHlmbRBnGyKNnFU9*$&es~{h{J^?VgZQav z3UO8Q?FjGQf{fs4W@*BX^qC#fjR7&19BRZh&h| z0j_PYqtkci>9=N3v~A$grB@8_Gi~{1g4u8hp+h@T4NL<4+(uYIm&RrliB#QcW}s&_ z{A*f@z!-0)rwvn&F3Ci-Xa}7HDP)}4y%#JEG`;(!IR43&~>;HSH#TGC(oLA8nK_s9GU|VR21dp7?b1gK0Rdg=9gLI z9$nxwsVmpW@p7I)$~thPUNTdy-dDCH3ptkWL#{o1O)l=(QnflcHZEZODCOVTFuadgq9~aL`>drI%|yt;2~fFff6yx@Qi# zt%fy1l+`-!;g-ew5G{4i=JcGe5@catwRl)aB%6FEE>In-pvqhmtTASXqPmgY>_utu zw344>GTS=egtHw(2cc`$R}-Q&jLCz+DB--<82>62e6O4?JP1LRE-Y_IFbUkcKh)62 zRRnaws7YuMcV-c@b+;@Asld?328%(+$z<7p462NeF;nEk+HOVF2-+$mG zqKk=(QqukO(n8bQ2$cV2=&XmXnoeC*WWiw3Gpc7V~(Gt`FZB%M+lC=rc2Bu5B%Fz5(1BK7&ulya$4(DUD1 z5E*(GZn~_zUFf4xe#SN} z(bGVbJlU&Eyk8Wkd`z7YHoIoX1XrRXsMpl^^XHZ&kZGI`=7L6mIpyN@&#YY07w1rC zc5D6vL?OdlVbj=WLZIu*Yi1h*MQ+%&j3t>1wYMG1xR0~?E zIie0;5tr`?XMg90)k6UGIzBin4$FkskaxUy5W$FdfCkISA#a?uqG`^oz{O9G5*^VF zYq8R212}8JJH<*32|$So4dcE)gN6s|f^4HJ!_aXW8`Zgge=%Sfmg`CN<43y8`Tlh$ z>QPiX9#oW>1xUlwkQwT=%a3IE3m6uGPv!Ca1s0fnsw@e2<6auUfC}GO*t@-2G32o)I#3_u2oRzL>?m335 zAH6`Pl763OLuj2*0vJXDko{kyC41$i9`t&n4kA$>y6LIuERvYD#V z`c9vf#7?VEgQ0GOJ_5I_U_I%I6Rw%7uw&e5yB-HD3|xz1QTB);x_X24An)%a7xOhbN|u@zfWZEd;frJK=Wld{)5Rg*mh zXCC~58BTatCsb)gbEhk9d6vfD_*#+3Nynw{?blC}&=aTN)A3k3P5MR=FTru_3_`|! zI*(eJQ`0i&HjFKa(y~iVj&6g{yeAwE3-t_Nnxyrn%hs%XW|fv^Bbyd4yA8lDE>Cg| zNq)Ffutf+nQw5GkI;D9GiLfcJDF=>gbZ+}B``_aGeuw~dka0zg_;!rNrsfah+XCJx zD9vq)yJQVZxvbymoUIhdm6*7+PlQ{fyOOtZKEwtX2TZN{Y8&LKY3KGTeanVx4u6D- zxLYtiqL(E51cOJR!-)N$Se!qV3CyjAKnk&P#~c6P{*CwDwK@~Nf$I#3jyyFHz7A|D z_jhJ7EyUxH<~%N8mGQu6EPYp~@jPG^d*gox(4)~s;)#4kYAnQSaNb(`_LDio&;BSB zAgoQA;^m))Ch{3h9F7^-K}(ASGriRvHUyXF5OSv-p_o&MyYWu>1mb}ZLnsH7F~mEo*$^F6-e{3kyIGOGw-*Bk_X%@2NmE#o=nz=q~N1haRbDy@M_Y6 z*=z8CcBe318si!)1@d-T0UnSy6}!P&s@hC;>=vPr=5LZ0w0M--GfW1)fz%Qp^DQl} zLnkn6EP%nJ<@_~Z4pLVg7N(7Tky;!e<001ckq6v=>f@e}NmZ|_s#z(+WD zQ{f5t5RRKsx>D@5-dB21f^3mu(#sE%``PS>B9g$>_giRWzY6S$C9}ur8O;&=zg4BC zyw+A7-?cR2w|a@|pVZO{M)u!?HepvI11Cot`|l@bv;V6u%~bv_tCumnqloM4I>3hM zRgerE>ZvS|ds^|q>4u1k>T;9lM$9CZ%MCmtJ1CXgSz3#eH|9w?rye{^Ui!f2AdTgX zC5k7Q7&A|sKQmdprp&3K@zbSvO{Zr)ww~Q=rn@{oU$?v8QF`D%;YE=MzPrt7wZgi( zv#KoeT_z|BMF#0z=Mw9ZkrPSW%XFQ!>2nX)y>tVXL@j#l|81QABazkwq)4ln$#Q8G|8_$EwO zC}vu5mqX7nZ8G9nJuyVF2*%D8I5=*Y`qjoT4$qDJa8gD7niaNxcbYGdXU32Gsa}Lj zr9j*nVHtU^J-V<;3KE+)Ii7MP@zm|Zg+&ris#8#4J$Rh#oaNHA)%zKNaL)J0x0@nY z^DGCoFXm_S$FS|ZH2S^$6a}jl0(P+WuAFSpb%0Y@S@DcPF+!lLDtUeQ9|9M z2MfBe@tt8vL7E<0^dz_)s_K!2oSCAuxvE{C{d=B7G9T?#;SMT zaR)|zUNWY7*!^sk*jWfG*`^yea`~!?n-Y{f*@Gp)4jeO{FBw`f;V|DXEra}I z2ee`kIM$&m1NSzRYJJUqP#j)9k)54qdc+w+yj^P(EAk~%)FdnUVd23B<9ae!6{Al< z9EFiM$RfS;xSyg(jAgdTpjlNO8Vt&-e+WY;%T!fBotIH95*AEm#fN$p>5Lsaf`7*@ z+{byMOK6&AculFtC9_k{!EUGihQ?l-nYistW8EPI@=8xAdvXf956WKD^B;pQ66 zCM3#qt^n$suI)^pr%7*C4- zoU3qjNEG8>fKy0iVdFAce1Qs-v+3R2@u0u|0%#phLxbs=ZOKP&%*T+4CZ_VCt>>N5ThnS7 z{c$C1x^(4#9F53|&_mPA+mgaiV(&JGSzzeQ$RB#GlY1z{OiVAwok)`wN01$l$j-z} z9jpJ?Z=&9u&6A^%TzX*5$k+xK?7vq1m}JIK$r-0rh(II!83v)UW#Xz~E}slDb`gWC%a^gNoFhH=4a*!(MJF42MJ!%|wW%kZ{5e@XKo5=g-NP<}KeOit zZ3IOH?pf15IU5;$M!!^RodKI(3G0g_;y&-mT#ZWqj>~($g>l4506eB9v1y*~Fh9&Y zP;jYNrF6>_9ra~2dPnruyBD_ZA)FJow(#Bv1z3wC_A#N_YPJ3eZk9*RSX zb(`wVF4wqE_~Q$sNWueF$6R1_+3J;vP>*E984)93A6@(C3R%P!6NxQ=tNI7UK2Bn2 z-^IcdG=s7m!Yr`oyt>3jguU-an;-= zpf~?D2_I9~iu>P|^vNd_G+M3En%e9wA*WI&CnK3*wyZb4CF@~H6_RX%+ei=Wqw|tn z*#iC+ap!i17JC?FX2ropm+UMw1#f_~#=S_oePk7*nF@F3_O#O^KG#OD+Sat)bfx{< znU?ywo65v1vssN$5o;B~YTZulfJHSVEygqs*Yfj04OdR8jqH_Fuyp8~u z0F}D-u{i6Fkmmr|_FIMbh!$4=oqTEx*y?+8H^9#E&j1kbSsno$Exn8LN6jF+;;O7S ztOW@^miJf+$V>zP{huM7=__@mZy}&oJq38I4$f}BEg%;Bp?9b_( z4qh%k4$!*b7ChLBT#CREy~I>mqTPA2X*WV;ng!IE$Zn)E!V9D=Z}Q_E{Hm%pysIKG z9$1@p3v^e#zv%pYZQ;|sQ_>GM9v*}djD-p22$0OknCqPi`lMyZ7SBk})=SMO;={UG z91BP$I$M*Y#iiU_fA0VnK{DF7LKxVE5h*UQwgsBczaNY$YM9%pKTHe#CFD@3kZJ~C zms%%`+4I1cyU7>__CNV#>P6d3qSgX#qG=6c59UrL`j2PRx__yxEYb^OLU(!7=-ZEM zQA`Hr6gG;bQ;ae6CZGs>Y@hGPcU8etZZLu$D(voL|IoyY#;izc)7X9oZrh6uQ1gCM zwyL>@ci>9%alC!bFKjWoPUSZ;0jmJOQS4*vtM85w_zw@_>-!<$q_ zQthMTN{gU3{xpcq9~&ce_Nrxw%& z?;`wH_!a&I)i(A}TcN>2YlYO7lPssAxCIqN&Ko6>lrzU0o1c-M@4d9FN871>f%asf zk1^HXE9aHquBUF~$CX+!beYO{oqEo6c$%2j(dheZ7&Ko?ju6XB}4(GxZ+$ay76jWd_tq>4gM{ZmmP_ZL`2_z zFvzZkK9~|_hlSKKMrVf*itmCvrb?GG*y*&q=oFzpHpFCnQn5=a7jGDz`L}8{Me>6= z6ROdKuw9{0iu4P8Xj=W$`cc|cY_5t5sRNTp4={6GybG4{Nuy$v@kZv&#jK=5>~xf^6ntykn! zg_P`5s(|x{M)`~oa?3q1wfIan{jS72>GQBcEW7jzJ9O<&))S~hKw8q&GaOFJ0uxnC z@52Z}{ghs^{frulgpO|F@sW(J@HnPdosH?Hw2>r^sZ5h^dyA;!T3fV!IE`*>1&p3k zE6kx{d(5dh;bJlS<`Dq!#uu8Q(A8P|2iz+=v6JW-n7BkPPC z^x{$O@H3a8CSiz8RXQJC={{7flK0=+5Mf~;SpKj-exzdmLutu>%1ZvH3sI;F>506E z@zs4nQ{Rq(2Lb_t3@juDgpcP+>>CG9)I!7ugxb(&=z@pD!gzi`^|Rb+amluG?; z&j|#8-a$`sPc?xrK9HzLWfRn}M1wlDu}2v}s>F#iMd><9B(*yi4V__?9_EziPaW~o zGI)Ooa)|PgAPU)~-<5hCZC2)X(c)Sf0t^xyY4H+Jp>1@*dM*B3D1r+PXRzd?vG$la8p~^aDZcs0$fYTv;y{Fd#SqZD z_+1#pL57kN+|SUJg^whZ(D&u2_8l7d^u(XIwdsn=$oNb7a%#==v^sl6W51^1I1JKy zI{HoIO`h(ATf;F*;L%0$8IwSU3|BI0`QpdvB-1=29we8jvj!a$vH$Etkg9Lg)QRsD zOzYX(j3U|khaP@p)pA1+p@=K7Y}ZtNgBtYp{5~fUDFFj%j5NGW+g#Y{)(MhnEZ_-B5*E5x^ zg&7A$9yXn?Oqe(z#%IbC@0CC)x5czjR>lQvbk8NIlSMW%!#+Y|fkutdRC!QNYDF?b zNvqg_b&=6||2S0r83y+)qmTsCQ%{pK#7xsri%e0r@K6fk5HSw04zVL)LYdSxgJMuG zZ*Y}xk+jMLf-8dC@$P4^U=@7~#12I02ScHz8wpWgGL7&NscU6OSGO$Rg!T82qzd9@ zUz+JBg(x~{NjwsnH)s7*H)<9{T+h_IrxVwk{;HpXRaPfKsa5C;!P}-&Oh1BwIWtJ8 z)qyIk@o+Mf#{`3HiI_689?+C2!RAgC;K~>|MZ^Mk5jVo4F_>%{#b;edd^lig(78wx zqZ1c478>iQitPVV7hXMrBwzd3Qbp{fMmD8j*9D_2wr%M;3$VE6--`SxK5K`{TAGFLS-F~+t#u!nB{ca(#8L0KCBJZ;-I(GWMBtTvusC$f+l{@ ze$My!SwelFo|IMsdLp8%c*ow)-x@@*c&U2X&Auv!Zd2KoI69LGWkmySA8G>1fU+tH zRjc0=hirKZZVhR`DWObMqatr;urYRIt!Qni$QFWiM+B6$v;|`uA>|}4wct;ask6Mx z$*;)>Gv|sSkK!Z0M8Kb=EIwJ0zn0QA`W4oLE-0QdgU@zYg}4{mw8Ul2Y#gruveRt2 z)6>PfgUup+&a0dg=XKi@VZXTfa!HlZ#NQ!6+C~$jd+_Q|=k`T&mA0{V$eii^1qK~dM1=a@@k|B}G_ltZ~Bv{|U zkZBoDOw8rKGHz}#(8tduP%kfH zxivxO9XO2qt~w}lqktul9gFKqiK8~DV}C{DD&IqkJC^0fSVVN6(2&33g6{7H zIjv%dINpKYTYeB?QYPBgR=h>jKB_GuMr^}CBI6|joqI3ycTEgYR<(Br44%{QV2`g8 z^51Kq*tF4wk7*0_u5S*uxa`90Q+MR=X}xD1nbxWrP{h_?2y#2;2%jmKABNqDqWJWA zfXtJq@Q;80%&PH^kl9vdi&Wdl^nNJ3iHaNB^kfRh`f>V};TF2HvSd7SQiF{zJoMkv z?QUPN%jgd6vZobznC06aK7B=gpHhhc=+SS5|8Nt<)2dS)Xf8->!u zTZXX|$uE|^FlDM|iC4=4Dnc&lA^mt7qwz_iGmP7eETh{!To5+$$NX!0B3e~w!0--P zjt42_i!M&jCIBLHImY8m8W!`XeV`=KB-E8Vv9S*rt*rv*snsRT6Mq#o-B|~a^5{Q= z{fdwLTWUwoGnOBA%xa{|D7=90?|jU^QXO-oTu1j>Kn6qVvV#^E)>F?cuTKvB$Xg;o z_J29c(sl=8RVLspqG2!;^t#j#H|$6tp8G=;hG#W@`DZt(D2Hr^O_R{CpSc90FrfBDV=^HbZcP

    Kv_L9s^jE_zaGXhYRmTYiL`^loTHH;qzV($@nMS}B_!?j3LHqswmP zoOW|G8JbT&zJ3n~=>9@I&grV~TmvrY{!RI`1MSV_&&XL5HP@8iHm7rP-Sc;hPj5%A zK|5MjZwH~_>RjOb?A=u_2kEFwqWdk0>cOO=(Zgn4PWCi;?#cItC(=1X<7B%T$-6?3 zbLz-RoyTTK?dqPU;AJBq#5MZJW$8I`@v9x5Bnb9-&r`*d#LrFyGTfE<@S^5?BR^U8EQWc8OwH6;1%hldO<+T@sS5U{{VebxSliSl}_KONum@4 zD^r?Iq9KhA$qZG3z%h;mM7w5_fVAA<1Je}&>u^s-PJm{<{Oy7$HohKWeJwfYXKjYo zP;uD}U$pN++P)f4l4Q?_^SS(J=>l$c+;M(g^WzLF#(6w!x7&u59lz(~sCff}al&vB zAEKG<2ZmovJiaEJePmnZUGBz;KKim`&02{x(M*v#?Q;k`p}qH6^g|=M(H`*$;>CFZ zphnqP4xhMRr)r$iCa?RbblJRHzwh$AY-EfL3k!|jyQboNQg+~Vj22H1Dlr;1Oc``# zO>3dStDw; z+Ox$xvc%j{AFg+m(PSLZeXYpQ2KP+COxdT<6xi-!eL_+wBsR=%^)NZ(ZyPk21OJ4l zLR87x&K8%LR5VkxFh9_CX1n6C?X-OX>t8o4k9F6hi+1p!E0>XG@cr4%puqbHsAloWfaeX~R*l z1s}63wrD*&>VcVNS>`a|z%vPO)^kW1PXLf%!A%(=E-Z3A3{;;}3wFsvJtdIEj{^{lRYWv3J#cWLv zV2H;21>VUk1@r1~qYK6+a`YzY7l9b$A(dlRMtDPpZvx@vlwB*F9qI4!9YnEpk^vaG zSQ2S_;WC{V=PDaf7r@PB=!=Ujvyhh|s-eg;$l`Q>>tlhJE^q^x+anJ*naLj{0 zsZ|sHFq3Y7gV4ABsB)Qv8O+UkB}FAf=^T;AtOv=YoP2FAx5J6M9tCmUm~o6Z6WWwI z>OWtZ*=xs*e0T)jF!osLVGN{l_iznyyXq8R(js(K%Py+4?lb%EI9++9b8h!g4PC1q zO>uPufOV_`N2pob(b{S?R)7{;Zdbr?9O6hLT}Wre}#e7YBJ^B`272M&IuMu^FMspfWA8ytpB`g@t@Fy{}&PR z?YIL(5AQV?BnSXER3n~>mKzb^chr1IjI7a5 zd|$T379CTv>nLl_{VLK#5N$ za80^ZLKwpX=5R-E=PkM)K`>UcLOjRg$i`^6bkcG~^+xI1b4(VaBaomj&QX&IeQkYo z_vJV^p>G0CjeVR=dYem5;kghs-;o*V)^NY!Fg>Q6Jlbf2|Bye}_>pAPhFLClz z+E^m98#p<26_IahI41sEAe-VjGN6I18M2jcsK|8^53+c%|)XaJ;1yK_MB$CeF)v`eTGfpW>_*q^t&t0CL z*B-R5@9tkeZ2kDLaC=W32MZ&pS%YLJ?u0eTyQtl+ay-L!3}}UB_(NHGbtkjXQPQMw zubbFuD`&#-T2Licu>9lo)*y%!Bsm&IN{krB1cX3TT4_oa57ny|9E4ewMgfhx!BB@H zlq6>L!xt`*yq}f3WP~?1kN!vx^0_a9N)xTAwXKd)yo>Dd7>s#c*J9gM1HpLWU)-$t zp+#*B?A>UM2F(c4;qp=Eq69--Zy6k+ocM5>mu!xZ*CFiZ@vIHnI1jcb_6HQUQ9leE zan3FSo|7mG2#`}w^eaQ-Pnr;mzWm79VRbC&WD~e9j&1- z?5h9GU6*8f9=2J~g}0xSJ%u8V0|Xn-W4&g3sgSvdikQXSbFin75IAxpkY*}xi=aTG zT7oC<&~ar?*VcBnkLwDngK|Y-D5t+wZV)~k&e#TFJ4Xzofd46>PXOYD!AMFwcJm^J z<7j9qgk$T^BPH@lj{?{1ejW{4$;z|x(t&2o_ANY+jX4Zv^)bAofDpH%r9u=BQN#{j zvus6QDvI~~^I2pzVcY(dq!<50`!FE;SG>$j^11u!L?Bl=yLamo^M(vTVhHsNX56G> z9wj)jRG=Fx!c}+b)_khW$;4(Hd+4fE5&XSz+xi4%Dk?$KCdeDxp{<-?5!cBb z7Br<=jJMMvO{;JgcM?{=F`MTN@`;d;N>1ZvzVq+#g(U|l<(f2cRn`7R%gsKg4ZPBH zCTHnbjlhi}yPqx)`DkqP9{ioVitB@fsvZ{}L-f|ps8@uo-ecGfm1hIDG#|tQSBu(l zYad~0lA(Hu>!K~5B#Gc(w$ephl!C*f``kVK6*fC^sQyMu?h$9d|9x34^&9SmeUAoM zzHeszBQ(vwFDn@(#s6AXO55K$ROC)V>R=g~dGEN=T>2RqL_$amIB<{oAlk_a~e# zyOiEa8zbJB;xR*&t=N6XV=yqG2Wun4{) zYj))pDp?8)&Lf9<^VY?rec|035v!62f$=I#W%#ABP3$_hT>RpF>Jy;l=wpwr&Q|;! zx32YMemuHwS|;w_8i0w$s``{ObqJTVS<0LpCaB*$I2d)FwWJQ-d8#*Fs}i~>!dNBM z)J)lZjv4wx>25_&t6Y71tdR}5fbmTe!2yn+V(G@@p4QJ!A{|w|+H31k4Pm3(|1bm2 zN=VHj&7}4#-+#xIBjz!O+Vl7S&M_+HAI*BG_dt0S7qq7>5s32;o4JuasdK7b6sj|} z!ox~bP@3)r02Vdby6ieF0d3s);clkOt*DD%l;6;7oZq_~lrI|%gsY#@I~Ti}aX^bq zRFE0GGu6WF+^5wopH&zvIuC_t>Q(vfr$tdRH{Tv&Hfv23M{G=f@9J--?)CL%mRwd1 z*UY1`k~@a|PX05FeO2KkgR$@+<~4>u#W3Tsg4I|g8ZtS$Ef2HIUXqtcMjW2Tv0=pf zFJ2QRBvc{z(V5r+>lx^NrL!0WCLx5cvu~tnuL$dZXUC}SKf|ILFH`gm?j=5aV6Rkj z>{VuQ>xo}9>9%BP+?NvAeKYibOW#jK^D^e7op@`B@@g3AL(2;LIePoXTNxU|HiO!@|zgpVk&Uw6NWD;285N zSG)ve>~Gf*5A!d57wR)M=CFDlM3Y7d_aBq0uCUY}NDV>U868?FpRkP4I=h;B&c3a! zKf{B5k-^;T;*L3ZlyqqV$`gGtK4qEa~&8k*wy{&uL;?36z_8P2^jV)&(5S+MKb*QU(P^7(w)ua-x*JpGhls9ZC3(%xNTWR|~(5zt@vM5j`q9KMA8-o<$ zPi&xJqYS=mzVVB`iL*O1YHziuS_oRqs`foy!=Av_Edz(!WX{!!fw)*YicB~y# z0Fd1+y$EbV5NQbM0S zz2E(a#5ZWh_YaWCma4CjyWL4|4aBu&fGOq+l{mT{6>S;Q;bwRBs=AY$LOJH!d0kH} z7z#}EeoMFoV%7Nf1gmG1Z>GS|NUITNvQT5OqRiQeacP1fdCLjJYG+^mVvwAC_^&+1_pA_VP^i+kjoJzqMMFg z9iU`r>ueXxzC)4gY-^=$ws3@r<@<#S+3|-g$^h4^0PUqfIA+&q{5aS#`HL$?2jb;g z4iWq8S)4ZRuH`M`>47EhbV6ZZUQlNClUq%W%I>lNc!z13F?62_Kq5zEf~VNeAi>A? zdL_j73P;!?*%snK_QX1&wI>E?5umx6ffLOA z-S|xR08`;48~rGoP0);kQ}Qj-V^_1N9R}+?sOlyQ{*qJpCFv5-3XoLmK^UF^QJB1B zw3p$RK=RvwqyQ%5Gydv33qgl_9Zxyo4wbYWvR#FeM<7p0vaZ(+Mwo4nV9956@ps57 z%l#o1lB19&y3QD$=17g`&)53_K_XAEe#Gs|KosQ@FbD2lSrUw)1)7ysoE7#2$XGOJ zjY5Blk8C~{_%Z91R;?ON1g|Ju$o}bB%3KTo-wi$0JznNPN}3_K7g(~Frzn{zntn6)48^`#lix-2zg~ZtN~`k!;qQq< z2i%W`jsQdf;ZMSkJ-E3O-X5TVS7I|+KnyJCVvag1a|`XMT&`LE{$t#xu7evAQCJ9| z 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 jf{G`5#_ogs0) { + Storage_Info result = new Storage_Info(vv); + return result; + } + } + } + return null; + } + + /** + * Get Storage Info + * Map will contain keys : + * key = "Path" , requested path + * key = "Total" , value (Double) in bytes + * key = "Free", , value (Double) in bytes + * key = "FreePercentage" , value (Double) in Percentage + * + * Use function Convert_FileSize_toString for easy reading string + * @param folderpath : path to check + * @return Map result + */ + private Map Get_StorageInfo(String folderpath) { + Map data = new Map(); + data.Initialize(); + + File dir = new File(folderpath); + if (dir.exists()) { + if (dir.isDirectory()) { + double total = dir.getTotalSpace(); + double free = dir.getUsableSpace(); + double percentage = (free / total) * 100; + data.Put("Path", folderpath); + data.Put("Total", total); + data.Put("Free", free); + data.Put("FreePercentage", mycodes.pembulatan_1desimal(percentage)); + + } + } + return data; + } + + + private final double file_KB = 1024; + private final double file_MB = file_KB * 1024; + private final double file_GB = file_MB * 1024; + private final double file_TB = file_GB * 1024; + /** + * Convert a filesize to easy to read string + * @param value : size in bytes + * @return string of filesize + */ + public String Convert_FileSize_toString(double value) { + if (value < file_KB) { + return mycodes.pembulatan_1desimal(value)+" B"; + } else if (value < file_MB) { + return mycodes.pembulatan_1desimal(value/file_KB)+" KB"; + } else if (value < file_GB) { + return mycodes.pembulatan_1desimal(value / file_MB)+" MB"; + } else if (value < file_TB) { + return mycodes.pembulatan_1desimal(value / file_GB)+" GB"; + } else return mycodes.pembulatan_1desimal(value / file_TB)+" TB"; + } + + /** + * Get All registered Network Adapters, whether up, down, or virtual + * @return null if not available or failed + */ + public String[] Get_NetworkAdapters() { + File dir1 = new File("/sys/class/net"); + if (dir1!=null) { + if (dir1.isDirectory()) { + String[] dir1_content = dir1.list(); + if (dir1_content!=null) { + if (dir1_content.length>0) { + return dir1_content; + } + } + } + } + return null; + } + + + + /** + * Get MAC Address + * @param networkdevice : network device name eth0 / wlan0 + * @return MAC (String) if success, or empty string if failed + */ + public String Get_MAC(String networkdevice) { + final String targetfile = sysclassnet+networkdevice+"/address"; + String[] result = ReadLines(targetfile); + if (result!=null && result.length>0) { + return result[0]; + } + return ""; + } + + /** + * Get Network Status if Up or Down + * @param networkdevice : network device name eth0 / wlan0 + * @return true if UP, or false if DOWN + */ + public boolean NetworkIsActive(String networkdevice) { + final String targetfile = sysclassnet+networkdevice+"/operstate"; + if (new File(targetfile).exists()) { + String[] result = ReadLines(targetfile); + if (result!=null && result.length>0) { + return "up".equalsIgnoreCase(result[0]); + } + } else { + try { + NetworkInterface inf = NetworkInterface.getByName(networkdevice); + if (inf!=null) return inf.isUp(); + } catch (SocketException e) { + if (BA.debugMode) BA.Log("NetworkIsActive device="+networkdevice+" exception, Msg : "+e.getMessage()); + } + + } + + return false; + } + + /** + * Get Volume Level + * Types are : + * 0 = System + * 1 = Music + * 2 = Alarm + * 3 = Notification + * 4 = Ring + * 5 = Voice Call + * 6 = DTMF + * 7 = Accessibility + * @param type 0 - 7, default to 0 + * @return volume level, -1 if not available + */ + public int GetVolume(int type) { + AudioManager am = (AudioManager) ba.context.getSystemService(Context.AUDIO_SERVICE); + switch(type) { + case 1 : + return am.getStreamVolume(AudioManager.STREAM_MUSIC); + case 2 : + return am.getStreamVolume(AudioManager.STREAM_ALARM); + case 3 : + return am.getStreamVolume(AudioManager.STREAM_NOTIFICATION); + case 4 : + return am.getStreamVolume(AudioManager.STREAM_RING); + case 5: + return am.getStreamVolume(AudioManager.STREAM_VOICE_CALL); + case 6: + return am.getStreamVolume(AudioManager.STREAM_DTMF); + case 7: + return am.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY); + default: + return am.getStreamVolume(AudioManager.STREAM_SYSTEM); + } + } + + /** + * Set Volume Level + * Types are : + * 0 = System + * 1 = Music + * 2 = Alarm + * 3 = Notification + * 4 = Ring + * 5 = Voice Call + * 6 = DTMF + * 7 = Accessibility + * @param type 0 - 7, default to 0 + * @param level volume level + */ + public void SetVolume(int type, int level) { + AudioManager am = (AudioManager) ba.context.getSystemService(Context.AUDIO_SERVICE); + switch(type) { + case 1: + am.adjustStreamVolume(AudioManager.STREAM_MUSIC, level, 0); + break; + case 2: + am.adjustStreamVolume(AudioManager.STREAM_ALARM, level, 0); + break; + case 3: + am.adjustStreamVolume(AudioManager.STREAM_NOTIFICATION, level, 0); + break; + case 4: + am.adjustStreamVolume(AudioManager.STREAM_RING, level, 0); + break; + case 5: + am.adjustStreamVolume(AudioManager.STREAM_VOICE_CALL, level, 0); + break; + case 6: + am.adjustStreamVolume(AudioManager.STREAM_DTMF, level, 0); + break; + case 7: + am.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY, level, 0); + break; + default: + am.adjustStreamVolume(AudioManager.STREAM_SYSTEM, level, 0); + break; + } + } + + /** + * Set Muted + * Types are : + * 0 = System + * 1 = Music + * 2 = Alarm + * 3 = Notification + * 4 = Ring + * 5 = Voice Call + * 6 = DTMF + * 7 = Accessibility + * @param type 0 - 7, default to 0 + * @param mute true to mute, false to unmute + */ + public void SetMuted(int type, boolean mute) { + AudioManager am = (AudioManager) ba.context.getSystemService(Context.AUDIO_SERVICE); + switch(type) { + case 1: + am.setStreamVolume(AudioManager.STREAM_MUSIC, mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, 0); + break; + case 2: + am.setStreamVolume(AudioManager.STREAM_ALARM, mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, + 0); + break; + case 3: + am.setStreamVolume(AudioManager.STREAM_NOTIFICATION, + mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, 0); + break; + case 4: + am.setStreamVolume(AudioManager.STREAM_RING, mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, + 0); + break; + case 5: + am.setStreamVolume(AudioManager.STREAM_VOICE_CALL, + mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, 0); + break; + case 6: + am.setStreamVolume(AudioManager.STREAM_DTMF, mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, + 0); + break; + case 7: + am.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, + mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, 0); + default : + am.setStreamVolume(AudioManager.STREAM_SYSTEM, mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, 0); + break; + } + + } + + /** + * Get Mute status + * Types are : + * 0 = System + * 1 = Music + * 2 = Alarm + * 3 = Notification + * 4 = Ring + * 5 = Voice Call + * 6 = DTMF + * 7 = Accessibility + * @param type 0 - 7, default to 0 + * @return true if muted, false if not + */ + public boolean GetMute(int type) { + AudioManager am = (AudioManager) ba.context.getSystemService(Context.AUDIO_SERVICE); + switch(type) { + case 1: + return am.isStreamMute(AudioManager.STREAM_MUSIC); + case 2: + return am.isStreamMute(AudioManager.STREAM_ALARM); + case 3: + return am.isStreamMute(AudioManager.STREAM_NOTIFICATION); + case 4: + return am.isStreamMute(AudioManager.STREAM_RING); + case 5: + return am.isStreamMute(AudioManager.STREAM_VOICE_CALL); + case 6: + return am.isStreamMute(AudioManager.STREAM_DTMF); + case 7: + return am.isStreamMute(AudioManager.STREAM_ACCESSIBILITY); + default : + return am.isStreamMute(AudioManager.STREAM_SYSTEM); + } + } + + /** + * Get Maximum allowed Volume Level + * Types are : + * 0 = System + * 1 = Music + * 2 = Alarm + * 3 = Notification + * 4 = Ring + * 5 = Voice Call + * 6 = DTMF + * 7 = Accessibility + * @param type 0 - 7, default to 0 + * @return maximum volume level, -1 if not available + */ + public int GetMaximumVolume(int type) { + AudioManager am = (AudioManager) ba.context.getSystemService(Context.AUDIO_SERVICE); + switch (type) { + case 1: + return am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + case 2: + return am.getStreamMaxVolume(AudioManager.STREAM_ALARM); + case 3: + return am.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION); + case 4: + return am.getStreamMaxVolume(AudioManager.STREAM_RING); + case 5: + return am.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); + case 6: + return am.getStreamMaxVolume(AudioManager.STREAM_DTMF); + case 7: + return am.getStreamMaxVolume(AudioManager.STREAM_ACCESSIBILITY); + default: + return am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM); + } + } + + /** + * Get Minimum allowed Volume Level + * Types are : + * 0 = System + * 1 = Music + * 2 = Alarm + * 3 = Notification + * 4 = Ring + * 5 = Voice Call + * 6 = DTMF + * 7 = Accessibility + * @param type 0 - 7, default to 0 + * @return minimum volume level, -1 if not available + */ + public int GetMinimumVolume(int type) { + AudioManager am = (AudioManager) ba.context.getSystemService(Context.AUDIO_SERVICE); + switch (type) { + case 1: + return am.getStreamMinVolume(AudioManager.STREAM_MUSIC); + case 2: + return am.getStreamMinVolume(AudioManager.STREAM_ALARM); + case 3: + return am.getStreamMinVolume(AudioManager.STREAM_NOTIFICATION); + case 4: + return am.getStreamMinVolume(AudioManager.STREAM_RING); + case 5: + return am.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL); + case 6: + return am.getStreamMinVolume(AudioManager.STREAM_DTMF); + case 7: + return am.getStreamMinVolume(AudioManager.STREAM_ACCESSIBILITY); + default: + return am.getStreamMinVolume(AudioManager.STREAM_SYSTEM); + } + } + + /** + * Check if Screen ON or OFF + * @return true if ON, false if OFF + */ + public boolean IsScreenON() { + PowerManager pm = (PowerManager) ba.context.getSystemService(Context.POWER_SERVICE); + return pm.isInteractive(); + } + + /** + * Set Screen ON (wakeup) or OFF (sleep) + * Require permission android.permission.DEVICE_POWER to be enabled in Manifest + * @param isON true to wakeup, false to sleep + * @return true if success + */ + public boolean SetScreenON(boolean isON) { + PowerManager pm = (PowerManager) ba.context.getSystemService(Context.POWER_SERVICE); + if (isON) { + + try { + pm.getClass().getMethod("wakeUp", new Class[]{long.class}).invoke(pm, SystemClock.uptimeMillis()); + return true; + } catch (IllegalAccessException e) { + raise_log("SetScreenON isON=true exception, Msg : "+e.getMessage()); + } catch (InvocationTargetException e) { + raise_log("SetScreenON isON=true exception, Msg : "+e.getMessage()); + } catch (NoSuchMethodException e) { + raise_log("SetScreenON isON=true exception, Msg : "+e.getMessage()); + } + } else { + try { + pm.getClass().getMethod("goToSleep", new Class[]{long.class}).invoke(pm, SystemClock.uptimeMillis()); + return true; + } catch (IllegalAccessException e) { + raise_log("SetScreenON isON=false exception, Msg : "+e.getMessage()); + } catch (InvocationTargetException e) { + raise_log("SetScreenON isON=false exception, Msg : "+e.getMessage()); + } catch (NoSuchMethodException e) { + raise_log("SetScreenON isON=false exception, Msg : "+e.getMessage()); + } + } + return false; + } + + /** + * Reboot the device + * Require permission android.permission.REBOOT to be enabled in Manifest + * @return true if can be rebooted + */ + public boolean Reboot() { + PowerManager pm = (PowerManager) ba.context.getSystemService(Context.POWER_SERVICE); + if (pm.isRebootingUserspaceSupported()) { + pm.reboot("Rebooting"); + return true; + } + return false; + } + + /** + * Check if network device is a WLan Device + * @param networkdevice + * @return true if wireless device + */ + public boolean IsWirelessDevice(String networkdevice) { + if (networkdevice!=null && networkdevice.length()>0) { + File wireless = new File(sysclassnet+networkdevice+"/wireless"); + return wireless.exists() && wireless.isDirectory(); + } + return false; + } + + /** + * Get Wireless SSID + * @param networkdevice + * @return null if failed or not wireless device or not connected + */ + public String Get_WirelessSSID(String networkdevice) { + if (IsWirelessDevice(networkdevice)) { + final String regex = ".*SSID:.(\\S+).*"; + final Pattern pattern = Pattern.compile(regex); + String[] result = Shell("iw dev "+networkdevice+" link"); + if (result!=null && result.length>0) { + for(String rr : result) { + Matcher m = pattern.matcher(rr); + if (m.find()) { + return m.group(1); + } + } + } + } + return null; + } + + /** + * Get Wireless Signal Strength + * @param networkdevice + * @return null if not available, or failed, or not wireless device + */ + public String Get_WirelessSignal(String networkdevice) { + if (IsWirelessDevice(networkdevice)) { + final String regex = ".*signal:.(\\S+).(\\S+).*"; + final Pattern pattern = Pattern.compile(regex); + String[] result = Shell("iw dev " + networkdevice + " link"); + if (result != null && result.length > 0) { + for (String rr : result) { + Matcher m = pattern.matcher(rr); + if (m.find()) { + return m.group(1)+" "+m.group(2); + } + } + } + } + return null; + } + + /** + * Get Wireless Frequency + * @param networkdevice + * @return 0 if failed or not available + */ + public int Get_WirelessFrequency(String networkdevice) { + if (IsWirelessDevice(networkdevice)) { + final String regex = ".*freq:.(\\d+).*"; + final Pattern pattern = Pattern.compile(regex); + String[] result = Shell("iw dev "+networkdevice+" link"); + if (result!=null && result.length>0) { + for(String rr : result) { + Matcher m = pattern.matcher(rr); + if (m.find()) { + try { + return Integer.parseInt(m.group(1)); + + } catch(NumberFormatException e) { + + } + + + } + } + } + } + return 0; + } + + /** + * Get Network Speed + * @param networkdevice + * @return -1 if not available + */ + public int Get_NetworkSpeed(String networkdevice) { + if (networkdevice!=null && networkdevice.length()>0) { + if (IsWirelessDevice(networkdevice)) { + // wireless device, pake metode iw + final String regex = ".*tx bitrate:.(\\S+).MBit/s"; + final Pattern pattern = Pattern.compile(regex); + String[] result = Shell("iw dev "+networkdevice+" link"); + if (result!=null && result.length>0) { + for(String rr : result) { + Matcher m = pattern.matcher(rr); + if (m.find()) { + try { + double value = Double.parseDouble(m.group(1)); + return (int) value; + } catch (NumberFormatException e) { + + } + } + } + } + } else { + // device lain, coba pakai metode speed + final String targetfile = sysclassnet+networkdevice+"/speed"; + String[] result = ReadLines(targetfile); + if (result!=null && result.length>0) { + try { + int value = Integer.parseInt(result[0]); + return value; + } catch(NumberFormatException e) { + + } + } + } + } + return -1; + } + + /** + * Get result from ifconfig command + * @param networkdevice network to check + * @return null if failed, also check for Ifconfig_Info.isvalid() to verify + */ + public Ifconfig_Info Get_IfConfig(String networkdevice) { + String[] result = Shell("ifconfig "+networkdevice); + if (result!=null && result.length>0) { + return new Ifconfig_Info(result); + } + return null; + } + + public GetProp_Info Get_Properties() { + String[] result = Shell("getprop"); + if (result!=null && result.length>0) { + return new GetProp_Info(result); + } + return null; + } + + /** + * Get IP Address in String + * Revision 12072020: Sometimes an interface may contain more than 1 IP addres + * so this function will return List instead of one String + * @param networkdevice : network device name eth0 / wlan0 + * @return List contain IP Address (String) if success, or empty List if failed + */ + public List Get_IPAddress(String networkdevice) { + List result = new List(); + result.Initialize(); + try { + NetworkInterface inf = NetworkInterface.getByName(networkdevice); + + if (inf instanceof NetworkInterface) { + if (inf.isUp()) { + Enumeration inetAddress = inf.getInetAddresses(); + InetAddress currentAddress; + + while (inetAddress.hasMoreElements()) { + currentAddress = inetAddress.nextElement(); + if (currentAddress instanceof Inet4Address && !currentAddress.isLoopbackAddress()) { + String ip = currentAddress.getHostAddress(); + if (!ip.isEmpty()) + result.Add(ip); + } + } + } + } + + } catch (SocketException e) { + raise_log("Get_IPAddress device="+networkdevice+" exception, Msg : "+e.getMessage()); + } + + return result; + } + + /** + * Get IP Bytes + * @param networkdevice : network device name eth0 / wlan0 + * @return IP Bytes (4 integers) if success, or null if failed + */ + public int[] Get_IP_Bytes(String networkdevice) { + try { + NetworkInterface inf = NetworkInterface.getByName(networkdevice); + if (inf instanceof NetworkInterface) { + if (inf.isUp()) { + Enumeration inetAddress = inf.getInetAddresses(); + InetAddress currentAddress; + while (inetAddress.hasMoreElements()) { + currentAddress = inetAddress.nextElement(); + if (currentAddress instanceof Inet4Address && !currentAddress.isLoopbackAddress()) { + byte[] result = currentAddress.getAddress(); + if (result == null) + return null; + if (result.length != 4) + return null; + int[] intresult = new int[4]; + for (int ii = 0; ii < 4; ii++) { + intresult[ii] = Bit.And(result[ii], 0xFF); + } + return intresult; + } + } + } + } + return null; + } catch (SocketException e) { + raise_log("Get_IP_Bytes device="+networkdevice+" exception, Msg : "+e.getMessage()); + return null; + } + } + + private boolean cpu_monitoring = false; + private long _last_boottime = 0; + + /** + * Get Boot Time + * @return Date and Time in String + */ + public String getBootTime() { + long tick = _last_boottime * DateTime.TicksPerSecond; + return DateTime.Date(tick)+" "+DateTime.Time(tick); + } + + /** + * Get Processor State + * will try to read state from /proc/stat + * @return null if invalid + */ + public ProcStat_Info Get_Proc_Stat() { + if (Platform.isAndroid()) { + String[] result = ReadLines("/proc/stat"); + if (result!=null && result.length>0) { + return new ProcStat_Info(result); + } + } + return null; + } + + + + /** + * Start CPU Usage Monitoring + * will raise event cpuusage + * @param interval value in milliseconds + * @return true if monitoring thread can be started + */ + public boolean Start_CPU_Usage_Monitoring(int interval) { + if (Platform.isAndroid()) { + Thread tx = new Thread(new cpumonitorrunnable(interval)); + tx.start(); + return true; + } + return false; + } + + private class cpumonitorrunnable implements Runnable { + private ProcStat_Info prev; + private int _interval = 1000; + public cpumonitorrunnable(int interval) { + if (interval>0) _interval = interval; + + } + + @Override + public void run() { + raise_log("Start CPU Usage Monitoring"); + cpu_monitoring = true; + while(cpu_monitoring) { + // Sleep 1 detik + try { + Thread.sleep(_interval); + } catch (InterruptedException e) { + break; + } + + String[] values = ReadLines("/proc/stat"); + if (values != null && values.length>0) { + ProcStat_Info xx = new ProcStat_Info(values); + _last_boottime = xx.getBoottime(); + if (prev==null) + prev = xx; + else { + Map result = new Map(); + result.Initialize(); + + result.Put("Total", xx.getTotalCpuData().getCPU_Usage(prev.getTotalCpuData())); + cpudata[] prevdata = prev.getCpuData(); + cpudata[] nowdata = xx.getCpuData(); + if (prevdata.length==nowdata.length) { + for (int ii = 0; ii < prevdata.length; ii++) { + result.Put(prevdata[ii].name, nowdata[ii].getCPU_Usage(prevdata[ii])); + } + } + raise_cpuusage(result); + + prev = xx; + + } + } else cpu_monitoring=false; // proc stat gak bisa dibaca + + } + raise_log("Stop CPU Usage Monitoring"); + } + } + + + + public void Stop_CPU_Usage_Monitoring() { + cpu_monitoring = false; + } + + + + private boolean valid_string(String x) { + if (x != null) { + if (x.length()>0) { + return true; + } + } + return false; + } + + + + /** + * Check if a value is valid IP address + * will raise event isvalidip(toip as string, isvalid as boolean) + * @param value : target host ip or name + */ + public void IP_isValid(String value) { + if (value.isEmpty()) { + raise_isvalidip(value, false); + return; + } + Thread tx = new Thread(new CheckIPValid(value)); + tx.start(); + } + + private class CheckIPValid implements Runnable{ + final String targetip; + + public CheckIPValid(String value){ + targetip = value.trim(); + + } + + + + @Override + public void run() { + try { + InetAddress result = InetAddress.getByName(targetip); + if (result instanceof InetAddress) { + raise_isvalidip(targetip, true); // ikut default + } else { + raise_isvalidip(targetip, false); + } + + } catch(UnknownHostException | SecurityException e) { + raise_log("CheckIPValid "+targetip+" Exception, Msg : "+e.getMessage()); + raise_isvalidip(targetip, false); + } + } + + }; + + private class CheckIPReachable implements Runnable{ + final String targetip; + final String networkdev; + + // revisi 31052021 + // timeout berubah dari 2000 ke 5000 + final int pingtimeout = 5000; + public CheckIPReachable(String value, String networkdevice){ + targetip = value; + networkdev = networkdevice; + } + + + @Override + public void run() { + + try { + NetworkInterface nef = NetworkInterface.getByName(networkdev); + if (nef instanceof NetworkInterface) { + InetAddress ip = InetAddress.getByName(targetip); + if (ip instanceof InetAddress) { + boolean result = ip.isReachable(nef, 0, pingtimeout); + raise_isreachableip(targetip, result); // ikut default + return; + } + } + + + } catch (SocketException e1) { // dari NetworkInterface.getByName + raise_log("CheckIPReachable IP="+targetip+" Device="+networkdev+" Exception, Msg : "+e1.getMessage()); + + } catch (UnknownHostException | SecurityException e) { // dari InetAddress.getByName + raise_log("CheckIPReachable IP="+targetip+" Device="+networkdev+" Exception, Msg : "+e.getMessage()); + + + } catch (IOException e) { // dari InetAddress.IsReachable + raise_log("CheckIPReachable IP="+targetip+" Device="+networkdev+" Exception, Msg : "+e.getMessage()); + } + + raise_isreachableip(targetip, false); // ikut default + } + } + + + /** + * Check if IP address is reachable / Ping-able + * will raise event isreachableip(toip as string, isreachable as boolean) + * @param value : Ip address + * @param networkdevice : network device name eth0 /wlan0 + */ + public void IP_isReachable(String value, String networkdevice) { + if (value.isEmpty()) { + raise_isreachableip(value, false); + return; + } + + if (networkdevice.isEmpty()) { + raise_isreachableip(value, false); + return; + } + + Thread tx = new Thread(new CheckIPReachable(value, networkdevice)); + tx.start(); + + } + + + + /** + * Get Single Board Computer Model + * @return empty string if not available + */ + public String read_SBC_model() { + if (!Platform.isAndroid()) return ""; + + File ff = new File("/sys/firmware/devicetree/base/model"); + String result = ""; + if (ff.exists()) { + if (ff.canRead()) { + try { + BufferedReader reader = new BufferedReader(new FileReader(ff.getAbsolutePath())); + result = reader.readLine(); + if (result!=null) { + result = result.trim(); + } else result = ""; + reader.close(); + + } catch (IOException e) { + raise_log("Failed to read from "+ff.getAbsolutePath()+", Message="+e.getMessage()+", Caused="+e.getCause()); + } + + } + } + return result; + } + + /** + * Get HW Monitor Infos + * @return null if failed + */ + public Hwmon_Info[] Get_Hwmon_Info() { + File dir1 = new File(sysclasshwmon); + if (dir1 != null && dir1.isDirectory()) { + String[] hwmons = dir1.list(); + if (hwmons!=null && hwmons.length>0) { + Set result = new HashSet(); + + for(String xx: hwmons) { + File _hwmondir = new File(sysclasshwmon+xx); + Hwmon_Info hwmon = new Hwmon_Info(_hwmondir); + if (hwmon.IsValid()) result.add(hwmon); + } + + return result.toArray(new Hwmon_Info[result.size()]); + } + } + return null; + } + + + //TODO remove kalau berhasil + @BA.Hide + /** + * Get CPU Temperature + * On multiple cores , Map will contains several keys + * Map key = name of thermal zone, start from 0 (thermal_zone0) + * Map value = CPU temperature in double + * @return Map containing values if success, or empty map if failed + */ + public Map Get_CPU_Temp() { + Map result = new Map(); + result.Initialize(); + final String childstring = "thermal_zone"; + final String parentpath = "/sys/devices/virtual/thermal"; + final String tempstring = "temp"; + + File parent = new File(parentpath); + if (parent.exists()) { + String parentcontent[] = parent.list(); + if (parentcontent!=null) { + if (parentcontent.length>0) { + for(String xx: parentcontent) { + if (xx.contains(childstring)) { + // ketemu nya thermal_zoneX + + File parentchild = new File(parentpath, xx); + if (parentchild.isDirectory()) { + File tempfile = new File(parentchild.getAbsolutePath(), tempstring); + if (tempfile.exists()) { + if (tempfile.canRead()) { + try { + BufferedReader reader = new BufferedReader(new FileReader(tempfile.getAbsolutePath())); + String readresult = reader.readLine(); + if (readresult!=null) { + readresult = readresult.trim(); + } else readresult = ""; + reader.close(); + + double doubleresult = Double.valueOf(readresult) / 1000.0; + // pembulatan 1 desimal + int pembulatan = (int) (doubleresult * 10); + doubleresult = pembulatan / 10.0; + + result.Put(xx, doubleresult); + } catch(NumberFormatException e) { + // readresult bukan angka + result.Put(xx, -1); + raise_log("NumberFormatException "+e.getMessage()); + } + catch ( IOException e) { + raise_log("Failed to access "+tempfile.getAbsolutePath()+", Msg:"+e.getMessage()); + result.Put(xx, -1); + + } + } + } else { + + result.Put(xx, -1); + raise_log(tempfile.getAbsolutePath()+" is not exist"); + } + } + } + } + } + } + } + + return result; + } + + protected boolean network_metering = false; + private final String rx_bytes_file = "rx_bytes"; + private final String tx_bytes_file = "tx_bytes"; + private final String rx_packets_file = "rx_packets"; + private final String tx_packets_file = "tx_packets"; + private final String rx_errors_file = "rx_errors"; + private final String tx_errors_file = "tx_errors"; + private final String rx_dropped_file = "rx_dropped"; + private final String tx_dropped_file = "tx_dropped"; + + /** + * Start Network Metering + * Will raise networkstatistic(ifname as string, rx_pps as long, tx_pps as long, rx_bps as long, tx_bps as long) + * @param ifname : valid interface name + * @return true if started + */ + public boolean Start_Network_Meter(String ifname) { + if (Platform.isAndroid()) { + if (NetworkIsActive(ifname)) { + String networkpath = sysclassnet+ifname+"/statistics"; + File netdir = new File(networkpath); + if (netdir.exists()) { + if (netdir.isDirectory()) { + if (FileExistAndReadable(networkpath, rx_bytes_file)) { + if (FileExistAndReadable(networkpath, tx_bytes_file)) { + if (FileExistAndReadable(networkpath, rx_packets_file)) { + if (FileExistAndReadable(networkpath, tx_packets_file)) { + if (FileExistAndReadable(networkpath, rx_dropped_file)) { + if (FileExistAndReadable(networkpath, tx_dropped_file)) { + if (FileExistAndReadable(networkpath, rx_errors_file)) { + if (FileExistAndReadable(networkpath, tx_errors_file)) { + Thread tx = new Thread(new networkmeterrunnable(ifname,networkpath)); + tx.start(); + return true; + } else raise_log("Unable to access "+tx_errors_file); + } else raise_log("Unable to access "+rx_errors_file); + } else raise_log("Unable to access "+tx_dropped_file); + } else raise_log("Unable to access "+rx_dropped_file); + } else raise_log("Unable to access "+tx_packets_file); + } else raise_log("Unable to access "+rx_packets_file); + } else raise_log("Unable to access "+tx_bytes_file); + } else raise_log("Unable to access "+rx_bytes_file); + } else raise_log(networkpath+" is not directory"); + } else raise_log(networkpath+" is not exist"); + + } else raise_log(ifname+" is down"); + } else raise_log("System is not Linux"); + return false; + } + + public void Stop_Network_Meter() { + network_metering = false; + + } + + /** + * Update manually Linux Date and Time + * @param datenya : must in format yyyy-MM-dd + * year only valid from 2000 - 2999 + * @param timenya : must in format hh:mm:ss + * @return true if success + */ + public boolean Set_Linux_DateTime(String datenya, String timenya) { + if (!Regex.IsMatch("^(2[0-9]{3})-([0]?[1-9]|[1][0-2])-([0]?[1-9]|[1|2][0-9]|[3][0-1])$", datenya)) { + raise_log("Date Format must be yyyy-MM-dd"); + return false; + } + + if (!Regex.IsMatch("^([0]?[0-9]|[1][0-9]|[2][0-3]):([0]?[0-9]|[1-5][0-9]):([0]?[0-9]|[1-5][0-9])$", timenya)) { + raise_log("Time Format must be hh:mm:ss"); + return false; + } + try { + Process changetime = Runtime.getRuntime().exec("sudo date -s '"+datenya+" "+timenya+"'"); + if (changetime!=null) { + if (changetime.exitValue()==0) { + return true; + } + } + } catch (Exception e) { + raise_log("SetDateTime Exception, Msg : "+e.getMessage()); + + } + return false; + } + + /** + * Manually set Linux Date and Time + * @param DayOfMonth : start from 1 + * Month = 1, 3, 5, 7, 8 10, 12 --> Day Of Month up to 31 + * Month = 4, 6, 9, 11 --> Day of Month up to 30 + * Month = 2, normal 28, kabisat 29 + * @param Month : 1 - 12 + * @param Year : 2000 - 2999 + * @param Hour : 0 - 23 + * @param Minute : 0 - 59 + * @param Second : 0 - 59 + * @return true if success + */ + public boolean Set_Linux_Date_and_Time(byte DayOfMonth, byte Month, int Year, byte Hour, byte Minute, byte Second) { + if (Hour<0) return false; + if (Hour>23) return false; + if (Minute<0) return false; + if (Minute>59) return false; + if (Second<0) return false; + if (Second>59) return false; + + if (Year < 2000) return false; + if (Year > 2999) return false; + if (Month<1) return false; + if (Month>12) return false; + + if (DayOfMonth<1) return false; + switch(Month) { + case 2 : + // february + if ((Year % 4)==0) { + // kabisat + if (DayOfMonth>29) return false; + } else { + if (DayOfMonth>28) return false; + } + + break; + // bulan -bulan yang berakhir di 31 + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12 : + if (DayOfMonth>31) return false; + break; + // sisanya, berarti bulan yang berakhir di 30 + default: + if (DayOfMonth>30) return false; + break; + } + + // sampe sini dah valid + // bentuk datevalue dengan format yyyy-MM-dd + String datevalue = Create_YYYY_MM_DD(DayOfMonth, Month, Year); + // bentuk timevalue dengan format hh:mm:ss + String timevalue = Create_HH_MM_SS(Hour, Minute, Second); + return Set_Linux_DateTime(datevalue, timevalue); + + } + + private String Create_YYYY_MM_DD(byte DD, byte MM, int YYYY) { + StringBuilder str = new StringBuilder(); + if (DD<10) str.append("0"); + str.append(DD); + str.append("-"); + if (MM<10) str.append("0"); + str.append(MM); + str.append("-"); + str.append(YYYY); + return str.toString(); + } + + private String Create_HH_MM_SS(byte HH, byte MM, byte SS) { + StringBuilder str = new StringBuilder(); + if (HH<10) str.append("0"); + str.append(HH); + str.append(":"); + if (MM<10) str.append("0"); + str.append(MM); + str.append(":"); + if (SS <10) str.append("0"); + str.append(SS); + return str.toString(); + } + + protected boolean FileExistAndReadable(String path, String filename) { + File ff = new File(path, filename); + if (ff.exists()) { + if (ff.canRead()) { + return true; + } + } + return false; + } + + private class networkmeterrunnable implements Runnable{ + + private final String ifname; + private final String rx_bytes_path; + private final String tx_bytes_path; + private final String rx_packets_path; + private final String tx_packets_path; + private final String rx_dropped_path; + private final String rx_errors_path; + private final String tx_dropped_path; + private final String tx_errors_path; + private NetworkMeterInfo previous = null; + networkmeterrunnable(String ifname, String pathname){ + this.ifname = ifname; + this.rx_bytes_path = new File(pathname,rx_bytes_file).getAbsolutePath(); + this.tx_bytes_path = new File(pathname,tx_bytes_file).getAbsolutePath(); + this.rx_packets_path = new File(pathname,rx_packets_file).getAbsolutePath(); + this.tx_packets_path = new File(pathname,tx_packets_file).getAbsolutePath(); + this.rx_dropped_path = new File(pathname,rx_dropped_file).getAbsolutePath(); + this.tx_dropped_path = new File(pathname,tx_dropped_file).getAbsolutePath(); + this.rx_errors_path = new File(pathname,rx_errors_file).getAbsolutePath(); + this.tx_errors_path = new File(pathname,tx_errors_file).getAbsolutePath(); + } + @Override + public void run() { + raise_log("Network Meter started"); + network_metering = true; + while(network_metering) { + NetworkMeterInfo current = new NetworkMeterInfo(ifname, DateTime.getNow(), + read_value(rx_bytes_path), read_value(tx_bytes_path), + read_value(rx_packets_path), read_value(tx_packets_path), + read_value(rx_dropped_path), read_value(tx_dropped_path), + read_value(rx_errors_path), read_value(tx_errors_path) + ); + + if (current.IsValidValue()) { + current.calculate(previous); + previous = current; + raise_networkstatistic(current); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + break; + } + + + } + raise_log("Network Meter stopped"); + + } + + private long read_value(String path) { + + try(BufferedReader reader = new BufferedReader(new FileReader(path))) { + String line = reader.readLine(); + if (line!=null && line.length()>0) { + line = line.trim(); + long result = Long.valueOf(line); + return result; + } + } catch (IOException | NumberFormatException e) { + } + + return 0; + } + + }; + + protected void raise_log(String msg) { + if (need_log_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname + "_log", false, new Object[] { msg }); + } + } + + protected void raise_isreachableip(String toip, boolean isreachable) { + if (need_isreachableip_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname + "_isreachableip", false, + new Object[] { toip, isreachable }); + } + } + + + + protected void raise_isvalidip(String toip, boolean isvalid) { + if (need_isvalidip_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname + "_isvalidip", false, + new Object[] { toip, isvalid }); + } + } + + + + protected void raise_networkstatistic(NetworkMeterInfo value) { + if (need_networkstatistic_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname+"_networkstatistic", false, new Object[] {value}); + } + + } + + protected void raise_cpuusage(Map value) { + if (need_cpuusage_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname+"_cpuusage", false, new Object[] {value}); + } + } + + + /** + * Untuk write hasil 'Sub Application_Error (Error As Exception, StackTrace As String) As Boolean' ke file + * hasil nya file di run directory, dengan format Crash-ddmmyyyy-hhmmss.log + * @param error : exception dari Application Error + * @param stacktrace : stacktrace dari Application Error + * @return true kalau success create file + */ + public boolean Create_Crash_Log(Exception error, String stacktrace) { + if (error instanceof Exception) { + final String errormessage = error.getMessage(); + if (errormessage instanceof String) { + if (!errormessage.isEmpty()) { + if (stacktrace instanceof String) { + if (!stacktrace.isEmpty()) { + + + File crashfile = new File(rundir, create_crashlog_name()); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(crashfile.getAbsolutePath())); + writer.write(error.getMessage()); + writer.newLine(); + writer.write(stacktrace); + writer.close(); + return true; + } catch (IOException e) { + BA.Log("Unable to Create Crash Log, Msg : "+e.getMessage()); + } + } + } + } + } + } + return false; + + } + + + /** + * Get Current Running Directory + * @return current running directory + */ + public String getRunDirectory() { + return rundir; + } + + private String create_crashlog_name() { + // simpan format awal + String dateformat = DateTime.getDateFormat(); + String timeformat = DateTime.getTimeFormat(); + + // ganti format Datetime + DateTime.setDateFormat("ddMMyyyy"); + DateTime.setTimeFormat("HHmmss"); + + StringBuilder result = new StringBuilder(); + long tt = DateTime.getNow(); + result.append("Crash-"); + result.append(DateTime.Date(tt)); + result.append("-"); + result.append(DateTime.Time(tt)); + result.append(".log"); + + // balikin format Datetime ke format awal + DateTime.setDateFormat(dateformat); + DateTime.setTimeFormat(timeformat); + return result.toString(); + } + + public void WriteLog(String appname, String msg) { + File logfile = new File(rundir, create_applog_name(appname)); + + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logfile.getAbsolutePath(),true)); + writer.write(create_log_time()); + writer.write(msg); + writer.newLine(); + writer.close(); + } catch (IOException e) { + BA.Log("Unable to Create Crash Log, Msg : "+e.getMessage()); + } + } + + private String create_log_time() { + String timeformat = DateTime.getTimeFormat(); + DateTime.setTimeFormat("HH:mm:ss"); + String result = DateTime.Time(DateTime.getNow())+"\t"; + DateTime.setTimeFormat(timeformat); + return result; + } + + private String create_applog_name(String appname) { + // simpan format awal + String dateformat = DateTime.getDateFormat(); + + // ganti format Datetime + DateTime.setDateFormat("ddMMyyyy"); + + StringBuilder result = new StringBuilder(); + long tt = DateTime.getNow(); + result.append(appname.trim()); + result.append("-"); + result.append(DateTime.Date(tt)); + result.append(".log"); + + // balikin format Datetime ke format awal + DateTime.setDateFormat(dateformat); + return result.toString(); + } + +} diff --git a/src/SBC/CPU_Info.java b/src/SBC/CPU_Info.java new file mode 100644 index 0000000..c9d4c55 --- /dev/null +++ b/src/SBC/CPU_Info.java @@ -0,0 +1,95 @@ +package SBC; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("CPU_Info") +public class CPU_Info { + private String _serial=null; + private String _hardware=null; + private String _processor=null; + private int _proc_count = -1; + private final String serialpattern_string = "^Serial\\s+:\\s(\\S+)$"; + + // revisi 07022024 + //private final String hardwarepattern_string1 = "^Hardware\\s+:\\s(\\S+)$"; + private final String hardwarepattern_string = "^Hardware\\s+:\\s(\\S+( \\S+)*)$"; + + // baru, ada di huawei + private final String processorpattern_string = "^Processor\\s+:\\s(\\S+( \\S+)*)$"; + + private final String processorcountpattern_string = "^processor\\s+:\\s(\\d)$"; + + + public CPU_Info(String[] cpuinfovalues) { + + if (cpuinfovalues!=null) { + if (cpuinfovalues.length>0) { + + Stream.of(cpuinfovalues).forEach(ss ->{ + //BA.Log("CPU Info value : "+ss); + // CPU Serial Number + if (ss.matches(serialpattern_string)) { + Matcher m = Pattern.compile(serialpattern_string).matcher(ss); + if (m.find()) _serial = m.group(1); + } + // Hardware + if (ss.matches(hardwarepattern_string)) { + Matcher m = Pattern.compile(hardwarepattern_string).matcher(ss); + if (m.find()) _hardware = m.group(1); + } + + // Processor + if (ss.matches(processorpattern_string)) { + Matcher m = Pattern.compile(processorpattern_string).matcher(ss); + if (m.find()) _processor = m.group(1); + } + + // Processor Count + if (ss.matches(processorcountpattern_string)) { + Matcher m = Pattern.compile(processorcountpattern_string).matcher(ss); + if (m.find()) { + try { + int vv = Integer.valueOf(m.group(1)); + if (vv > _proc_count) _proc_count = vv; + } catch(NumberFormatException e) { + + } + + } + } + }); + } + } + } + + public String getSerial() { + return _serial; + } + + public String getHardware() { + return _hardware; + } + + public String getProcessorName() { + return _processor; + } + + public int getCPUCores() { + return _proc_count+1; + } + + public boolean isValid() { + if (_serial!=null && _serial.length()>0) { + if (_hardware!=null && _hardware.length()>0) { + if (_proc_count>-1) { + return true; + } + } + } + return false; + } +} diff --git a/src/SBC/GeneralAndroid.java b/src/SBC/GeneralAndroid.java new file mode 100644 index 0000000..3b6fd05 --- /dev/null +++ b/src/SBC/GeneralAndroid.java @@ -0,0 +1,728 @@ +package SBC; + +import java.io.IOException; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.nio.file.Files; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import androgpio.mycodes; +import androgpio.customsocket.JsonObject; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.os.Build; +import android.provider.Settings; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.DateTime; + +@BA.ShortName("GeneralAndroid") +@BA.Permissions(values= { + "android.permission.INTERNET", + "android.permission.ACCESS_NETWORK_STATE", + "android.permission.ACCESS_WIFI_STATE", + "android.permission.READ_PRIVILEGED_PHONE_STATE" + }) +@BA.Events(values= { + "wifinetworkstatus(available as boolean, blocked as boolean, network as NetworkInterface_info)", + "cellularnetworkstatus(available as boolean, blocked as boolean, network as NetworkInterface_info)", + "ethernetnetworkstatus(available as boolean, blocked as boolean, network as NetworkInterface_info)", + "bluetoothnetworkstatus(available as boolean, blocked as boolean, network as NetworkInterface_info)", + "vpnnetworkstatus(available as boolean, blocked as boolean, network as NetworkInterface_info)", + "usbnetworkstatus(available as boolean, blocked as boolean, network as NetworkInterface_info)" + }) + +public class GeneralAndroid extends BasicSBCInfo { + + ConnectivityManager _cm = null; + + NetworkCallback ncb_wifi = null; + NetworkCallback ncb_cellular = null; + NetworkCallback ncb_ethernet = null; + NetworkCallback ncb_bluetooth = null; + NetworkCallback ncb_vpn = null; + NetworkCallback ncb_usb = null; + + public GeneralAndroid() { + super(); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + // do something + raise_log("GeneralAndroid ShutdownHook called"); + if (_cm != null) { + if (ncb_wifi != null) { + _cm.unregisterNetworkCallback(ncb_wifi); + ncb_wifi = null; + } + if (ncb_cellular != null) { + _cm.unregisterNetworkCallback(ncb_cellular); + ncb_cellular = null; + } + if (ncb_ethernet != null) { + _cm.unregisterNetworkCallback(ncb_ethernet); + ncb_ethernet = null; + } + if (ncb_bluetooth != null) { + _cm.unregisterNetworkCallback(ncb_bluetooth); + ncb_bluetooth = null; + } + if (ncb_vpn != null) { + _cm.unregisterNetworkCallback(ncb_vpn); + ncb_vpn = null; + } + if (ncb_usb != null) { + _cm.unregisterNetworkCallback(ncb_usb); + ncb_usb = null; + } + + + } + } + }); + } + + /** + * Initialize General Android Device + * @param callerobject Callback object + * @param event Event name + */ + public void Initialize(BA bax, Object callerobject, String event) { + setup_events(bax, callerobject, event, this); + if (bax!=null) { + if (bax.context != null) { + _cm = (ConnectivityManager) bax.context.getSystemService(Context.CONNECTIVITY_SERVICE); + } + } + } + + + @BA.Hide + @Override + public double Get_CPU_CoreVolt() { + // Belum bisa diimplementasikan + return 0; + } + + @SuppressWarnings("deprecation") + /** + * Get Android Serial Number + * On old android, will return Build.Serial + * On Android 8.0 and above, will return Build.getSerial() + * if failed will return Settings.Secure.ANDROID_ID + * @return Serialnumber in string + */ + public String Get_SerialNumber() { + String value = Build.UNKNOWN; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + try { + value = Build.getSerial(); + + if (BA.debugMode) raise_log("Get_SerialNumber using Build.getSerial()"); + } catch (SecurityException e) { + //raise_log("Get_SerialNumber Build.getSerial exception = " + e.getMessage()); + } + + } else { + value = Build.SERIAL; + if (BA.debugMode) raise_log("Get_SerialNumber using Build.SERIAL"); + } + + if (value!=null && value.length()>0) { + if (!value.equals("unknown")) return value; + } + if (BA.debugMode) raise_log("Get_SerialNumber using Settings.Secure.ANDROID_ID"); + return Settings.Secure.getString(ba.context.getContentResolver(), Settings.Secure.ANDROID_ID); + } + + + /** + * Get All registered Network Adapters, whether up, down, or virtual + * @return null if not available or failed + */ + public String[] Get_NetworkAdapters() { + try { + Enumeration values = NetworkInterface.getNetworkInterfaces(); + if (values!=null) { + List result = new ArrayList<>(); + while(values.hasMoreElements()) { + result.add(values.nextElement().getName()); + } + return result.toArray(new String[0]); + } + + } catch (SocketException e) { + raise_log("Get_NetworkAdapters exception = "+e.getMessage()); + } + return new String[0]; + } + + + /** + * Get MAC Address + * @param networkdevice : network device name eth0 / wlan0 + * @return MAC (String) if success, or empty string if failed + */ + public String Get_MAC(String networkdevice) { + try { + NetworkInterface xx = NetworkInterface.getByName(networkdevice); + if (xx!=null) { + byte[] _mac = xx.getHardwareAddress(); + if (mycodes.valid_bytearray(_mac)) { + StringBuilder str = new StringBuilder(); + for(byte vv: _mac) { + if (str.length()>0) str.append(":"); + str.append(mycodes.Byte_toHex(vv).toUpperCase()); + } + return str.toString(); + } + } + } catch (SocketException | NullPointerException e) { + raise_log("Get_MAC exception = "+e.getMessage()); + } + return ""; + } + + /** + * Get Network Status if Up or Down + * @param networkdevice : network device name eth0 / wlan0 + * @return true if UP, or false if DOWN + */ + public boolean NetworkIsActive(String networkdevice) { + try { + NetworkInterface xx = NetworkInterface.getByName(networkdevice); + if (xx!=null) { + + return xx.isUp(); + } + } catch (SocketException e) { + raise_log("NetworkIsActive exception = "+e.getMessage()); + } + return false; + } + + private Map _map_network = new HashMap<>(); + + /** + * Register Network Status + * will raise event_wifinetworstatus, event_cellularnetworkstatus, event_ethernetnetworkstatus, event_bluetoothnetworkstatus, event_vpnnetworkstatus, event_usbnetworkstatus + * @param networktype : WIFI, CELLULAR, ETHERNET, BLUETOOTH, VPN, USB + */ + public void StartMonitorNetworkStatus(String networktype) { + if (_cm != null) { + final String nettype = networktype.toUpperCase().trim(); + final String _event = eventname+ "_"+nettype.toLowerCase()+"networkstatus"; + final boolean need_event = ba!=null ? ba.subExists(_event) : false; + + if (BA.debugMode) { + if (need_event) { + raise_log("StartMonitorNetworkStatus, networktype="+networktype+" will raise " + _event); + } else { + raise_log("StartMonitorNetworkStatus, networktype="+networktype+" no event to raise"); + } + } + + NetworkRequest.Builder b = new NetworkRequest.Builder(); + + NetworkCallback ncb = new NetworkCallback() { + @Override + public void onAvailable(Network network) { + //raise_log(nettype + " NetworkCallback onAvailable, handle = "+network.getNetworkHandle()); + NetworkInterface_info nii = _map_network.get(network.getNetworkHandle()); + if (nii != null) { + nii.setAvailable(true); + } else { + nii = new NetworkInterface_info(ba, network); + nii.setAvailable(true); + _map_network.put(network.getNetworkHandle(), nii); + } + + if (need_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, _event, false, new Object[] {nii.getAvailable(), nii.getBlocked(), nii}); + } + } + + @Override + public void onLost(Network network) { + //raise_log(nettype + " NetworkCallback onLost, handle = "+network.getNetworkHandle()); + NetworkInterface_info nii = _map_network.get(network.getNetworkHandle()); + if (nii != null) { + nii.setAvailable(false); + } else { + nii = new NetworkInterface_info(ba, network); + nii.setAvailable(false); + _map_network.put(network.getNetworkHandle(), nii); + } + + if (need_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, _event, false, new Object[] {nii.getAvailable(), nii.getBlocked(), nii}); + } + } + + @Override + public void onLinkPropertiesChanged(Network network, LinkProperties lp) { + //raise_log(nettype + " NetworkCallback onLinkPropertiesChanged, handle = "+network.getNetworkHandle()); + NetworkInterface_info nii = _map_network.get(network.getNetworkHandle()); + if (nii != null) { + nii.setLinkProperties(lp); + } else { + nii = new NetworkInterface_info(ba, network); + nii.setLinkProperties(lp); + _map_network.put(network.getNetworkHandle(), nii); + } + + if (need_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, _event, false, new Object[] {nii.getAvailable(), nii.getBlocked(), nii}); + } + } + + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { + //raise_log(nettype + " NetworkCallback onCapabilitiesChanged, handle = "+network.getNetworkHandle()); + NetworkInterface_info nii = _map_network.get(network.getNetworkHandle()); + if (nii != null) { + nii.setNetworkCapabilities(nc); + } else { + nii = new NetworkInterface_info(ba, network); + nii.setNetworkCapabilities(nc); + _map_network.put(network.getNetworkHandle(), nii); + } + if (need_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, _event, false, + new Object[] { nii.getAvailable(), nii.getBlocked(), nii }); + } + } + + @Override + public void onBlockedStatusChanged(Network network, boolean blocked) { + //raise_log(nettype + " NetworkCallback onBlockedStatusChanged, handle = "+network.getNetworkHandle()); + NetworkInterface_info nii = _map_network.get(network.getNetworkHandle()); + if (nii != null) { + nii.setBlocked(blocked); + } else { + nii = new NetworkInterface_info(ba, network); + nii.setBlocked(blocked); + _map_network.put(network.getNetworkHandle(), nii); + } + if (need_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, _event, false, + new Object[] { nii.getAvailable(), nii.getBlocked(), nii }); + } + } + }; + + switch(nettype) { + case "WIFI": + b.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + ncb_wifi = ncb; + break; + case "CELLULAR": + b.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + ncb_cellular = ncb; + break; + case "ETHERNET": + b.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); + ncb_ethernet = ncb; + break; + case "BLUETOOTH": + b.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH); + ncb_bluetooth = ncb; + break; + case "VPN": + b.addTransportType(NetworkCapabilities.TRANSPORT_VPN); + ncb_vpn = ncb; + break; + case "USB": + b.addTransportType(NetworkCapabilities.TRANSPORT_USB); + ncb_usb = ncb; + break; + } + + NetworkRequest nr = b.build(); + _cm.registerNetworkCallback(nr, ncb); + + } + } + + /** + * Unregister Network Status + * @param networktype : WIFI, CELLULAR, ETHERNET, BLUETOOTH, VPN, USB + */ + public void StopMonitorNetworkStatus(String networktype) { + if (_cm != null) { + final String nettype = networktype.toUpperCase().trim(); + switch(nettype) { + case "WIFI": + if (ncb_wifi != null) { + _cm.unregisterNetworkCallback(ncb_wifi); + ncb_wifi = null; + } + break; + case "CELLULAR": + if (ncb_cellular != null) { + _cm.unregisterNetworkCallback(ncb_cellular); + ncb_cellular = null; + } + break; + case "ETHERNET": + if (ncb_ethernet != null) { + _cm.unregisterNetworkCallback(ncb_ethernet); + ncb_ethernet = null; + } + break; + case "BLUETOOTH": + if (ncb_bluetooth != null) { + _cm.unregisterNetworkCallback(ncb_bluetooth); + ncb_bluetooth = null; + } + break; + case "VPN": + if (ncb_vpn != null) { + _cm.unregisterNetworkCallback(ncb_vpn); + ncb_vpn = null; + } + break; + case "USB": + if (ncb_usb != null) { + _cm.unregisterNetworkCallback(ncb_usb); + ncb_usb = null; + } + break; + + } + } + } + + /** + * Get Current Network Type + * possible return value : WIFI, CELLULAR, ETHERNET, BLUETOOTH, VPN, USB, UNKNOWN + * @return null if failed + */ + public String ActiveNetwork_Type() { + if (_cm != null) { + NetworkCapabilities capabilities = _cm.getNetworkCapabilities(_cm.getActiveNetwork()); + if (capabilities != null) { + + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + return "WIFI"; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + return "CELLULAR"; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { + return "ETHERNET"; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { + return "BLUETOOTH"; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { + return "VPN"; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_USB)) { + return "USB"; + } else { + return "UNKNOWN"; + } + } + } + return null; + } + + /** + * Check if Current Network is connected to Internet + * @return true if connected, or false if not connected + */ + public boolean ActiveNetwork_IsInternetConnected() { + if (_cm!=null) { + NetworkCapabilities capabilities = _cm.getNetworkCapabilities(_cm.getActiveNetwork()); + if (capabilities != null) { + return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); + } + } + return false; + } + + /** + * Check if current network can ping to target + * @param target : target IP Address or Hostname + * @return true if can ping, or false if not + */ + public boolean ActiveNetwork_CanPing(String target) { + try { + Process pingprocess = Runtime.getRuntime().exec("ping -c 1 " + target); + int exitvalue = pingprocess.waitFor(); + return exitvalue == 0; + } catch (IOException e) { + if (BA.debugMode) raise_log("ActiveNetwork_CanPing exception = "+e.getMessage()); + } catch (InterruptedException e) { + if (BA.debugMode) raise_log("ActiveNetwork_CanPing exception = "+e.getMessage()); + } + return false; + } + + + /** + * Check if Network is a specific type + * @param n Network object + * @param type Type constant : TRANSPORT_WIFI, TRANSPORT_CELLULAR, TRANSPORT_ETHERNET, TRANSPORT_BLUETOOTH, TRANSPORT_VPN, TRANSPORT_USB + * @return true if it is, or false if it's not + */ + private boolean NetworkIsType(Network n, int type) { + if (n != null) { + if (_cm != null) { + NetworkCapabilities nc = _cm.getNetworkCapabilities(n); + if (nc != null) { + return nc.hasTransport(type); + } + } + } + return false; + } + + /** + * Check if Android have Wifi Network + * @return true if have, or false if not + */ + public boolean Have_Wifi_Network() { + if (_cm != null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + return networks.stream().anyMatch(e -> NetworkIsType(e, NetworkCapabilities.TRANSPORT_WIFI)); + } + return false; + } + + /** + * Check if Android have Ethernet Network + * @return true if have, or false if not + */ + public boolean Have_Ethernet_Network() { + if (_cm != null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + return networks.stream().anyMatch(e -> NetworkIsType(e, NetworkCapabilities.TRANSPORT_ETHERNET)); + } + return false; + } + + /** + * Check if Android have Cellular Network + * @return true if have, or false if not + */ + public boolean Have_Cellular_Network() { + if (_cm != null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + return networks.stream().anyMatch(e -> NetworkIsType(e, NetworkCapabilities.TRANSPORT_CELLULAR)); + } + return false; + } + + /** + * Check if Android have Bluetooth Network + * + * @return true if have, or false if not + */ + public boolean Have_Bluetooth_Network() { + if (_cm != null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + return networks.stream().anyMatch(e -> NetworkIsType(e, NetworkCapabilities.TRANSPORT_BLUETOOTH)); + } + return false; + } + + /** + * Check if Android have VPN Network + * + * @return true if have, or false if not + */ + public boolean Have_VPN_Network() { + if (_cm != null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + return networks.stream().anyMatch(e -> NetworkIsType(e, NetworkCapabilities.TRANSPORT_VPN)); + } + return false; + } + + /** + * Check if Android have USB Network + * + * @return true if have, or false if not + */ + public boolean Have_USB_Network() { + if (_cm != null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + return networks.stream().anyMatch(e -> NetworkIsType(e, NetworkCapabilities.TRANSPORT_USB)); + } + return false; + } + + + /** + * Get Network Interface with Wifi Type (first find, the rest not checked) + * @return null if not available + */ + public NetworkInterface_info Get_Wifi_Network() { + if (_cm!=null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + Network n = networks.stream().filter(e->NetworkIsType(e, NetworkCapabilities.TRANSPORT_WIFI )).findFirst().orElse(null); + if (n!=null) { + return new NetworkInterface_info(ba, n); + } + } + return null; + } + + /** + * Get Network Interface with Ethernet Type (firdt find, the rest not checked) + * @return null if not available + */ + public NetworkInterface_info Get_Ethernet_Network() { + if (_cm!=null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + Network n = networks.stream().filter(e->NetworkIsType(e, NetworkCapabilities.TRANSPORT_ETHERNET)).findFirst().orElse(null); + if (n!=null) { + return new NetworkInterface_info(ba, n); + } + } + + return null; + } + + /** + * Get Network Interface with Cellular Type (first find, the rest not checked) + * @return null if not available + */ + public NetworkInterface_info Get_Celluar_Network() { + if (_cm!=null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + Network n = networks.stream().filter(e->NetworkIsType(e, NetworkCapabilities.TRANSPORT_CELLULAR)).findFirst().orElse(null); + if (n!=null) { + return new NetworkInterface_info(ba, n); + } + } + return null; + } + + /** + * Get Network Interface with Bluetooth Type (first find, the rest not checked) + * @return null if not available + */ + public NetworkInterface_info Get_Bluetooth_Network() { + if (_cm != null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + Network n = networks.stream().filter(e -> NetworkIsType(e, NetworkCapabilities.TRANSPORT_BLUETOOTH)) + .findFirst().orElse(null); + if (n != null) { + return new NetworkInterface_info(ba, n); + } + } + return null; + } + + /** + * Get Network Interface with VPN Type (first find, the rest not checked) + * @return null if not available + */ + public NetworkInterface_info Get_VPN_Network() { + if (_cm != null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + Network n = networks.stream().filter(e -> NetworkIsType(e, NetworkCapabilities.TRANSPORT_VPN)).findFirst() + .orElse(null); + if (n != null) { + return new NetworkInterface_info(ba, n); + } + } + return null; + } + + /** + * Get Network Interface with USB Type (first find, the rest not checked) + * + * @return null if not available + */ + public NetworkInterface_info Get_USB_Network() { + if (_cm != null) { + @SuppressWarnings("deprecation") + List networks = Arrays.asList(_cm.getAllNetworks()); + Network n = networks.stream().filter(e -> NetworkIsType(e, NetworkCapabilities.TRANSPORT_USB)).findFirst() + .orElse(null); + if (n != null) { + return new NetworkInterface_info(ba, n); + } + } + return null; + } + + /** + * Return File Information in JsonObject + * Object structure : + * { + * FileName : short filename + * FileSize : size of file + * Duration : put 0 + * CreationTime : creation time + * AccessTime : access time + * ModifiedTime : modified time + * } + * @param filename filename to check + * @return null if failed + */ + public JsonObject GetFileInformation(String filename) { + + if (mycodes.valid_string(filename)) { + java.io.File f = new java.io.File(filename); + if (f.exists()) { + if (f.isFile()) { + try { + BasicFileAttributes attr = Files.readAttributes(f.toPath(), BasicFileAttributes.class); + JsonObject jo = new JsonObject(); + jo.InitializeEmpty(); + jo.PutString("FileName", f.getName()); + jo.PutLong("FileSize", f.length()); + jo.PutInt("Duration", 0); + jo.PutString("CreationTime", MakeDateTimeFromLong(attr.creationTime().to(TimeUnit.MILLISECONDS))); + jo.PutString("AccessTime", MakeDateTimeFromLong(attr.lastAccessTime().to(TimeUnit.MILLISECONDS))); + jo.PutString("ModifiedTime", MakeDateTimeFromLong(attr.lastModifiedTime().to(TimeUnit.MILLISECONDS))); + return jo; + } catch (IOException e) { + raise_log("GetFileInformation exception = "+e.getMessage()); + } + + + } + } + } + return null; + } + + private final String[] monthname = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; + + private String MakeDateTimeFromLong(long value) { + if (value>0) { + StringBuilder str = new StringBuilder(); + str.append(DateTime.GetDayOfMonth(value)).append(" "); + str.append(monthname[DateTime.GetMonth(value)-1]).append(" "); + str.append(DateTime.GetYear(value)).append(" "); + str.append(DateTime.GetHour(value)).append(":"); + str.append(DateTime.GetMinute(value)).append(":"); + str.append(DateTime.GetSecond(value)); + return str.toString(); + } + return ""; + } +} diff --git a/src/SBC/GetProp_Info.java b/src/SBC/GetProp_Info.java new file mode 100644 index 0000000..c5266ec --- /dev/null +++ b/src/SBC/GetProp_Info.java @@ -0,0 +1,130 @@ +package SBC; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("GetProp_Info") +public class GetProp_Info { + private final String regex = "\\[(\\S+)\\]: \\[(\\S+)\\]"; + private final Pattern p = Pattern.compile(regex); + private Map mm; + public GetProp_Info(String[] values) { + mm = new HashMap<>(); + if (values!=null && values.length>0) { + Stream.of(values).forEach(vv ->{ + Matcher xx = p.matcher(vv); + if (xx.find()) { + mm.put(xx.group(1), xx.group(2)); + } + }); + } + } + + public boolean HaveProperties() { + if (mm!=null) { + if (mm.size()>0) { + return true; + } + } + return false; + } + + public String[] GetKeys() { + if (mm!=null) { + if (mm.size()>0) { + return mm.keySet().toArray(new String[0]); + } + } + return null; + } + + public String getBuildType() { + return GetValue("ro.product.build.type",null); + } + + public String getBuildFingerprint() { + return GetValue("ro.product.build.fingerprint",GetValue("ro.build.fingerprint",null)); + } + + public String getBuildID() { + return GetValue("ro.build.id",null); + } + + public String getBuildDate() { + return GetValue("ro.build.date",null); + } + + public String getBuildProduct() { + return GetValue("ro.build.product",null); + } + + public String getAndroidVersion() { + return GetValue("ro.build.version.release",null); + } + + public String getAndroidSDKVersion() { + return GetValue("ro.build.version.sdk",null); + } + + public String getSecurityPatchDate() { + return GetValue("ro.build.version.security_patch",null); + } + + public String getBoardVersion() { + + return GetValue("ro.board.version",GetValue("ro.product.hardwareversion",null)); + } + + public String getSoftwareVersion() { + return GetValue("ro.sys.info.software_version",null); + } + + public String getBoardPlatform() { + return GetValue("ro.board.platform",null); + } + + public String getBoardName() { + return GetValue("ro.board.name", GetValue("ro.board.boardname",null)); + } + + public String getProductModel() { + return GetValue("ro.product.model",null); + } + + public String getProductName() { + return GetValue("ro.product.name", null); + } + + public String getNetHostName() { + return GetValue("net.hostname", null); + } + + public String getHardware() { + return GetValue("ro.hardware",null); + } + + public String getBoardBrand() { + return GetValue("ro.product.brand",null); + } + + public String GetValue(String key, String ifnotfound) { + if (key!=null && key.length()>0) { + String lcasekey = key.toLowerCase(); + if (HaveProperties()) { + String kk = mm.keySet().stream() + .filter(e -> e.toLowerCase().indexOf(lcasekey)!=-1) + .findFirst() + .orElse(null); + if (kk!=null && kk.length()>0) { + return mm.get(kk); + } + } + } + return ifnotfound; + } +} diff --git a/src/SBC/Hwmon_Info.java b/src/SBC/Hwmon_Info.java new file mode 100644 index 0000000..102dfd5 --- /dev/null +++ b/src/SBC/Hwmon_Info.java @@ -0,0 +1,90 @@ +package SBC; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.regex.Pattern; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Hwmon_Info") +public class Hwmon_Info { + private String name; + private double temperature; + private double critical; + private String regex_temp_input = "temp(\\d+)_input"; + private String regex_temp_critical = "temp(\\d+)_crit"; + Hwmon_Info(File hwmondir){ + if (hwmondir!=null) { + if (hwmondir.isDirectory()) { + File[] files = hwmondir.listFiles(); + if (files!=null) { + for (File file : files) { + if (file.getName().contains("name")) { + name = readfile(file); + } + if (Pattern.matches(regex_temp_input, file.getName())) { + String _value = readfile(file); + temperature = Integer.parseUnsignedInt(_value)/1000.0; + } + if (Pattern.matches(regex_temp_critical, file.getName())) { + String _value = readfile(file); + critical = Integer.parseUnsignedInt(_value)/1000.0; + } + } + } + } + } + } + + private String readfile(File file) { + if (file!=null) { + if (file.isFile() && file.canRead()) { + try(BufferedReader reader = new BufferedReader(new FileReader(file))){ + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine())!= null) { + sb.append(line); + } + return sb.toString(); + } catch (FileNotFoundException e) { + + e.printStackTrace(); + } catch (IOException e) { + + e.printStackTrace(); + } + + + } + } + return null; + } + + Hwmon_Info(String name, String temperature, String critical){ + this.name = name; + this.temperature = Integer.parseUnsignedInt(temperature)/1000.0; + this.critical = Integer.parseUnsignedInt(critical)/1000.0; + } + + public String getName() { + return name; + } + + public double getTemperature() { + return temperature; + } + + public double getCritical() { + return critical; + } + + public boolean IsValid() { + if (name==null || temperature==0 || critical==0) { + return false; + } + return true; + } +} diff --git a/src/SBC/Ifconfig_Info.java b/src/SBC/Ifconfig_Info.java new file mode 100644 index 0000000..957643b --- /dev/null +++ b/src/SBC/Ifconfig_Info.java @@ -0,0 +1,201 @@ +package SBC; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Ifconfig_Info") +public class Ifconfig_Info { + //final String regex = "^(\\S+)\\s+Link encap:(\\S+)\\s+HWaddr\\s+(\\S+).*\\s+inet addr:(\\S+)\\s+Bcast:(\\S+)\\s+Mask:(\\S+)\\n\\s+inet6 addr: (\\S+).*\\n.*MTU:(\\d+).*\\n.*RX packets:(\\d+) errors:(\\d+) dropped:(\\d+) overruns:(\\d+) frame:(\\d+)\\n.*TX packets:(\\d+) errors:(\\d+) dropped:(\\d+) overruns:(\\d+) carrier:(\\d+)\\n.*collisions:(\\d+) txqueuelen:(\\d+)\\n.*RX bytes:(\\d+).*TX bytes:(\\d+)\\n.*$"; + private final String regex_devicename = "(\\S+).*Link encap.*"; + private final String regex_devicetype = ".*Link encap:(\\S+).*"; + private final String regex_macaddress = ".*HWaddr (\\S+).*"; + private final String regex_driver = ".*Driver (\\S+).*"; + private final String regex_ipv4 = ".*inet addr:(\\S+).*"; + private final String regex_broadcastv4 = ".*Bcast:(\\S+).*"; + private final String regex_subnetv4 = ".*Mask:(\\S+).*"; + private final String regex_ipv6 = ".*inet6 addr: (\\S+).*"; + private final String regex_scope = ".*Scope: (\\S+).*"; + private final String regex_mtu = ".*MTU:(\\d+).*"; + private final String regex_rx_status = ".*RX packets:(\\d+).errors:(\\d+).dropped:(\\d+).overruns:(\\d+).frame:(\\d+).*"; + private final String regex_tx_status = ".*TX packets:(\\d+).errors:(\\d+).dropped:(\\d+).overruns:(\\d+).carrier:(\\d+).*"; + private final String regex_collision = ".*collisions:(\\d+).*"; + private final String regex_txqueuelen = ".*txqueuelen:(\\d+).*"; + private final String regex_rx_tx_bytes = ".*RX bytes:(\\d+).TX bytes:(\\d+).*"; + //private final String regex_interrupt = ".*Interrupt:(\\d+).*"; + private String _devicename; + private String _devicetype; + private String _macaddress; + private String _ipv4; + private String _broadcastv4; + private String _subnetv4; + private String _ipv6; + private int _mtu; + private int _rx_packet; + private int _rx_error; + private int _rx_dropped; + private int _rx_overrun; + private int _rx_frame; + private int _tx_packet; + private int _tx_error; + private int _tx_dropped; + private int _tx_overrun; + private int _tx_carrier; + private int _collision; + private int _tx_queue_len; + private long _rx_bytes; + private long _tx_bytes; + private String _driver; + private String _scope; + //private int _interrupt; + + public String getDeviceName() {return _devicename;} + public String getDeviceType() {return _devicetype;} + public String getMACAddress() {return _macaddress;} + public String getIPAddressV4() {return _ipv4;} + public String getBroadcastAddressV4() {return _broadcastv4;} + public String getSubnetMaskV4() {return _subnetv4;} + public String getIPAddressV6() {return _ipv6;} + public int getMTU() {return _mtu;} + public int getRX_Packet() {return _rx_packet;} + public int getRX_Error() {return _rx_error;} + public int getRX_Dropped() {return _rx_dropped;} + public int getRX_Overrun() {return _rx_overrun;} + public int getRX_Frame() {return _rx_frame;} + public int getTX_Packet() {return _tx_packet;} + public int getTX_Error() {return _tx_error;} + public int getTX_Dropped() {return _tx_dropped;} + public int getTX_Overrun() {return _tx_overrun;} + public int getTX_Carrier() {return _tx_carrier;} + public int getCollision() {return _collision;} + public int getTX_Queue_Length() {return _tx_queue_len;} + public long getRX_bytes() {return _rx_bytes;} + public long getTX_bytes() {return _tx_bytes;} + public String getDriver() {return _driver;} + public String getScope() {return _scope;} + //public int getInterrupt() {return _interrupt;} + + public boolean isValid() { + return valid_string(_devicename) & valid_string(_macaddress) & valid_string(_ipv4); + } + + public Ifconfig_Info(String[] values) { + if (values!=null && values.length>0) { + for(String xx : values) { + if (valid_string(xx)) { + if (Pattern.matches(regex_devicename, xx)) { + _devicename = getvalue(xx, regex_devicename,1, null); + } + if (Pattern.matches(regex_devicetype, xx)) { + _devicetype = getvalue(xx,regex_devicetype,1,null); + } + if (Pattern.matches(regex_macaddress, xx)) { + _macaddress = getvalue(xx,regex_macaddress,1,null); + } + if (Pattern.matches(regex_ipv4, xx)) { + _ipv4 = getvalue(xx,regex_ipv4,1,null); + } + if (Pattern.matches(regex_broadcastv4, xx)) { + _broadcastv4 = getvalue(xx,regex_broadcastv4,1,null); + } + if (Pattern.matches(regex_subnetv4, xx)) { + _subnetv4 = getvalue(xx,regex_subnetv4,1,null); + } + if (Pattern.matches(regex_ipv6, xx)) { + _ipv6 = getvalue(xx,regex_ipv6,1,null); + } + if (Pattern.matches(regex_mtu, xx)) { + _mtu = getvalue(xx,regex_mtu,1,0); + } + if (Pattern.matches(regex_rx_status, xx)) { + _rx_packet = getvalue(xx,regex_rx_status,1,0); + _rx_error = getvalue(xx,regex_rx_status,2,0); + _rx_dropped = getvalue(xx,regex_rx_status,3,0); + _rx_overrun = getvalue(xx,regex_rx_status,4,0); + _rx_frame = getvalue(xx,regex_rx_status,5,0); + } + if (Pattern.matches(regex_tx_status, xx)) { + _tx_packet = getvalue(xx,regex_tx_status,1,0); + _tx_error = getvalue(xx,regex_tx_status,2,0); + _tx_dropped = getvalue(xx,regex_tx_status,3,0); + _tx_overrun = getvalue(xx,regex_tx_status,4,0); + _tx_carrier = getvalue(xx,regex_tx_status,5,0); + } + if (Pattern.matches(regex_collision, xx)) { + _collision = getvalue(xx,regex_collision,1,0); + } + if (Pattern.matches(regex_driver, xx)) { + _driver = getvalue(xx,regex_driver,1,null); + } + if (Pattern.matches(regex_scope, xx)) { + _scope = getvalue(xx,regex_scope,1,null); + } + if (Pattern.matches(regex_txqueuelen, xx)) { + _tx_queue_len = getvalue(xx,regex_txqueuelen,1,0); + } + if (Pattern.matches(regex_rx_tx_bytes, xx)) { + _rx_bytes = getvalue(xx,regex_rx_tx_bytes,1,0l); + _tx_bytes = getvalue(xx,regex_rx_tx_bytes,2,0l); + } +// if (Pattern.matches(regex_interrupt, xx)) { +// _interrupt = getvalue(xx,regex_interrupt,1,0); +// } + } + } + } + } + + + + private boolean valid_string(String x) { + if (x!=null) { + if (x.length()>0) { + return true; + } + } + return false; + } + + private String getvalue(final String source, final String regex, int index, String ifnotexists) { + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(source); + if (m.find()) { + if (index<=m.groupCount()) { + return m.group(index); + } + } + return ifnotexists; + } + + private int getvalue(final String source, final String regex, int index, int ifnotexists) { + String result = getvalue(source,regex,index,null); + if (result!=null && result.length()>0) { + try { + int resultint = Integer.parseInt(result); + return resultint; + } catch(NumberFormatException e) { + + } + } + return ifnotexists; + } + + private long getvalue(final String source, final String regex, int index, long ifnotexists) { + String result = getvalue(source,regex,index,null); + if (result!=null && result.length()>0) { + try { + long resultlong = Long.parseLong(result); + return resultlong; + } catch(NumberFormatException e) { + + } + } + return ifnotexists; + } + + +} + + + + diff --git a/src/SBC/NetworkInterface_info.java b/src/SBC/NetworkInterface_info.java new file mode 100644 index 0000000..e9aa7b4 --- /dev/null +++ b/src/SBC/NetworkInterface_info.java @@ -0,0 +1,522 @@ +package SBC; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import androgpio.mycodes; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.IpPrefix; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.RouteInfo; +import anywheresoftware.b4a.*; + +@BA.ShortName("NetworkInterface_info") +public class NetworkInterface_info { + private NetworkInterface _networkinterface = null; + private Network _network = null; + private NetworkCapabilities _networkcapabilities = null; + private LinkProperties _linkproperties = null; + private final BA _ba; + private ConnectivityManager _cm = null; + + private boolean _blocked = false; + private boolean _available = false; + + /** + * Create uninitialized NetworkInterface_Info. + */ + public NetworkInterface_info() { + _ba = null; + } + + @BA.Hide + /** + * Create NetworkInterface_Info from NetworkInterface. + * @param net NetworkInterface to wrap. + */ + public NetworkInterface_info(BA ba, NetworkInterface net) { + _networkinterface = net; + _ba = ba; + if (_ba != null) { + if (_ba.context != null) { + _cm = (ConnectivityManager) _ba.context.getSystemService(Context.CONNECTIVITY_SERVICE); + } + } + } + + @BA.Hide + /** + * Create NetworkInterface_Info from Network. + * @param net Network to wrap. + */ + public NetworkInterface_info(BA ba, Network net) { + _ba = ba; + if (_ba != null) { + if (_ba.context != null) { + _cm = (ConnectivityManager) _ba.context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (_cm!=null) { + if (net != null) { + LinkProperties prop = _cm.getLinkProperties(net); + if (prop != null) { + try { + _networkinterface = NetworkInterface.getByName(prop.getInterfaceName()); + _available = true; + } catch (SocketException e) { + + } + } + } + + } + } + } + } + + public boolean getAvailable() { + return _available; + } + + @BA.Hide + public void setAvailable(boolean value) { + _available = value; + } + + public boolean getBlocked() { + return _blocked; + } + + @BA.Hide + public void setBlocked(boolean value) { + _blocked = value; + } + + @BA.Hide + public void setNetwork(Network net) { + _network = net; + } + + @BA.Hide + public Network getNetwork() { + return _network; + } + + @BA.Hide + public void setNetworkCapabilities(NetworkCapabilities nc) { + _networkcapabilities = nc; + } + + @BA.Hide + public NetworkCapabilities getNetworkCapabilities() { + return _networkcapabilities; + } + + @BA.Hide + public void setLinkProperties(LinkProperties lp) { + _linkproperties = lp; + } + + @BA.Hide + public LinkProperties getLinkProperties() { + return _linkproperties; + } + + private NetworkInterface LinkPropertiesToNetworkInterface() { + if (_linkproperties != null) { + try { + return NetworkInterface.getByName(_linkproperties.getInterfaceName()); + } catch (SocketException e) { + + } + } + return null; + } + + /** + * Check if the network interface is up and running. + * @return true if the network interface is up, false if not available or not up. + */ + public boolean isUp() { + + if (_networkinterface ==null) _networkinterface = LinkPropertiesToNetworkInterface(); + + if (_networkinterface != null) { + try { + return _networkinterface.isUp(); + } catch (SocketException e) { + } + } + return false; + } + + /** + * Get NetworkInterface name. + * @return null if not available. + */ + public String GetName() { + if (_networkinterface == null) _networkinterface = LinkPropertiesToNetworkInterface(); + + if (_networkinterface != null) { + return _networkinterface.getName(); + } + return null; + } + + /** + * Get MAC Address of the network interface. + * @return null if not available. + */ + public String GetMACAddress() { + if (_networkinterface == null) _networkinterface = LinkPropertiesToNetworkInterface(); + if (_networkinterface != null) { + try { + byte[] mac = _networkinterface.getHardwareAddress(); + if (mac != null) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < mac.length; i++) { + sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : "")); + } + return sb.toString(); + } + } catch (SocketException e) { + } + } + return null; + } + + /** + * Check if the network interface supports multicast. + * @return true if supports multicast, false if not available or not supported. + */ + public boolean SupportsMulticast() { + if (_networkinterface == null) _networkinterface = LinkPropertiesToNetworkInterface(); + if (_networkinterface != null) { + try { + return _networkinterface.supportsMulticast(); + } catch (SocketException e) { + } + } + return false; + } + + /** + * Get IP Address of the network interface. + * @param mode 0 = IPv4, 1 = IPv6, other = Both + * @return comma separated string of IP Addresses. + */ + public String GetInetAddresses_String(int mode) { + String[] addresses = GetInetAddresses(mode); + return String.join(",", addresses); + } + + /** + * Get Gateway IPV4 Address. + * @return "0.0.0.0" if not available + */ + public String GetGateway() { + if (_linkproperties!=null) { + for (RouteInfo route : _linkproperties.getRoutes()) { + if (route.isDefaultRoute()) { + InetAddress gateway = route.getGateway(); + if (gateway.getAddress().length == 4) { + return gateway.getHostAddress(); + } + } + } + } else BA.Log("GetGateway failed, linkproperties is null"); + return "0.0.0.0"; + } + + /** + * Get Netmask of the network interface. + * @return "0.0.0.0" if failed + */ + public String GetNetmask() { + if (_linkproperties!=null) { + for (RouteInfo route : _linkproperties.getRoutes()) { + if (route.isDefaultRoute()) { + IpPrefix prefix = route.getDestination(); + return prefix.getPrefixLength()+""; + } + } + } else BA.Log("GetNetmask failed, linkproperties is null"); + return "0.0.0.0"; + } + + /** + * Get IP Address of the network interface. + * @param mode 0 = IPv4, 1 = IPv6, other = Both + * @return array of IP Addresses in string. + */ + public String[] GetInetAddresses(int mode) { + if (_networkinterface == null) _networkinterface = LinkPropertiesToNetworkInterface(); + if (_networkinterface!=null) { + Enumeration inetAddresses = _networkinterface.getInetAddresses(); + if (inetAddresses != null) { + List result = new ArrayList(); + while (inetAddresses.hasMoreElements()) { + InetAddress addr = inetAddresses.nextElement(); + if (addr.isLoopbackAddress()) { + continue; + } + + switch(mode) { + case 0: + if (addr.getAddress().length == 4) { + result.add(addr.getHostAddress()); + } + + break; + case 1: + if (addr.getAddress().length == 16) { + result.add(addr.getHostAddress()); + } + + break; + default: + result.add(addr.getHostAddress()); + break; + } + + + } + return result.toArray(new String[0]); + } + } + return new String[0]; + } + + /** + * Get the maximum transmission unit (MTU) of the network interface. + * @return 0 if not available. + */ + public int GetMTU() { + if (_networkinterface == null) _networkinterface = LinkPropertiesToNetworkInterface(); + if (_networkinterface != null) { + try { + return _networkinterface.getMTU(); + } catch (SocketException e) { + } + } + return 0; + } + + /** + * Get Connectivity Type of the network interface. + * Possible values are WIFI, CELLULAR, ETHERNET, USB, BLUETOOTH, VPN, UNKNOWN + * @return null if failed + */ + public String GetConnectivityType() { + if (_networkcapabilities != null) { + if (_networkcapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + return "WIFI"; + } else if (_networkcapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + return "CELLULAR"; + } else if (_networkcapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { + return "ETHERNET"; + } else if (_networkcapabilities.hasTransport(NetworkCapabilities.TRANSPORT_USB)) { + return "USB"; + } else if (_networkcapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { + return "BLUETOOTH"; + } else if (_networkcapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { + return "VPN"; + } else { + return "UNKNOWN"; + } + } + return null; + } + + /** + * Check if the network interface is configured for internet and validated. + * @return false if not confirmed + */ + public boolean HaveInternetCapability() { + if (_networkcapabilities != null) { + return _networkcapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + && _networkcapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); + } + return false; + } + + + + /** + * Check if the network interface is internet connected + * Wait time to check is 3 seconds. + * @param host specific host to use for checking internet connection. + * On null or empty, will default to www.google.com + * @param timeout timeout in milliseconds. On 0 or negative, will default to 3000. + * @return true if the network interface is internet connected. + */ + public boolean CanConnectToHost(String host, int timeout) { + if (isUp()) { + if (host == null || host.isEmpty()) { + host = "www.google.com"; + } + + if (timeout <= 0) { + timeout = 3000; + } + + if (_network!=null) { + try{ + URLConnection urlconnect = _network.openConnection(new URL("https://" + host)); + urlconnect.setConnectTimeout(timeout); + urlconnect.connect(); + return true; + } catch(SocketTimeoutException e) { + if (BA.debugMode) BA.Log("CanConnectToHost timeout: " + e.getMessage()); + } catch (IOException e) { + if (BA.debugMode) BA.Log("CanConnectToHost exception: " + e.getMessage()); + } + } + } + return false; + } + + /** + * Get DHCP Server IPV4 Address. + * IPV6 is not supported. + * @return "0.0.0.0" if not available. + */ + public String GetDHCPServer() { + if (_linkproperties != null) { + InetAddress inet = _linkproperties.getDhcpServerAddress(); + if (inet != null) { + return inet.getHostAddress(); + } + } + return "0.0.0.0"; + } + + /** + * Get DNS Server IPV4 Address. + * @return array of DNS Server IP Addresses in string. + */ + public String[] GetDNSServer() { + if (_linkproperties != null) { + List dns = _linkproperties.getDnsServers(); + if (dns != null) { + List result = new ArrayList(); + for (InetAddress addr : dns) { + result.add(addr.getHostAddress()); + } + return result.toArray(new String[0]); + } + } + return new String[0]; + } + + /** + * Get Downstream Bandwidth of the network interface. + * + * @return upstream in Kbps, or 0 if not available. + */ + public int GetUpstreamBandwidth() { + if (_networkcapabilities != null) { + return _networkcapabilities.getLinkUpstreamBandwidthKbps(); + } + return 0; + } + + /** + * Get Downstream Bandwidth of the network interface. + * + * @return downstream in Kbps, or 0 if not available. + */ + public int GetDownstreamBandwidth() { + if (_networkcapabilities != null) { + return _networkcapabilities.getLinkDownstreamBandwidthKbps(); + } + return 0; + } + + @BA.Hide + /** + * Set DNS Server IPV4 Address. + * + * @param dns1 : DNS Server 1 in IPV4 Address + * @param dns2 : DNS Server 2 in IPV4 Address + * @return true if success, false if failed. + */ + public boolean SetDNS(String dns1, String dns2) { + //TODO masih belum jelas + if (_linkproperties != null) { + List dns = new ArrayList(); + if (mycodes.valid_string(dns1)) { + try { + dns.add(InetAddress.getByName(dns1)); + } catch(UnknownHostException e) { + if (BA.debugMode) BA.Log("SetDNS exception on dns1: " + e.getMessage()); + } + } else if (BA.debugMode) BA.Log("SetDNS invalid dns1"); + + if (mycodes.valid_string(dns2)) { + try { + dns.add(InetAddress.getByName(dns2)); + } catch (UnknownHostException e) { + if (BA.debugMode) BA.Log("SetDNS exception on dns2: " + e.getMessage()); + } + } else if (BA.debugMode) BA.Log("SetDNS invalid dns2"); + + _linkproperties.setDnsServers(dns); + return true; + } + return false; + } + + @BA.Hide + /** + * Set DHCP Server IPV4 Address. + * @param dhcp + * @return true if success, false if failed. + */ + public boolean SetDHCPServer(String dhcp) { + //TODO masih belum jelas + if (_linkproperties != null) { + try { + _linkproperties.setDhcpServerAddress((Inet4Address) Inet4Address.getByName(dhcp)); + return true; + } catch (UnknownHostException e) { + if (BA.debugMode) BA.Log("SetDHCPServer exception: " + e.getMessage()); + } + } + return false; + } + + @BA.Hide + public boolean SetStaticIP(String ip) { + //TODO masih belum jelas + if (_linkproperties != null) { + + } + return false; + } + + + @BA.Hide + private List toInetAddressList(String[] addresses) { + List result = new ArrayList(); + for (String addr : addresses) { + try { + result.add(InetAddress.getByName(addr)); + } catch (IOException e) { + + } + } + return result; + } +} diff --git a/src/SBC/NetworkMeterInfo.java b/src/SBC/NetworkMeterInfo.java new file mode 100644 index 0000000..4457fce --- /dev/null +++ b/src/SBC/NetworkMeterInfo.java @@ -0,0 +1,330 @@ +package SBC; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.DateTime; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("NetworkMeterInfo") +public class NetworkMeterInfo { + public final String InterfaceName; + public final long ticktime; + public final long Total_RX_Bytes; + public final long Total_TX_Bytes; + public final long Total_RX_Packets; + public final long Total_TX_Packets; + public final long Total_RX_Errors; + public final long Total_TX_Errors; + public final long Total_RX_Drop; + public final long Total_TX_Drop; + + + private boolean isvalid = false; + + private long rx_pps = 0; + private long tx_pps = 0; + private long rx_bps = 0; + private long tx_bps = 0; + public NetworkMeterInfo() { + InterfaceName=""; + ticktime = DateTime.getNow(); + Total_RX_Bytes = 0; + Total_TX_Bytes = 0; + Total_RX_Packets =0; + Total_TX_Packets = 0; + Total_RX_Errors = 0; + Total_TX_Errors = 0; + Total_RX_Drop = 0; + Total_TX_Drop = 0; + isvalid = false; + } + public NetworkMeterInfo(String ifname, long tick, long rx_bytes, long tx_bytes, long rx_packets, long tx_packets, long rx_errors, long tx_errors, long rx_drop, long tx_drop) { + InterfaceName = ifname; + ticktime = tick; + Total_RX_Bytes = rx_bytes; + Total_TX_Bytes = tx_bytes; + Total_RX_Packets = rx_packets; + Total_TX_Packets = tx_packets; + Total_RX_Errors = rx_errors; + Total_TX_Errors = tx_errors; + Total_RX_Drop = rx_drop; + Total_TX_Drop = tx_drop; + if (!InterfaceName.isEmpty()) { + if (ticktime>0) { + if (Total_TX_Bytes>0) { + if (Total_RX_Bytes>0) { + if (Total_TX_Packets>0) { + if (Total_RX_Packets>0) { + isvalid = true; + } + } + } + } + } + } + } + + /** + * Check if contain valid value + * @return true if valid + */ + public boolean IsValidValue() { + return isvalid; + } + + /** + * RX Bytes per second + * @return value in long + */ + public long RX_BytesPerSecond() { + return rx_bps; + } + + /** + * RX Packets per second + * @return value in long + */ + public long RX_PacketsPerSecond() { + return rx_pps; + } + + /** + * TX Bytes per second + * @return value in long + */ + public long TX_BytesPerSecond() { + return tx_bps; + } + + /** + * TX Packets per second + * @return value in long + */ + public long TX_PacketsPerSecond() { + return tx_pps; + } + + public long TX_Errors() {return Total_TX_Errors;} + + public long RX_Errors() {return Total_RX_Errors;} + + public long RX_Drop() {return Total_RX_Drop;} + + public long TX_Drop() {return Total_TX_Drop;} + + protected void calculate(NetworkMeterInfo prev) { + if (prev instanceof NetworkMeterInfo) { + if (prev.isvalid) { + rx_pps = Total_RX_Packets - prev.Total_RX_Packets; + tx_pps = Total_TX_Packets - prev.Total_TX_Packets; + rx_bps = Total_RX_Bytes - prev.Total_RX_Bytes; + tx_bps = Total_TX_Bytes - prev.Total_TX_Bytes; + + if (rx_pps<1) rx_pps = 0; // tidak boleh negatif + if (tx_pps<1) tx_pps = 0; + if (rx_bps<1) rx_bps = 0; + if (tx_bps<1) tx_bps = 0; + } + } + } + + private final long kp = 1000; + private final long mp = kp * 1000; + private final long gp = mp * 1000; + private final long tp = gp * 1000; + private final long kb = 1024; + private final long mb = kb * 1024; + private final long gb = mb * 1024; + private final long tb = gb * 1024; + + /** + * Current Total received packets, in readable string + * @return value in string + */ + public String Current_RX_PacketTotal() { + if (this.Total_RX_Packets < kp) { + return String.valueOf(Total_RX_Packets); + } else if (Total_RX_Packets < mp) { + return pembulatan_1_desimal(Total_RX_Packets, kp)+" K"; + } else if (Total_RX_Packets _cpumap = new HashMap<>(); + private long _boottime = 0; + private int _processes = 0; + private int _procsrunning = 0; + private int _procsblocked = 0; + + /** + * Get the time which the System booted, in seconds since January 1, 1970. + * If want to get DateTime tick (long), multiply by DateTime.TicksPerSecond. + * @return seconds since January 1, 1970. + */ + public long getBoottime() { + return _boottime; + } + + /** + * Get the Boottime in String format. + * @return Date and Time in String format. + */ + public String getBootTimeString() { + long tick = _boottime * DateTime.TicksPerSecond; + return DateTime.Date(tick)+" "+DateTime.Time(tick); + } + + /** + * Get the number of created processes and threads. + * @return of created processes and threads. + */ + public int getProcesses() { + return _processes; + } + + /** + * Get the number of processes currently running in CPU + * @return running processes in CPU. + */ + public int getProcessRunning() { + return _procsrunning; + } + + /** + * Get the number of processes currently blocked, waiting for I/O to complete. + * @return blocked processes + */ + public int getProcessBlocked() { + return _procsblocked; + } + + public cpudata getTotalCpuData() { + return _cpudata; + } + + public cpudata[] getCpuData() { + return _cpumap.values().toArray(new cpudata[_cpumap.size()]); + } + + public ProcStat_Info(String[] values) { + if (values != null && values.length > 0) { + for(String vv : values) { + if (Pattern.matches(regex_cpu, vv)) { + Matcher m = Pattern.compile(regex_cpu).matcher(vv); + if (m.find()) { + int _user = getvalue(m.group(1), 0); + int _nice = getvalue(m.group(2), 0); + int _system = getvalue(m.group(3), 0); + int _idle = getvalue(m.group(4), 0); + int _iowait = getvalue(m.group(5), 0); + int _irq = getvalue(m.group(6), 0); + int _softirq = getvalue(m.group(7), 0); + int _steal = getvalue(m.group(8), 0); + int _guest = getvalue(m.group(9), 0); + int _guestnice = getvalue(m.group(10), 0); + _cpudata = new cpudata("Total",_user,_nice,_system,_idle,_iowait,_irq,_softirq,_steal,_guest,_guestnice); + + } + } + if (Pattern.matches(regex_cpuN, vv)) { + + Matcher m = Pattern.compile(regex_cpuN).matcher(vv); + if (m.find()) { + String _name = m.group(1); + int _user = getvalue(m.group(2), 0); + int _nice = getvalue(m.group(3), 0); + int _system = getvalue(m.group(4), 0); + int _idle = getvalue(m.group(5), 0); + int _iowait = getvalue(m.group(6), 0); + int _irq = getvalue(m.group(7), 0); + int _softirq = getvalue(m.group(8), 0); + int _steal = getvalue(m.group(9), 0); + int _guest = getvalue(m.group(10), 0); + int _guestnice = getvalue(m.group(11), 0); + cpudata n = new cpudata(_name,_user,_nice,_system,_idle,_iowait,_irq, _softirq,_steal,_guest,_guestnice); + _cpumap.put(_name, n); + + } + } + if (Pattern.matches(regex_boottime, vv)) { + + Matcher m = Pattern.compile(regex_boottime).matcher(vv); + if (m.find()) { + _boottime = Long.parseLong(m.group(1)); + + } + } + if (Pattern.matches(regex_processes, vv)) { + + Matcher m = Pattern.compile(regex_processes).matcher(vv); + if (m.find()) { + _processes = Integer.parseInt(m.group(1)); + + } + } + if (Pattern.matches(regex_procsrunning, vv)) { + + Matcher m = Pattern.compile(regex_procsrunning).matcher(vv); + if (m.find()) { + _procsrunning = Integer.parseInt(m.group(1)); + + } + } + if (Pattern.matches(regex_procsblocked, vv)) { + + Matcher m = Pattern.compile(regex_procsblocked).matcher(vv); + if (m.find()) { + _procsblocked = Integer.parseInt(m.group(1)); + + } + } + + } + } + } + + private int getvalue(String value, int defaultValue) { + try { + return Integer.parseInt(value); + } catch (Exception e) { + return defaultValue; + } + } + +} diff --git a/src/SBC/RAM_Info.java b/src/SBC/RAM_Info.java new file mode 100644 index 0000000..6eeadb6 --- /dev/null +++ b/src/SBC/RAM_Info.java @@ -0,0 +1,195 @@ +package SBC; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("RAM_Info") +public class RAM_Info { + + private double _jvmtotalmemory; + private double _jvmfreememory; + private double _jvmmaxmemory; + private double _jvmfreepercentage; + private final String memtotal_pattern = "^MemTotal:\\s+(\\d+)\\skB$"; + private double _memtotal; + private final String memfree_pattern = "^MemFree:\\s+(\\d+)\\skB$"; + private double _memfree; + private final String memavailable_pattern = "^MemAvailable:\\s+(\\d+)\\skB$"; + private double _memavailable; + private double _memfreepercentage; + private final String swaptotal_pattern = "^SwapTotal:\\s+(\\d+)\\skB$"; + private double _swaptotal; + private final String swapfree_pattern = "^SwapFree:\\s+(\\d+)\\skB$"; + private double _swapfree; + private double _swapfreepercentage; // tambahan + + private final int _KB = 1000; + + + public double JVMTotalMemory_KB() { + return _jvmtotalmemory; + } + + public double JVMFreeMemory_KB() { + return _jvmfreememory; + } + + public double JVMMaxMemory_KB() { + return _jvmmaxmemory; + } + + public double JVMFreePercentage() { + return _jvmfreepercentage; + } + + public double MemTotal_KB() { + return _memtotal; + } + + public double MemTotal() { + return _memtotal * _KB; + } + + public double MemFree_KB() { + return _memfree; + } + + public double MemFree() { + return _memfree * _KB; + } + + public double MemUsed_KB() { + return _memtotal - _memfree; + } + + public double MemUsed() { + return MemUsed_KB() * _KB; + } + + public double MemAvailable_KB() { + return _memavailable; + } + + public double MemAvailable() { + return _memavailable * _KB; + } + + public double MemFreePercentage() { + return _memfreepercentage; + } + + public double SwapTotal_KB() { + return _swaptotal; + } + + public double SwapFree_KB() { + return _swapfree; + } + + public double SwapFreePercentage() { + return _swapfreepercentage; + } + + public RAM_Info(String[] values) { + Runtime jvm = java.lang.Runtime.getRuntime(); + _jvmtotalmemory = pembulatan_1desimal(jvm.totalMemory() / 1024.0); + _jvmfreememory = pembulatan_1desimal(jvm.freeMemory() / 1024.0); + _jvmmaxmemory = pembulatan_1desimal(jvm.maxMemory() / 1024.0); + _jvmfreepercentage = pembulatan_1desimal((_jvmfreememory/_jvmtotalmemory)*100); + Stream.of(values).forEach(xx ->{ + if (xx.matches(memavailable_pattern)) { + _memavailable = getvalue(xx, memavailable_pattern); + } + if (xx.matches(memfree_pattern)) { + _memfree = getvalue(xx, memfree_pattern); + } + if (xx.matches(memtotal_pattern)) { + _memtotal = getvalue(xx, memtotal_pattern); + } + if (xx.matches(swapfree_pattern)) { + _swapfree = getvalue(xx, swapfree_pattern); + } + if (xx.matches(swaptotal_pattern)) { + _swaptotal = getvalue(xx, swaptotal_pattern); + } + }); + } + + private double getvalue(String value, String pattern) { + Matcher m = Pattern.compile(pattern).matcher(value); + if (m.find()) + return Double.valueOf(m.group(1)); + else + return 0; + } + + + + private double pembulatan_1desimal(double value) { + int temp = (int)(value * 10); + return (temp / 10.0); + } + + private final double memory_MB = 1024; + private final double memory_GB = memory_MB * 1024; + private final double memory_TB = memory_GB * 1024; + /** + * Return an easy to read from values inside this class + * @param value : Member of this class, example MemTotal_KB, or MemAvailable_KB + * @return String value + */ + public String EasyRead_Value(double value) { + if (value < memory_MB) { + return pembulatan_1desimal(value)+ " KB"; + } else if (value < memory_GB) { + return pembulatan_1desimal(value / memory_MB)+" MB"; + } else if (value < memory_TB) { + return pembulatan_1desimal(value / memory_GB)+" GB"; + } else return pembulatan_1desimal(value / memory_TB)+" TB"; + } + + /** + * Return an easy to read from values inside this class + * @param value value to read + * @return Size_Info object + */ + public Size_Info EasyRead_SizeInfo(double value) { + Size_Info xx = new Size_Info(); + xx.SetInKB(value); + return xx; + //TODO hapus kalau sudah benar +// if (value < memory_MB) { +// return new Size_Info(pembulatan_1desimal(value), "KB"); +// } else if (value < memory_GB) { +// return new Size_Info(pembulatan_1desimal(value / memory_MB), "MB"); +// } else if (value < memory_TB) { +// return new Size_Info(pembulatan_1desimal(value / memory_GB), "GB"); +// } else +// return new Size_Info(pembulatan_1desimal(value / memory_TB), "TB"); + } + + /** + * Create values in JSON Map + * @return Map value + */ + public Map MakeJSONMap() { + Map result = new Map(); + result.Initialize(); + result.Put("JVMTotalMemory", EasyRead_Value(this._jvmtotalmemory)); + result.Put("JVMFreeMemory", EasyRead_Value(this._jvmfreememory)); + result.Put("JVMMaxMemory", EasyRead_Value(this._jvmmaxmemory)); + result.Put("JVMFreePercentage", this._jvmfreepercentage); + result.Put("MemTotal", EasyRead_Value(this._memtotal)); + result.Put("MemFree", EasyRead_Value(this._memfree)); + result.Put("MemFreePercentage", this._memfreepercentage); + result.Put("MemAvailable", EasyRead_Value(this._memavailable)); + result.Put("SwapTotal", EasyRead_Value(this._swaptotal)); + result.Put("SwapFree", EasyRead_Value(this._swapfree)); + result.Put("SwapFreePercentage", this._swapfreepercentage); + return result; + } +} diff --git a/src/SBC/RaspberryPi/Pi4.java b/src/SBC/RaspberryPi/Pi4.java new file mode 100644 index 0000000..30cc8db --- /dev/null +++ b/src/SBC/RaspberryPi/Pi4.java @@ -0,0 +1,369 @@ +package SBC.RaspberryPi; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import SBC.BasicSBCInfo; +import SBC.WifiInfo; +import anywheresoftware.b4a.BA; + + +@BA.ShortName("RaspberryPi4") +public class Pi4 extends BasicSBCInfo { + + public Pi4() { + super(); + } + + + /** + * Equal Pi4J GPIO 8 + * atau GPIO02 di referensi Linux + * Dipakai I2C 1 (SDA) + */ + public final int pin03 = 2; + + /** + * Equal Pi4J GPIO 9 + * atau GPIO03 di referensi Linux + * Dipakai I2C 1 (SCL) + */ + public final int pin05 = 3; + + /** + * Equal Pi4J GPIO 7 + * atau GPIO04 di referensi Linux + * Dipakai GPCLK0 + */ + public final int pin07 = 4; + + /** + * Equal Pi4J GPIO 0 + * atau GPIO17 di referensi Linux + */ + public final int pin11 = 17; + + /** + * Equal Pi4J GPIO 2 + * atau GPIO27 di referensi Linux + * Biasa dipakai sebagai Relay 1 di PCB Leon + */ + public final int pin13 = 27; + + /** + * PCB Leon, Relay 1, di pin 13 + */ + public final int PCB_Relay1 = pin13; + + /** + * Equal Pi4J GPIO 3 + * atau GPIO22 di referensi Linux + * Biasa dipakai sebagai Relay 2 di PCB Leon + */ + public final int pin15 = 22; + + /** + * PCB Leon, Relay 2, di pin 15 + */ + public final int PCB_Relay2 = pin15; + + + /** + * Equal Pi4J GPIO 12 + * atau GPIO10 di referensi Linux + * Dipakai SPI 0 (MOSI) + * Biasa dipakai sebagai Relay 3 di PCB Leon + */ + public final int pin19 = 10; + + /** + * PCB Leon, relay 3 , di pin 19 + */ + public final int PCB_Relay3 = pin19; + + /** + * Equal Pi4J GPIO 13 + * atau GPIO09 di referensi Linux + * Dipakai SPI 0 (MISO) + * Biasa dipakai sebagai Relay 4 di PCB Leon + */ + public final int pin21 = 9; + + /** + * PCB Leon, relay 4 , di pin 21 + */ + public final int PCB_Relay4 = pin21; + + /** + * Equal Pi4J GPIO 14 + * atau GPIO11 di referensi Linux + * Dipakai SPI 0 (CLK) + * Biasa dipakai sebagai Action LED di PCB Leon + */ + public final int pin23 = 11; + + /** + * PCB Leon, Action LED, di pin 23 + */ + public final int PCB_ActionLED = pin23; + + /** + * Equal Pi4J GPIO 30 + * atau GPIO00 di referensi Linux + * Dipakai I2C 0 (SDA) + */ + public final int pin27 = 0; + + /** + * Equal Pi4J GPIO 21 + * atau GPIO05 di referensi Linux + * Biasa dipakai sebagai Network LED di PCB Leon + */ + public final int pin29 = 5; + + /** + * PCB Leon, Network LED, di pin 29 + */ + public final int PCB_NetworkLED = pin29; + + /** + * Equal Pi4J GPIO 22 + * atau GPIO6 di referensi Linux + */ + public final int pin31 = 6; + + /** + * Equal Pi4J GPIO 23 + * atau GPIO13 di referensi Linux + * Dipakai PWM 1 + */ + public final int pin33 = 13; + + /** + * Equal Pi4J GPIO 24 + * atau GPIO19 di referensi Linux + */ + public final int pin35 = 19; + + /** + * Equal Pi4J GPIO 25 + * atau GPIO26 di referensi Linux + */ + public final int pin37 = 26; + + /** + * Equal Pi4J GPIO 15 + * atau GPIO14 di referensi Linux + * Dipakai UART 0 (TXD) + */ + public final int pin08 = 14; + + /** + * Equal Pi4J GPIO 16 + * atau GPIO15 di referensi Linux + * Dipakai UART 0 (RXD) + */ + public final int pin10 = 15; + + /** + * Equal Pi4J GPIO 1 + * atau GPIO18 di referensi Linux + * Dipakai PWM 0 + * Biasa dipakai sebagai Button 1 di PCB Leon + */ + public final int pin12 = 18; + + /** + * PCB Leon, Button 1 , di pin 12; + */ + public final int PCB_Button1 = pin12; + + /** + * Equal Pi4J GPIO 4 + * atau GPIO23 di referensi Linux + * Biasa dipakai sebagai Button 2 di PCB Leon + */ + public final int pin16 = 23; + + /** + * PCB Leon, Button 2, di pin 16 + */ + public final int PCB_Button2 = pin16; + + /** + * Equal Pi4J GPIO 5 + * atau GPIO24 di referensi Linux + * Biasa dipakai sebagai Button 3 di PCB Leon + */ + public final int pin18 = 24; + + /** + * PCB Leon, Button 3, di pin 18 + */ + public final int PCB_Button3 = pin18; + + /** + * Equal Pi4J GPIO 6 + * atau GPIO25 di referensi Linux + * Biasa dipakai sebagai Button 4 di PCB Leon + */ + public final int pin22 = 25; + + /** + * PCB Leon, Button 4, di pin 22 + */ + public final int PCB_Button4 = pin22; + + /** + * Equal Pi4J GPIO 10 + * atau GPIO8 di referensi Linux + * dipakai SPI 0 (Chip Enable 0) + * Biasa dipakai sebagai Button 5 di PCB Leon + */ + public final int pin24 = 8; + + /** + * PCB Leon, Button 5 , di pin 24 + */ + public final int PCB_Button5 = pin24; + + /** + * Equal Pi4J GPIO 11 + * atau GPIO7 di referensi Linux + * Dipakai SPI 0 (Chip Enable 1) + */ + public final int pin26 = 7; + + /** + * Equal Pi4J GPIO 26 + * atau GPIO12 di referensi Linux + * Dipakai PWM 0 + */ + public final int pin32 = 12; + + /** + * Equal Pi4J GPIO 27 + * atau GPIO16 di referensi Linux + */ + public final int pin36 = 16; + + /** + * Equal Pi4J GPIO 28 + * atau GPIO20 di referensi Linux + */ + public final int pin38 = 20; + + /** + * Equal Pi4J GPIO 29 + * atau GPIO21 di referensi Linux + */ + public final int pin40 = 21; + + public final String wlan_name = "wlan0"; + public final String eth_name = "eth0"; + + /** + * Nama untuk serial Bluetooth adalah ttyAMA0 + */ + public final String serial_Bluetooth = "/dev/ttyAMA0"; + + /** + * Nama untuk serial port UART adalah ttyS0 + */ + public final String serial_UART = "/dev/ttyS0"; + + /** + * I2C Port 1 + */ + public final String i2c_port1 = "/dev/i2c-1"; + + /** + * I2C Port 20, somehow bisa detect ini ?? + */ + public final String i2c_port20 = "/dev/i2c-20"; + + /** + * I2C Port 21, somehow bisa detect ini ?? + */ + public final String i2c_port21 = "/dev/i2c-21"; + + + /** + * Belum bisa, return 0 + */ + @Override + public double Get_CPU_CoreVolt() { + // TODO Auto-generated method stub + return 0; + } + + /** + * Initialize Pi4 for Android + * @param {Object} callerobject callerobject + * @param {String} event eventname + */ + public void Initialize(BA bax, Object callerobject, String event) { + setup_events(bax, callerobject, event, this); + + } + + /** + * Try to get Wifi Information + * @param wlanname wifi device name + * @return null if failed + */ + public WifiInfo Get_Wifi_Info(String wlanname) { + WifiInfo result = null; + if (wlanname!=null && wlanname.length()>0) { + try { + final String cmd = "iw dev "+wlanname+" info"; + final String interfacename = "Interface "+wlanname; + final String addr = "addr "; + final String ssid = "ssid "; + final String channel = "channel "; + final String txpower = "txpower "; + + Process prc = Runtime.getRuntime().exec(cmd); + if (prc!=null) { + InputStream ins = prc.getInputStream(); + if (ins!=null) { + BufferedReader bufread = new BufferedReader(new InputStreamReader(ins)); + String line; + do { + line = bufread.readLine(); + if (line!=null && line.length()>0) { + line = line.trim(); + if (line.startsWith(interfacename)) { + // correct interface, create result + result = new WifiInfo(); + } + if (line.startsWith(addr)) { + if (result!=null) result.MAC = line.substring(addr.length()); + } + if (line.startsWith(ssid)) { + if (result!=null) result.SSID = line.substring(ssid.length()); + } + if (line.startsWith("channel")) { + if (result!=null) result.Channel = line.substring(channel.length()); + } + if (line.startsWith("txpower")) { + if (result!=null) result.TxPower = line.substring(txpower.length()); + } + + } + } while(line!=null); + ins.close(); + return result; + } + + } + } catch (IOException e) { + raise_log("Get_Wifi_Info failed, exception = "+e.getMessage()); + } + + } else raise_log("Get_Wifi_Info failed, wlanname is invalid"); + return result; + } +} diff --git a/src/SBC/Size_Info.java b/src/SBC/Size_Info.java new file mode 100644 index 0000000..027ea45 --- /dev/null +++ b/src/SBC/Size_Info.java @@ -0,0 +1,195 @@ +package SBC; +import anywheresoftware.b4a.BA; + +@BA.ShortName("Size_Info") +public class Size_Info { + private long originalValue; + private double _value; + private String _unit; + private final long _KB = 1024; + private final long _MB = 1024 * _KB; + private final long _GB = 1024 * _MB; + private final long _TB = 1024 * _GB; + + /** + * Initialize Size_Info with originalvalue = 0 + */ + public Size_Info() { + originalValue = 0; + Set(originalValue); + } + + /** + * Initialize Size_Info with originalvalue + * @param valueinbytes originalvalue in bytes + */ + public Size_Info(long valueinbytes) { + originalValue = valueinbytes; + Set(valueinbytes); + } + + public Size_Info(double valueinbytes) { + originalValue = (long) valueinbytes; + Set(originalValue); + } + +// @BA.Hide +// public Size_Info(double value, String unit) { +// _value = value; +// _unit = unit; +// } + + /** + * Set originalvalue of Size_Info + * @param value originalvalue in bytes + */ + public void Set(long value) { + if (value < _KB) { + _value = 1.0* value; + _unit = "B"; + } else if (value < _MB) { + _value = 1.0* value / _KB; + _unit = "KB"; + } else if (value < _GB) { + _value = 1.0* value / _MB; + _unit = "MB"; + } else if (value < _TB) { + _value = 1.0* value / _GB; + _unit = "GB"; + } else { + _value = 1.0* value / _TB; + _unit = "TB"; + } + } + + /** + * Set originalvalue in KB + * value will be multiplied by 1024 + * @param value in KB + */ + public void SetInKB(double value) { + originalValue = (long) (value * _KB); + Set(originalValue); + } + + /** + * Set originalvalue in MB value will be multiplied by 1024*1024 + * + * @param value in MB + */ + public void SetInMB(double value) { + originalValue = (long) (value * _MB); + Set(originalValue); + } + + /** + * Set originalvalue in GB value will be multiplied by 1024*1024*1024 + * + * @param value in GB + */ + public void SetInGB(double value) { + originalValue = (long) (value * _GB); + Set(originalValue); + } + + /** + * Set originalvalue in TB value will be multiplied by 1024*1024*1024*1024 + * + * @param value in TB + */ + public void SetInTB(double value) { + originalValue = (long) (value * _TB); + Set(originalValue); + } + + /** + * Get originalvalue in bytes + * @return originalvalue in bytes + */ + public long GetOriginalValue() { + return originalValue; + } + + /** + * Get originalvalue in KB + * @param decimals number of decimals, if decimals < 1, return as is (long decimals) + * @return value in double + */ + public double GetOriginalValueInKB(int decimals) { + double value = 1.0 * originalValue / _KB; + if (decimals < 1) { + return value; + } else { + int vf = (int)(value * Math.pow(10, decimals)); + return 1.0 * vf / Math.pow(10, decimals); + } + } + + /** + * Get originalvalue in MB + * + * @param decimals number of decimals, if decimals < 1, return as is (long + * decimals) + * @return value in double + */ + public double GetOriginalValueInMB(int decimals) { + double value = 1.0 * originalValue / _MB; + if (decimals < 1) { + return value; + } else { + int vf = (int) (value * Math.pow(10, decimals)); + return 1.0 * vf / Math.pow(10, decimals); + } + } + + /** + * Get originalvalue in GB + * + * @param decimals number of decimals, if decimals < 1, return as is (long + * decimals) + * @return value in double + */ + public double GetOriginalValueInGB(int decimals) { + double value = 1.0 * originalValue / _GB; + if (decimals < 1) { + return value; + } else { + int vf = (int) (value * Math.pow(10, decimals)); + return 1.0 * vf / Math.pow(10, decimals); + } + } + + /** + * Get originalvalue in TB + * + * @param decimals number of decimals, if decimals < 1, return as is (long + * decimals) + * @return value in double + */ + public double GetOriginalValueInTB(int decimals) { + double value = 1.0 * originalValue / _TB; + if (decimals < 1) { + return value; + } else { + int vf = (int) (value * Math.pow(10, decimals)); + return 1.0 * vf / Math.pow(10, decimals); + } + } + + /** + * Return Easy-to-Read Value of originalvalue + * @return value in double + */ + public double Value() { + return _value; + } + + /** + * Return Easy-to-Read Unit of originalvalue + * + * @return unit in string + */ + public String Unit() { + return _unit; + } +} diff --git a/src/SBC/Storage_Info.java b/src/SBC/Storage_Info.java new file mode 100644 index 0000000..c61d057 --- /dev/null +++ b/src/SBC/Storage_Info.java @@ -0,0 +1,116 @@ +package SBC; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("Storage_Info") +public class Storage_Info { + public final String Path; + public final double Total; + public final double Free; + public final double Used; + public final double FreePercentage; + public final boolean isvalid; + + public Storage_Info() { + Path=""; + Total=0; + Free=0; + FreePercentage=0; + Used=0; + isvalid = false; + } + + public Storage_Info(Map value) { + Path = (String) value.GetDefault("Path", ""); + Total = (double) value.GetDefault("Total", 0); + Free = (double) value.GetDefault("Free", 0); + Used = Total - Free; + FreePercentage = (double) value.GetDefault("FreePercentage", 0); // sudah pembulatan 1 desimal + if (!Path.isEmpty()) { + if (Total>0) { + isvalid = true; + } else isvalid = false; + } else isvalid = false; + } + + public double Total_KB() { + return Total / file_KB; + } + + public double Free_KB() { + return Free / file_KB; + } + + public double Used_KB() { + return Used / file_KB; + } + + private final double file_KB = 1024; + private final double file_MB = file_KB * 1024; + private final double file_GB = file_MB * 1024; + private final double file_TB = file_GB * 1024; + + private double pembulatan_1desimal(double value) { + int temp = (int)(value * 10); + return (temp / 10.0); + } + + /** + * Return an easy to read from values inside this class + * @param value : Member of this class, either Total or Free + * @return String value + */ + public String EasyRead_Value(double value) { + if (value < file_KB) { + return pembulatan_1desimal(value)+ " B"; + } else if (value < file_MB) { + return pembulatan_1desimal(value / file_KB)+" KB"; + } else if (value < file_GB) { + return pembulatan_1desimal(value / file_MB)+" MB"; + } else if (value < file_TB) { + return pembulatan_1desimal(value / file_GB)+" GB"; + } else { + return pembulatan_1desimal(value / file_TB)+" TB"; + } + } + + /** + * Return an easy to read from values inside this class + * @param value value to read + * @return Size_Info object + */ + public Size_Info EasyRead_SizeInfo(double value) { + return new Size_Info(value); + //TODO hapus kalau sudah benar +// if (value < file_KB) { +// return new Size_Info(pembulatan_1desimal(value), "B"); +// } else if (value < file_MB) { +// return new Size_Info(pembulatan_1desimal(value / file_KB), "KB"); +// } else if (value < file_GB) { +// return new Size_Info(pembulatan_1desimal(value / file_MB), "MB"); +// } else if (value < file_TB) { +// return new Size_Info(pembulatan_1desimal(value / file_GB), "GB"); +// } else { +// return new Size_Info(pembulatan_1desimal(value / file_TB), "TB"); +// } + } + + /** + * Create values in JSON Map + * @return Map value + */ + public Map MakeJSONMap() { + Map result = new Map(); + result.Initialize(); + + result.Put("Path", this.Path); + result.Put("Total", EasyRead_Value(this.Total)); + result.Put("Free", EasyRead_Value(this.Free)); + result.Put("FreePercentage", this.FreePercentage); + + return result; + } + + +} diff --git a/src/SBC/WifiInfo.java b/src/SBC/WifiInfo.java new file mode 100644 index 0000000..c1c07a5 --- /dev/null +++ b/src/SBC/WifiInfo.java @@ -0,0 +1,11 @@ +package SBC; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Wifi_Info") +public class WifiInfo { + public String MAC; + public String SSID; + public String Channel; + public String TxPower; +} diff --git a/src/SBC/cpudata.java b/src/SBC/cpudata.java new file mode 100644 index 0000000..59e8ada --- /dev/null +++ b/src/SBC/cpudata.java @@ -0,0 +1,153 @@ +package SBC; + +import anywheresoftware.b4a.BA; + +/** + * Untuk olah data dari /proc/stat + * Mencari CPU usage + * https://stackoverflow.com/questions/23367857/accurate-calculation-of-cpu-usage-given-in-percentage-in-linux + * @author rdkartono + * + */ +@BA.ShortName("cpudata") +public class cpudata { + public String name; + public long user; + public long nice; + public long system; + public long idle; + public long iowait; + public long irq; + public long softirq; + public long steal; + public long guest; + public long guest_nice; + private boolean correct_data; + + public cpudata() { + this.name = ""; + this.user = 0; + this.nice = 0; + this.system = 0; + this.idle = 0; + this.iowait = 0; + this.irq = 0; + this.softirq = 0; + this.steal = 0; + this.guest = 0; + this.guest_nice = 0; + this.correct_data = false; + } + + public cpudata(String name, long user, long nice, long system, long idle, long iowait, long irq, long softirq, long steal, long guest, long guest_nice) { + this.name = name; + this.user = user; + this.nice = nice; + this.system = system; + this.idle = idle; + this.iowait = iowait; + this.irq = irq; + this.softirq = softirq; + this.steal = steal; + this.guest = guest; + this.guest_nice = guest_nice; + this.correct_data = false; + if (this.name!=null && this.name.length()>0) { + if (getTotalData()>0) { + correct_data = true; + } + } + } + + public boolean isCorrectData() { + return correct_data; + } + + + + /** + * IdleData = idle + iowait + * @return IdleData (Long) + */ + public long getIdleData() { + return idle+iowait; + } + + /** + * SystemData = system + irq + softirq + steal + * @return 0 if invalid + */ + public long getSystemData() { + return system+irq+softirq+steal; + } + + /** + * VirtualData = guest + guest_nice + * @return 0 if invalid + */ + public long getVirtualData() { + return guest+guest_nice; + } + + /** + * BusyData = user + nice + system + irq + softirq + * @return BusyData (Long) + */ + public long getBusyData() { + return user+nice+system+irq+softirq; + } + + /** + * TotalData = IdleData + BusyData + * @return TotalData (Long) + */ + public long getTotalData() { + return getIdleData()+getBusyData(); + } + + /** + * Get Percentage of Idle Time + * @return 0 - 100 + */ + public double getIdlePercentage() { + return (getIdleData() * 100.0) / getTotalData(); + } + + /** + * Get Busy Percentage + * @return 0 - 100 + */ + public double getBusyPercentage() { + return (getBusyData() * 100.0) / getTotalData(); + } + + /** + * Get CPU Usage, in percentage + * @param prev : previous cpudata. if null, then will calculate total average cpu_usage since boot + * @return CPU Usage (double) + */ + public double getCPU_Usage(cpudata prev) { + long prev_total = 0; + long prev_idle = 0; + if (prev != null) { + if (prev.correct_data) { + prev_total = prev.getTotalData(); + prev_idle =prev.getIdleData(); + } + } + + long delta_total = getTotalData() - prev_total; + long delta_idle = getIdleData() - prev_idle; + + double result = delta_total - delta_idle; + result = (result / delta_total) * 100; + return Math.round(result * 10.0) / 10.0; // pembulatan 1 desimal + } + + @Override + public String toString() { + return "name=" + name + ", user=" + user + ", nice=" + nice + ", system=" + system + ", idle=" + idle + + ", iowait=" + iowait + ", irq=" + irq + ", softirq=" + softirq + ", steal=" + steal + ", guest=" + + guest + ", guest_nice=" + guest_nice; + } +} diff --git a/src/androgpio/Closer.java b/src/androgpio/Closer.java new file mode 100644 index 0000000..8db9a54 --- /dev/null +++ b/src/androgpio/Closer.java @@ -0,0 +1,33 @@ +package androgpio; + +import java.io.Closeable; +import java.net.DatagramSocket; +import java.net.Socket; + +import anywheresoftware.b4a.BA; + +public class Closer { + // closeAll() + public static void closeSilently(Object... xs) { + // Note: on Android API levels prior to 19 Socket does not implement Closeable + for (Object x : xs) { + if (x != null) { + try { + BA.Log("closing: "+x); + if (x instanceof Closeable) { + ((Closeable)x).close(); + } else if (x instanceof Socket) { + ((Socket)x).close(); + } else if (x instanceof DatagramSocket) { + ((DatagramSocket)x).close(); + } else { + BA.Log("cannot close: "+x); + throw new RuntimeException("cannot close "+x); + } + } catch (Throwable e) { + BA.Log(e.getMessage()); + } + } + } + } +} diff --git a/src/androgpio/DigitalInput/DigitalInput.java b/src/androgpio/DigitalInput/DigitalInput.java new file mode 100644 index 0000000..58f66ec --- /dev/null +++ b/src/androgpio/DigitalInput/DigitalInput.java @@ -0,0 +1,192 @@ +package androgpio.DigitalInput; + + +import java.io.File; +import java.io.IOException; + +import com.sun.jna.Platform; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; + + +@BA.ShortName("DigitalInput") +@BA.Events(values={"error(pinnumber as int, Msg as String)"}) + +public class DigitalInput { + private BA bax; + private String event=""; + private int pinnya=0; + private Object myobject; + + private boolean need_error_event = false; + + private int last_value = -1; + private boolean currentIsON = false; + private boolean initialized = false; + private DigitalInputEvent javaevent; + + /** + * Buat di JAVA coding + */ + public DigitalInput() { + // do nothing here, buat syarat + + } + + @BA.Hide + /** + * Buat di JAVA coding + * @param pin_number : pin number + */ + public DigitalInput(int pin_number) { + + pinnya = pin_number; + if (Setup_Pin()) initialized = true; + } + + /** + * Initialize Digital Input + * @param caller : caller object + * @param eventname : event name + * @param pinnumber : pin number + */ + public void Initialize(BA xx, Object caller, String eventname,int pinnumber){ + bax = xx; + event=eventname; + pinnya = pinnumber; + myobject = caller; + + if (bax!=null) { + if (myobject!=null) { + if (!event.isEmpty()) { + need_error_event = bax.subExists(event+"_error"); + } + } + } + + + + initialized = true; + } + + /** + * Open Pin for operation + * @return true if success + */ + public boolean Open_Pin() { + if (Platform.isAndroid()) { + if (Setup_Pin()) { + // percuma, gak bisa release +// Runtime.getRuntime().addShutdownHook(new Thread() { +// @Override +// public void run() { +// Release(); +// } +// }); + return true; + } + } else raise_Error("System is not Android based"); + return false; + } + + + private boolean Setup_Pin() { + File ff = new File(mycodes.gpio_direction_path(pinnya)); + try { + if (!ff.exists()) { + // belum ada, export dulu + boolean exportresult = mycodes.GPIO_Export(pinnya); + if (!exportresult) { + // GPIO Export gagal + return false; + } + } + // sampe sini , harusnya direction path ada + return mycodes.GPIO_SetDirection(pinnya, false); + } catch(SecurityException|IOException e) { + BA.Log("Setup_Pin on DigitalInput pin "+pinnya+" failed, Msg : "+e.getMessage()); + } + return false; + } + + + + + @BA.Hide + public void SetJavaEvent(DigitalInputEvent xx) { + javaevent = xx; + } + + public boolean getIsInitialized(){ + return initialized; + } + + + /** + * Read State of GPIO + * @return 0 = Low, 1 = high, -1 = failed reading + */ + public int ReadState(){ + try { + int value = mycodes.GPIO_GetValue(pinnya); + if (value==0) { + currentIsON = false; + } else if (value==1) { + currentIsON = true; + } + + if (last_value != value) { + last_value = value; + if (javaevent!=null) { + if (last_value == 0) { + javaevent.newinstate(pinnya, false); + } else { + javaevent.newinstate(pinnya, true); + } + + } + } + + return value; + } catch (IOException|SecurityException e) { + raise_Error("DigitalInput GPIO="+pinnya+" ReadState error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return -1; + } + } + + + /* + * Get last ReadState value + * return true if High 1 + * return false if Low 0 + */ + public boolean getIsON(){ + return currentIsON; + } + + @BA.Hide + /* + * Release control of Digital Output (not controlling anymore) + * return true if success operation + */ + public boolean Release(){ + try { + return mycodes.GPIO_UnExport(pinnya); + } catch (IOException|SecurityException e) { + raise_Error("DigitalInput GPIO="+pinnya+" Release error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + } + + private void raise_Error(String msg) { + if (need_error_event) { + bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_error", false,new Object[]{pinnya,msg}); + } + if (javaevent!=null) { + javaevent.error(pinnya, msg); + } + + } + +} diff --git a/src/androgpio/DigitalInput/DigitalInputEvent.java b/src/androgpio/DigitalInput/DigitalInputEvent.java new file mode 100644 index 0000000..4563da6 --- /dev/null +++ b/src/androgpio/DigitalInput/DigitalInputEvent.java @@ -0,0 +1,6 @@ +package androgpio.DigitalInput; + +public interface DigitalInputEvent { + void error(int pinnumber, String Msg ); + void newinstate(int pinnumber , boolean isOn); +} diff --git a/src/androgpio/DigitalOutput/DigitalOutput.java b/src/androgpio/DigitalOutput/DigitalOutput.java new file mode 100644 index 0000000..c6a83f8 --- /dev/null +++ b/src/androgpio/DigitalOutput/DigitalOutput.java @@ -0,0 +1,169 @@ +package androgpio.DigitalOutput; + + +import java.io.File; +import java.io.IOException; + +import com.sun.jna.Platform; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; + + +@BA.ShortName("DigitalOutput") +@BA.Events(values={"error(pinnumber as int, Msg as String)"}) + +public class DigitalOutput { + private BA bax; + private String event=""; + private int pinnya=0; + private Object myobject; + private boolean need_error_event = false; + + + private boolean initialized = false; + + private DigitalOutputEvent javaevent; + + /** + * Buat di JAVA coding + */ + public DigitalOutput(){ + + } + + @BA.Hide + /** + * Buat di JAVA coding + * @param pin_number : pin number + * @param initialstate : true --> initial state = high, false --> initial state = low + */ + public DigitalOutput(int pin_number, boolean initialstate){ + pinnya = pin_number; + if (Setup_Pin(initialstate)) initialized = true; + } + + /** + * Initialize Digital Output + * @param caller : caller object + * @param eventname : event name + * @param pinnumber : pin number + */ + public void Initialize(BA xx, Object caller, String eventname,int pinnumber){ + bax = xx; + event=eventname; + pinnya = pinnumber; + myobject = caller; + + if (bax!=null) { + if(myobject!=null) { + if (!event.isEmpty()) { + need_error_event = bax.subExists(event+"_error"); + } + } + } + + initialized = true; + } + + /** + * Open Pin for Operation + * @param initialstate : true --> initial state = high, false --> initial state = low + * @return false kalau gagal + */ + public boolean Open_Pin(boolean initialstate) { + if (Platform.isAndroid()) { + if (Setup_Pin(initialstate)) { + + // Percuma, gak bisa release +// Runtime.getRuntime().addShutdownHook(new Thread() { +// @Override +// public void run() { +// Release(); +// } +// }); + return true; + } + } else raise_Error("System is not Android based"); + return false; + } + + @BA.Hide + public void SetJavaEvent(DigitalOutputEvent xx) { + javaevent = xx; + } + + public boolean getIsInitialized(){ + return initialized; + } + + private boolean Setup_Pin(boolean initialstate) { + File ff = new File(mycodes.gpio_direction_path(pinnya)); + try { + if (!ff.exists()) { + // belum ada, export dulu + boolean exportresult = mycodes.GPIO_Export(pinnya); + if (!exportresult) { + // GPIO Export gagal + return false; + } + } + // sampe sini , harusnya direction path ada + return mycodes.GPIO_SetDirection(pinnya, true); + } catch(SecurityException|IOException e) { + BA.Log("Setup_Pin on DigitalOutput pin "+pinnya+" failed, Msg : "+e.getMessage()); + } + return false; + } + + /* + * Set Digital Output to High + * return true if success operation + */ + public boolean SetHigh(){ + try { + return mycodes.GPIO_SetValue(pinnya, true); + } catch (IOException|SecurityException e) { + raise_Error("DigitalOutput GPIO="+pinnya+" SetHigh error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + } + + /* + * Set Digital Output to Low + * return true if success operation + */ + public boolean SetLow(){ + try { + return mycodes.GPIO_SetValue(pinnya, false); + } catch (IOException|SecurityException e) { + raise_Error("DigitalOutput GPIO="+pinnya+" SetLow error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + } + + @BA.Hide + /* + * Release control of Digital Output (not controlling anymore) + * return true if success operation + */ + public boolean Release(){ + try { + return mycodes.GPIO_UnExport(pinnya); + } catch (IOException|SecurityException e) { + raise_Error("DigitalOutput GPIO="+pinnya+" Release error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + } + + private void raise_Error(String msg) { + if (need_error_event) { + bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_error", false,new Object[]{pinnya,msg}); + } + if (javaevent!=null) { + javaevent.error(pinnya, msg); + } + + } + +} diff --git a/src/androgpio/DigitalOutput/DigitalOutputEvent.java b/src/androgpio/DigitalOutput/DigitalOutputEvent.java new file mode 100644 index 0000000..cd8eef8 --- /dev/null +++ b/src/androgpio/DigitalOutput/DigitalOutputEvent.java @@ -0,0 +1,6 @@ +package androgpio.DigitalOutput; + +public interface DigitalOutputEvent { + void error(int pinnumber, String Msg ); + void newoutstate(int pinnumber, boolean isOn); +} diff --git a/src/androgpio/DigitalOutput/GtcAndroidOutput.java b/src/androgpio/DigitalOutput/GtcAndroidOutput.java new file mode 100644 index 0000000..dd67dfc --- /dev/null +++ b/src/androgpio/DigitalOutput/GtcAndroidOutput.java @@ -0,0 +1,313 @@ +package androgpio.DigitalOutput; + + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; + +@BA.ShortName("GtcAndroidOutput") + +@BA.Events(values = { + "log(msg as String)", +}) + +public class GtcAndroidOutput { + private BA ba; + private String eventName; + private boolean need_log_event = false; + private final String path = "/sys/class/leds"; + private final String brightness = "brightness"; + + + private OutputDevice relay1; + private OutputDevice relay2; + private OutputDevice relay3; + private OutputDevice relay4; + private OutputDevice networkled; + private OutputDevice pilotled; + private OutputDevice actionled; + + private GtcAndroidOutputEvent javaEvent=null; + + @BA.Hide + public GtcAndroidOutput(GtcAndroidOutputEvent event) { + javaEvent = event; + + relay1 = new OutputDevice(path, "relay1",brightness); + relay2 = new OutputDevice(path, "relay2",brightness); + relay3 = new OutputDevice(path, "relay3",brightness); + relay4 = new OutputDevice(path, "relay4",brightness); + networkled = new OutputDevice(path, "networkled",brightness); + pilotled = new OutputDevice(path, "pilotled",brightness); + actionled = new OutputDevice(path, "actionled",brightness); + + } + + /** + * Initializes the GtcAndroidOutput object for B4X + * GtcAndroidOutput is different from Relay and DigitalOutput in Android + * @param eventname Eventname for the object + */ + public void Initialize(BA ba, String eventname) { + this.ba = ba; + this.eventName = eventname.toLowerCase(BA.cul); + if (this.ba!=null) { + if (mycodes.valid_string(eventName)) { + need_log_event = ba.subExists(eventName + "_log"); + } + } + } + + /** + * Check if relay1 exists + * @return true if exists and usable + */ + public boolean hasRelay1() { + return relay1.Exists(); + } + + /** + * Check if relay1 can be read + * @return true if readable + */ + public boolean canreadRelay1() { + return relay1.canRead(); + } + + /** + * Check if relay1 can be written + * + * @return true if writable + */ + public boolean canwriteRelay1() { + return relay1.canWrite(); + } + + /** + * Turn Relay1 on or off + * @param On true to turn on, false to turn off + * @return true if successful + */ + public boolean Relay1(boolean On) { + return relay1.ChangeState(On); + } + + /** + * Check if relay2 exists + * + * @return true if exists and usable + */ + public boolean hasRelay2() { + return relay2.Exists(); + } + + /** + * Check if relay2 can be read + * @return true if readable + */ + public boolean canreadRelay2() { + return relay2.canRead(); + } + + /** + * Check if relay2 can be written + * + * @return true if writable + */ + public boolean canwriteRelay2() { + return relay2.canWrite(); + } + + /** + * Turn Relay2 on or off + * @param On true to turn on, false to turn off + * @return true if successful + */ + public boolean Relay2(boolean On) { + return relay2.ChangeState(On); + } + + /** + * Check if relay3 exists + * + * @return true if exists and usable + */ + public boolean hasRelay3() { + return relay3.Exists(); + } + + /** + * Check if relay3 can be read + * @return true if readable + */ + public boolean canreadRelay3() { + return relay3.canRead(); + } + + /** + * Check if relay3 can be written + * + * @return true if writable + */ + public boolean canwriteRelay3() { + return relay3.canWrite(); + } + + /** + * Turn Relay3 on or off + * @param On true to turn on, false to turn off + * @return true if successful + */ + public boolean Relay3(boolean On) { + return relay3.ChangeState(On); + } + + /** + * Check if relay4 exists + * + * @return true if exists and usable + */ + public boolean hasRelay4() { + return relay4.Exists(); + } + + /** + * Check if relay4 can be read + * @return true if readable + */ + public boolean canreadRelay4() { + return relay4.canRead(); + } + + /** + * Check if relay1 can be written + * + * @return true if writable + */ + public boolean canwriteRelay4() { + return relay4.canWrite(); + } + + /** + * Turn Relay4 on or off + * @param On true to turn on, false to turn off + * @return true if successful + */ + public boolean Relay4(boolean On) { + return relay4.ChangeState(On); + } + + /** + * Check if networkled exists + * + * @return true if exists and usable + */ + public boolean hasNetworkLed() { + return networkled.Exists(); + } + + /** + * Check if networkled can be read + * + * @return true if readable + */ + public boolean canreadNetworkLed() { + return networkled.canRead(); + } + + /** + * Check if networkled can be written + * + * @return true if writable + */ + public boolean canwriteNetworkLed() { + return networkled.canWrite(); + } + + /** + * Turn NetworkLed on or off + * @param On true to turn on, false to turn off + * @return true if successful + */ + public boolean NetworkLed(boolean On) { + return networkled.ChangeState(On); + } + + /** + * Check if pilotled exists + * + * @return true if exists and usable + */ + public boolean hasPilotLed() { + return pilotled.Exists(); + } + + /** + * Check if pilotled can be read + * + * @return true if readable + */ + public boolean canreadPilotLed() { + return pilotled.canRead(); + } + + /** + * Check if pilotled can be written + * + * @return true if writable + */ + public boolean canwritePilotLed() { + return pilotled.canWrite(); + } + + /** + * Turn PilotLed on or off + * @param On true to turn on, false to turn off + * @return true if successful + */ + public boolean PilotLed(boolean On) { + return pilotled.ChangeState(On); + } + + /** + * Check if actionled exists + * + * @return true if exists and usable + */ + public boolean hasActionLed() { + return actionled.Exists(); + } + + /** + * Check if actionled can be read + * + * @return true if readable + */ + public boolean canreadActionLed() { + return actionled.canRead(); + } + + /** + * Check if actionled can be written + * + * @return true if writable + */ + public boolean canwriteActionLed() { + return actionled.canWrite(); + } + + /** + * Turn ActionLed on or off + * + * @param On true to turn on, false to turn off + * @return true if successful + */ + public boolean ActionLed(boolean On) { + return actionled.ChangeState(On); + } + + @SuppressWarnings("unused") + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(GtcAndroidOutput.this, null, 0, eventName+"_log", false, new Object[] {msg}); + if (javaEvent!=null) javaEvent.Log(msg); + } +} diff --git a/src/androgpio/DigitalOutput/GtcAndroidOutputEvent.java b/src/androgpio/DigitalOutput/GtcAndroidOutputEvent.java new file mode 100644 index 0000000..9484bd9 --- /dev/null +++ b/src/androgpio/DigitalOutput/GtcAndroidOutputEvent.java @@ -0,0 +1,12 @@ +package androgpio.DigitalOutput; + +public interface GtcAndroidOutputEvent { + void Log(String msg); + void Relay1State(boolean On); + void Relay2State(boolean On); + void Relay3State(boolean On); + void Relay4State(boolean On); + void NetworkLedState(boolean On); + void PilotLedState(boolean On); + void ActionLedState(boolean On); +} diff --git a/src/androgpio/DigitalOutput/OutputDevice.java b/src/androgpio/DigitalOutput/OutputDevice.java new file mode 100644 index 0000000..6647471 --- /dev/null +++ b/src/androgpio/DigitalOutput/OutputDevice.java @@ -0,0 +1,96 @@ +package androgpio.DigitalOutput; + +import java.io.File; + +import androgpio.mycodes; + +public class OutputDevice { + final String name; + final String path; + final File f; + public OutputDevice(String path, String name, String suffix) { + this.name = name; + this.path = mycodes.path_combine(path, name, suffix); + this.f = new File(this.path); + } + + /** + * Get the name of the device + * + * @return device name + */ + public String getName() { + return name; + } + + /** + * Get the full path of the device + * @return full path + */ + public String getPath() { + return path; + } + + /** + * Check if the file exists + * + * @return + */ + public boolean Exists() { + return mycodes.path_exists("", path); + } + + /** + * Check if the file is readable + * + * @return + */ + public boolean canRead() { + return mycodes.path_readable("", path); + } + + /** + * Check if the file is writable + * @return + */ + public boolean canWrite() { + return mycodes.path_writable("", path); + } + + /** + * Change State of the device + * + * @param state true = on, false = off + * @return true if successful + */ + public boolean ChangeState(boolean state) { + if (Exists() && canWrite()) { + return mycodes.FileWrite(f, state ? "1" : "0"); + } + return false; + } + + /** + * Read State of the device + * + * @return 0 = off, 1 = on, -1 = error + */ + public int ReadState() { + if (Exists() && canRead()) { + String s = mycodes.FileRead(f); + if (s != null) { + try { + int result = Integer.parseInt(s); + if (result == 0) + return 0; + else if (result == 1) + return 1; + } catch (NumberFormatException e) { + + } + + } + } + return -1; + } +} diff --git a/src/androgpio/I2C/I2CException.java b/src/androgpio/I2C/I2CException.java new file mode 100644 index 0000000..0406f30 --- /dev/null +++ b/src/androgpio/I2C/I2CException.java @@ -0,0 +1,49 @@ +package androgpio.I2C; + +import anywheresoftware.b4a.keywords.Bit; + +public class I2CException extends Exception{ + /** + * + */ + private static final long serialVersionUID = -8227969179418888752L; + public final byte bytes[]; + public I2CException(String msg) { + super(msg); + bytes = null; + } + public I2CException(String msg, final byte[] bytes) { + super(msg); + this.bytes = bytes; + } + public I2CException(String msg, byte command) { + super(msg); + this.bytes = new byte[] {command}; + } + + public I2CException(String function, String msg, byte command) { + super("Function="+function+", Message="+msg); + this.bytes = new byte[] {command}; + } + + public I2CException(String function, String msg, byte[] bytes) { + super("Function="+function+", Message="+msg); + this.bytes = bytes; + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append(this.getMessage()); + if (bytes!=null) { + if (bytes.length>0) { + str.append(" [ "); + for(byte xx:bytes) { + str.append(Bit.ToHexString(Bit.And(xx, 0xFF))).append(" "); + } + str.append("]"); + } + } + return str.toString(); + } +} diff --git a/src/androgpio/I2C/I2C_BUS.java b/src/androgpio/I2C/I2C_BUS.java new file mode 100644 index 0000000..7167d2f --- /dev/null +++ b/src/androgpio/I2C/I2C_BUS.java @@ -0,0 +1,84 @@ +package androgpio.I2C; +import anywheresoftware.b4a.BA; + +/** + * Source : https://groups.google.com/forum/#!topic/jna-users/9X0bu4tv7Fk + * @author rdkartono + * + */ +public class I2C_BUS { + private final int O_RDWR = 0x00000002; + + private boolean opened = false; + private String i2c_name = ""; + private int i2c_handle = -1; + private I2C_BUS_Event javaevent; + + /** + * Open I2C Bus using I2C Name + * @param i2c_name : i2c name, example : /dev/i2c-1 + */ + public I2C_BUS(String i2c_name) { + if (!i2c_name.isEmpty())this.i2c_name = i2c_name; + + } + + @BA.Hide + public void SetJavaEvent(I2C_BUS_Event ev) { + javaevent = ev; + } + + /** + * Open Bus + * @return true if opened + */ + public boolean Open_Bus() { + opened = false; + if (!i2c_name.isEmpty()) { + i2c_handle = libc.libC.open(i2c_name, O_RDWR); + if (i2c_handle<0) { + raise_log("Open_Bus failed"); + } + else { + raise_log("Open_Bus success"); + opened = true; + } + } + return opened; + + } + + /** + * Close Bus + */ + public void Close_Bus() { + if (i2c_handle >= 0) { + libc.libC.close(i2c_handle); + i2c_handle = -1; + } + opened = false; + raise_log("Bus Closed"); + } + + /** + * check if I2C bus is opened + * @return true if opened + */ + public boolean IsOpened() { + return opened; + } + + /** + * Get I2C Open handle + * @return -1 if closed + */ + public int I2C_Handle() { + return i2c_handle; + } + + private void raise_log(String msg) { + if (javaevent!=null) { + javaevent.Log(msg); + } + } +} diff --git a/src/androgpio/I2C/I2C_BUS_Event.java b/src/androgpio/I2C/I2C_BUS_Event.java new file mode 100644 index 0000000..5596d2a --- /dev/null +++ b/src/androgpio/I2C/I2C_BUS_Event.java @@ -0,0 +1,5 @@ +package androgpio.I2C; + +public interface I2C_BUS_Event { + void Log(String msg); +} diff --git a/src/androgpio/I2C/I2C_Device.java b/src/androgpio/I2C/I2C_Device.java new file mode 100644 index 0000000..59da88a --- /dev/null +++ b/src/androgpio/I2C/I2C_Device.java @@ -0,0 +1,271 @@ +package androgpio.I2C; +import anywheresoftware.b4a.BA; + +public class I2C_Device { + + private boolean opened = false; + + private int bus_handle = -1; + private int dev_handle = -1; + private int dev_address = -1; + private I2C_Device_Event javaevent; + + /** + * Create a I2C Device (Slave) + * @param bushandle : bus handle , get it from I2C_BUS object + * @param address : slave address to control + */ + public I2C_Device(int bushandle, int address) { + opened = false; + if (bushandle < 0) return; + if (address<0) return; + this.bus_handle = bushandle; + this.dev_address = address; + } + + @BA.Hide + public void SetJavaEvent(I2C_Device_Event ev) { + javaevent = ev; + } + + /** + * Open this Device, in I2C_SLAVE mode + * @return true if opened + */ + public boolean OpenDevice_SLAVE() { + opened = false; + if (bus_handle>0) { + if (dev_address>0) { + dev_handle = libc.libC.ioctl(bus_handle, libc.I2C_SLAVE, dev_address); + if (dev_handle<0) { + // gagal + raise_log("OpenDevice_SLAVE failed, dev_handle="+dev_handle+", dev_address="+dev_address); + } else { + raise_log("OpenDevice_SLAVE success"); + opened = true; + } + } else raise_log("OpenDevice_SLAVE failed, dev_address="+dev_address); + } else raise_log("OpenDevice_SLAVE failed, bus_handle="+bus_handle); + return opened; + } + + /** + * Open this Device, in I2C_SLAVE_FORCE mode. + * @return true if opened + */ + public boolean OpenDevice_SLAVEFORCE() { + opened = false; + if (bus_handle<0) return false; + if (dev_address<0) return false; + dev_handle = libc.libC.ioctl(bus_handle, libc.I2C_SLAVE_FORCE, dev_address); + + if (dev_handle<0) { + // gagal + raise_log("OpenDevice_SLAVEFORCE failed"); + } + else { + libc.libC.ioctl(bus_handle, libc.I2C_RETRIES, 3); + libc.libC.ioctl(bus_handle, libc.I2C_TIMEOUT, 3); + raise_log("OpenDevice_SLAVEFORCE success"); + opened = true; + } + return opened; + } + + /** + * Open this Device, in I2C_RDWR mode + * @return true if opened + */ + public boolean OpenDevice_RDWR() { + opened = false; + if (bus_handle<0) return false; + if (dev_address<0) return false; + dev_handle = libc.libC.ioctl(bus_handle, libc.I2C_RDWR, dev_address); + if (dev_handle<0) { + raise_log("OpenDevice_RDWR failed"); + } + else { + raise_log("OpenDevice_RDWR success"); + opened = true; + } + return opened; + } + + /** + * Open this Device, in I2C_TENBIT + * @return true if opened + */ + public boolean OpenDevice_SMBUS() { + opened = false; + if (bus_handle<0) return false; + if (dev_address<0) return false; + dev_handle = libc.libC.ioctl(bus_handle, libc.I2C_SMBUS, dev_address); + if (dev_handle<0) { + raise_log("OpenDevice_SMBUS failed"); + } + else { + raise_log("OpenDevice_SMBUS success"); + opened = true; + } + return opened; + } + + + /** + * Close this Device + */ + public void CloseDevice() { + opened = false; + bus_handle = -1; + dev_address = -1; + if (dev_handle>=0) { + int closeresult = libc.libC.close(dev_handle); + dev_handle = -1; + if (closeresult==0) + raise_log("Device Closed"); + else + raise_log("Failure in I2C_Device CloseDevice"); + } + + } + + /** + * Check if slave is opened + * @return true if opened + */ + public boolean IsOpened() { + return opened; + } + + /** + * Get associated Bus handle + * @return -1 if closed + */ + public int BusHandle() { + return this.bus_handle; + } + + /** + * Get associated Slave address + * @return -1 if closed + */ + public int SlaveAddress() { + return this.dev_address; + } + + /** + * Get associated Device Open Handle + * @return -1 if closed + */ + public int DeviceHandle() { + return this.dev_handle; + } + + /** + * Write bytes + * @param bb : data bytes + * @return 0 if write fail + */ + public int WriteBytes(byte[] bb) { + if (bus_handle<0) { + raise_log("WriteBytes failed, Bus Handle closed"); + return 0; + } + if (dev_handle<0) { + raise_log("WriteBytes failed, Device Handle closed"); + return 0; + } + if (dev_address<0) { + raise_log("WriteBytes failed, Device Address invalid"); + return 0; + } + + return libc.libC.write(bus_handle, bb, bb.length); + } + + public int WriteBytes(int register_address, byte bb) { + byte[] cmd = new byte[2]; + cmd[0] = (byte) register_address; + cmd[1] = bb; + return WriteBytes(cmd); + } + + public int WriteBytes(int register_address, byte[] bb ) { + if (bb!=null) { + if (bb.length>0) { + byte[] cmd = new byte[bb.length+1]; + cmd[0] = (byte) register_address; + for(int ii=0;ii=0) { + if (bb.length > (offset+size)) { + byte[] newbyte = new byte[size+1]; + newbyte[0] = (byte) register_address; + for(int ii=0;ii=0) { + if (bb.length > (offset + size)) { + + byte[] newbyte = new byte[size]; + for(int ii=0;ii getFieldOrder() { + return Arrays.asList(// + "tv_sec",// + "tv_usec"// + ); + } + @BA.Hide public void autoWrite(){} + @BA.Hide public void autoRead(){} + + public timeval(long second, long usecond) { + tv_sec = new NativeLong(second); + tv_usec = new NativeLong(usecond); + } + } + } + +} + + + + + diff --git a/src/androgpio/ModbusWrapper/jModbusCoil.java b/src/androgpio/ModbusWrapper/jModbusCoil.java new file mode 100644 index 0000000..a366f0e --- /dev/null +++ b/src/androgpio/ModbusWrapper/jModbusCoil.java @@ -0,0 +1,22 @@ +package androgpio.ModbusWrapper; + +import com.intelligt.modbus.jlibmodbus.data.ModbusCoils; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("ModbusCoils") +/*** + * ModbusCoil dataholder + * by default, create 1024 coils + * @author rdkartono + * + */ +public class jModbusCoil extends ModbusCoils { + public jModbusCoil() { + super(1024); + } + public jModbusCoil(int size) { + super(size); + } + +} diff --git a/src/androgpio/ModbusWrapper/jModbusHoldingRegister.java b/src/androgpio/ModbusWrapper/jModbusHoldingRegister.java new file mode 100644 index 0000000..4fd517c --- /dev/null +++ b/src/androgpio/ModbusWrapper/jModbusHoldingRegister.java @@ -0,0 +1,22 @@ +package androgpio.ModbusWrapper; + +import com.intelligt.modbus.jlibmodbus.data.ModbusHoldingRegisters; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("ModbusHoldingRegisters") +/** + * ModbusHoldingRegister dataholder + * by default create 1024 registers + * @author rdkartono + * + */ +public class jModbusHoldingRegister extends ModbusHoldingRegisters { + public jModbusHoldingRegister(){ + super(1024); + } + + public jModbusHoldingRegister(int size){ + super(size); + } +} diff --git a/src/androgpio/ModbusWrapper/jModbusMaster.java b/src/androgpio/ModbusWrapper/jModbusMaster.java new file mode 100644 index 0000000..bdf0fe8 --- /dev/null +++ b/src/androgpio/ModbusWrapper/jModbusMaster.java @@ -0,0 +1,447 @@ +package androgpio.ModbusWrapper; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import com.fazecast.jSerialComm.SerialPort; +import com.intelligt.modbus.jlibmodbus.Modbus; +import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException; +import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException; +import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException; +import com.intelligt.modbus.jlibmodbus.master.ModbusMaster; +import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory; +import com.intelligt.modbus.jlibmodbus.serial.SerialParameters; +import com.intelligt.modbus.jlibmodbus.serial.SerialUtils; +import com.intelligt.modbus.jlibmodbus.serial.SerialPort.BaudRate; +import com.intelligt.modbus.jlibmodbus.serial.SerialPort.Parity; +import com.intelligt.modbus.jlibmodbus.serial.SerialPortException; +import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters; +import com.intelligt.modbus.jlibmodbus.utils.FrameEvent; +import com.intelligt.modbus.jlibmodbus.utils.FrameEventListener; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.objects.collections.List; + +@BA.ShortName("jModbusMaster") +@BA.Events(values= { + "log(msg as string)", + "datasent(bb() as byte)", + "datareceived(bb() as byte)" +}) +@BA.DependsOn(values = { "jSerialComm-2.9.2","jlibmodbus-1.2.9.7" }) + +/** + * Create Modbus Master + * in Modbus, Master is actually a client that request data from server (Slave) + * @author rdkartono + * + */ +public class jModbusMaster { + private Object caller; + private String event; + private BA ba; + private final Object Me = this; + + private boolean need_log_event = false; + private boolean need_datasent_event = false; + private boolean need_datareceived_event = false; + + private ModbusMaster modbus; + + /** + * Initialize ModbusSerialSlaveRTU + * @param callerobject : caller object + * @param eventname : event name + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + this.ba = ba; + event = eventname.trim(); + caller = callerobject; + + if (this.ba!=null) { + if (caller!=null) { + if (!event.isEmpty()) { + need_log_event = this.ba.subExists(event+"_log"); + need_datasent_event = this.ba.subExists(event+"_datasent"); + need_datareceived_event = this.ba.subExists(event+"_datareceived"); + } + } + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + } + + /** + * Get available Serial Port Names + * @return List of port names. If exists, List size > 0 + */ + public List GetSerialPortNames() { + List result = new List(); + result.Initialize(); + + + SerialPort[] sp = SerialPort.getCommPorts(); + if (sp!=null) { + if (sp.length>0) { + for(SerialPort xx : sp) { + result.Add(xx.getSystemPortName()); + } + } + } + return result; + } + + /** + * Open Modbus Master in Serial ASCII format + * @param serialname : serial port name + * @param baudrate : baud rate, typically 4800, 9600, 144400, 19200, 38400, 57600, 115200 + * @param databit : data bit length , typically 8 + * @param stopbit : typically 1 + * @param parity : 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + * @return true if success + */ + public boolean OpenSerial_ASCII(String serialname, int baudrate, int databit, int stopbit, int parity) { + SerialParameters sp = new SerialParameters(); + + sp.setDataBits(databit); + sp.setDevice(serialname); + sp.setStopBits(stopbit); + sp.setBaudRate(BaudRate.getBaudRate(baudrate)); + sp.setParity(Parity.getParity(parity)); + + SerialUtils.setSerialPortFactoryJSerialComm(); + + try { + modbus = ModbusMasterFactory.createModbusMasterASCII(sp); + modbus.addListener(listener); + modbus.connect(); + return true; + } catch (SerialPortException e) { + raise_log("OpenSerial_ASCII failed, Msg : "+e.getMessage()); + return false; + } catch (ModbusIOException e) { + raise_log("Modbus Connect failed, Msg : "+e.getMessage()); + return false; + } + + } + + /** + * Open Modbus Master in Serial RTU format + * @param serialname : serial port name + * @param baudrate : baud rate, typically 4800, 9600, 144400, 19200, 38400, 57600, 115200 + * @param databit : data bit length , typically 8 + * @param stopbit : typically 1 + * @param parity : 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + * @return true if success + */ + public boolean OpenSerial_RTU(String serialname, int baudrate, int databit, int stopbit, int parity) { + SerialParameters sp = new SerialParameters(); + + sp.setDataBits(databit); + sp.setDevice(serialname); + sp.setStopBits(stopbit); + sp.setBaudRate(BaudRate.getBaudRate(baudrate)); + sp.setParity(Parity.getParity(parity)); + + SerialUtils.setSerialPortFactoryJSerialComm(); + + try { + modbus = ModbusMasterFactory.createModbusMasterRTU(sp); + modbus.addListener(listener); + modbus.connect(); + return true; + } catch (SerialPortException e) { + raise_log("OpenSerial_RTU failed, Msg : "+e.getMessage()); + return false; + } catch (ModbusIOException e) { + raise_log("Modbus Connect failed, Msg : "+e.getMessage()); + return false; + } + + } + + /** + * Open Modbus Master in TCP + * @param hostaddress : target slave IP address + * @param port : tcp port. if negative, will use default 502 + * @param enable_keepalive : set true to enable keep alive connection + * @return true if success + */ + public boolean OpenTCP(String hostaddress, int port, boolean enable_keepalive) { + InetAddress host; + try { + host = InetAddress.getByName(hostaddress); + } catch (UnknownHostException e) { + raise_log("OpenTCP failed, Invalid hostaddress="+hostaddress+", Msg : "+e.getMessage()); + return false; + } + + if (port<1) port = Modbus.TCP_PORT; + TcpParameters tp = new TcpParameters(); + tp.setPort(port); + tp.setHost(host); + tp.setKeepAlive(enable_keepalive); + + modbus = ModbusMasterFactory.createModbusMasterTCP(tp); + modbus.addListener(listener); + try { + modbus.connect(); + return true; + } catch (ModbusIOException e) { + raise_log("Modbus Connect failed, Msg : "+e.getMessage()); + return false; + } + } + + /** + * Close Modbus Master connection + * @return true if success + */ + public boolean Close() { + if (modbus instanceof ModbusMaster) { + modbus.removeListeners(); + try { + modbus.disconnect(); + } catch (ModbusIOException e) { + raise_log("Modbus Disconnect failed, Msg : "+e.getMessage()); + return false; + } + } + return true; + } + + /*** + * Write Single Coil (command = 5) + * @param slave_address : slave address + * @param coil_address : coil address + * @param is_on : true = 1, false = 0 + * @return true if success + */ + public boolean WriteSingleCoil(int slave_address, int coil_address, boolean is_on) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + modbus.writeSingleCoil(slave_address, coil_address, is_on); + return true; + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("WriteSingleCoil failed, Msg : "+e.getMessage()); + + } + } + } + return false; + } + + /** + * Write Single Register (command = 6) + * @param slave_address : slave address + * @param register_address : register address + * @param value : register value , 16 bit + * @return true if success + */ + public boolean WriteSingleRegister(int slave_address, int register_address, int value) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + modbus.writeSingleRegister(slave_address, register_address, value); + return true; + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("WriteSingleRegister failed, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Write multiple coils (command = 15) + * @param slave_address : slave address + * @param start_address : start address of coils + * @param values : array of boolean for coils, true = 1, false = 0 + * @return true if success + */ + public boolean WriteMultipleCoils(int slave_address, int start_address, boolean[] values) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + modbus.writeMultipleCoils(slave_address, start_address, values); + return true; + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("WriteMultipleCoils failed, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Write Multiple Register (command = 16) + * @param slave_address : slave address + * @param start_address : start address of register + * @param values : array of int (16 bit) for registers + * @return true if success + */ + public boolean WriteMultipleRegisters(int slave_address, int start_address, int[] values) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + modbus.writeMultipleRegisters(slave_address, start_address, values); + return true; + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("WriteMultipleRegisters failed, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Read Single coil (command = 1) + * @param slave_address : slave address + * @param coil_address : coil address + * @return -1 = failed, 0 = false, 1 = true + */ + public int ReadSingleCoil(int slave_address, int coil_address) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + boolean[] result = modbus.readCoils(slave_address, coil_address, 1); + if (result!=null) { + if (result.length>0) { + if (result[0]) return 1; else return 0; + } + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("ReadSingleCoil failed, Msg : "+e.getMessage()); + } + } + } + return -1; + } + + /** + * Read single Register (16 bit) (command = 3) + * @param slave_address : slave address + * @param register_address : register address + * @return -1 = failed, other = register value + */ + public int ReadSingleRegister(int slave_address, int register_address) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + int[] result = modbus.readHoldingRegisters(slave_address, register_address, 1); + if (result!=null) { + if (result.length>0) { + return Bit.And(result[0], 0xFFFF); + } + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("ReadSingleRegister failed, Msg : "+e.getMessage()); + } + } + } + return -1; + } + + /** + * Read multiple coils + * @param slave_address : slave address + * @param start_address : coil start address + * @param quantity : how many coils want to be read + * @return array of boolean, or null if failed. + */ + public boolean[] ReadMultipleCoils(int slave_address, int start_address, int quantity) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + boolean[] result = modbus.readCoils(slave_address, start_address, quantity); + if (result!=null) { + if (result.length>0) { + return result; + } + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("ReadMultipleCoils failed, Msg : "+e.getMessage()); + } + + } + } + return null; + } + + /** + * Read multiple registers + * @param slave_address : slave address + * @param start_address : register start address + * @param quantity : how many registers want to be read + * @return array of int, or null if failed + */ + public int[] ReadMultipleRegisters(int slave_address, int start_address, int quantity) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + int[] result = modbus.readHoldingRegisters(slave_address, start_address, quantity); + if (result!=null) { + if (result.length>0) { + return result; + } + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("ReadMultipleRegisters failed, Msg : "+e.getMessage()); + } + } + } + return null; + } + + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_datasent(byte[] value) { + if (need_datasent_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_datasent", false, new Object[] {value}); + } + + private void raise_datareceived(byte[] value) { + if (need_datareceived_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_datareceived", false, new Object[] {value}); + } + + private String printbytes(byte[] bb) { + if (bb!=null) { + if (bb.length>0) { + StringBuilder str = new StringBuilder(); + for(byte xx: bb) { + str.append(Bit.ToHexString(Bit.And(xx, 0xFF))).append(" "); + } + return str.toString(); + } + } + return ""; + } + + FrameEventListener listener = new FrameEventListener() { + + @Override + public void frameSentEvent(FrameEvent event) { + + raise_log("Data Sent : "+printbytes(event.getBytes())); + raise_datasent(event.getBytes()); + } + + @Override + public void frameReceivedEvent(FrameEvent event) { + raise_log("Data Received : "+printbytes(event.getBytes())); + raise_datareceived(event.getBytes()); + } + + + }; +} diff --git a/src/androgpio/ModbusWrapper/jModbusSlave.java b/src/androgpio/ModbusWrapper/jModbusSlave.java new file mode 100644 index 0000000..c236656 --- /dev/null +++ b/src/androgpio/ModbusWrapper/jModbusSlave.java @@ -0,0 +1,345 @@ +package androgpio.ModbusWrapper; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import com.fazecast.jSerialComm.SerialPort; +import com.intelligt.modbus.jlibmodbus.Modbus; +import com.intelligt.modbus.jlibmodbus.data.DataHolder; +import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException; +import com.intelligt.modbus.jlibmodbus.serial.SerialParameters; +import com.intelligt.modbus.jlibmodbus.serial.SerialPort.BaudRate; +import com.intelligt.modbus.jlibmodbus.serial.SerialPort.Parity; +import com.intelligt.modbus.jlibmodbus.serial.SerialPortException; +import com.intelligt.modbus.jlibmodbus.serial.SerialUtils; +import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave; +import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory; +import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters; +import com.intelligt.modbus.jlibmodbus.utils.FrameEvent; +import com.intelligt.modbus.jlibmodbus.utils.FrameEventListener; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.objects.collections.List; + +@BA.ShortName("jModbusSlave") +@BA.Events(values= { + "log(msg as string)", + "datasent(bb() as byte)", + "datareceived(bb() as byte)" +}) +@BA.DependsOn(values = { "jSerialComm-2.9.2","jlibmodbus-1.2.9.7" }) + +/** + * Create Modbus Slave + * in Modbus, Slave is actually a server that hold data. + * Master is a client, that request data from server. + * @author rdkartono + * + */ +public class jModbusSlave { + + + private Object caller; + private String event; + private BA ba; + private final Object Me = this; + + private boolean need_log_event = false; + private boolean need_datasent_event = false; + private boolean need_datareceived_event = false; + + + private ModbusSlave modbus = null; + private DataHolder dh = null; + private jModbusCoil coil = null; + private jModbusHoldingRegister reg = null; + + /** + * Initialize ModbusSerialSlaveRTU + * @param callerobject : caller object + * @param eventname : event name + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + this.ba = ba; + event = eventname.trim(); + caller = callerobject; + dh = new DataHolder(); + if (this.ba!=null) { + if (caller!=null) { + if (!event.isEmpty()) { + need_log_event = this.ba.subExists(event+"_log"); + need_datasent_event = this.ba.subExists(event+"_datasent"); + need_datareceived_event = this.ba.subExists(event+"_datareceived"); + } + } + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + } + + /** + * Get available Serial Port Names + * @return List of port names. If exists, List size > 0 + */ + public List GetSerialPortNames() { + List result = new List(); + result.Initialize(); + + + SerialPort[] sp = SerialPort.getCommPorts(); + if (sp!=null) { + if (sp.length>0) { + for(SerialPort xx : sp) { + result.Add(xx.getSystemPortName()); + } + } + } + return result; + } + + /** + * Open Modbus Slave in Serial ASCII format + * @param serialname : serial port name + * @param baudrate : baud rate, typically 4800, 9600, 144400, 19200, 38400, 57600, 115200 + * @param databit : data bit length , typically 8 + * @param stopbit : typically 1 + * @param parity : 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + * @return true if success + */ + public boolean OpenSerial_ASCII(String serialname, int baudrate, int databit, int stopbit, int parity) { + SerialParameters sp = new SerialParameters(); + + sp.setDataBits(databit); + sp.setDevice(serialname); + sp.setStopBits(stopbit); + sp.setBaudRate(BaudRate.getBaudRate(baudrate)); + sp.setParity(Parity.getParity(parity)); + + SerialUtils.setSerialPortFactoryJSerialComm(); + + try { + modbus = ModbusSlaveFactory.createModbusSlaveASCII(sp); + modbus.addListener(listener); + if (dh==null) dh = new DataHolder(); + modbus.setDataHolder(dh); + modbus.listen(); + return true; + } catch(SerialPortException e) { + raise_log("OpenSerial_ASCII failed, Msg : "+e.getMessage()); + return false; + } catch (ModbusIOException e) { + raise_log("Modbus Listen failed, Msg : "+e.getMessage()); + return false; + } + } + + /** + * Open Modbus Slave in Serial RTU format + * @param serialname : serial port name + * @param baudrate : baud rate, typically 4800, 9600, 144400, 19200, 38400, 57600, 115200 + * @param databit : data bit length , typically 8 + * @param stopbit : typically 1 + * @param parity : 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + * @return true if success + */ + public boolean OpenSerial_RTU(String serialname, int baudrate, int databit, int stopbit, int parity) { + + SerialParameters sp = new SerialParameters(); + + sp.setDataBits(databit); + sp.setDevice(serialname); + sp.setStopBits(stopbit); + sp.setBaudRate(BaudRate.getBaudRate(baudrate)); + sp.setParity(Parity.getParity(parity)); + + SerialUtils.setSerialPortFactoryJSerialComm(); + + try { + + modbus = ModbusSlaveFactory.createModbusSlaveRTU(sp); + + modbus.addListener(listener); + if (dh==null) dh = new DataHolder(); + modbus.setDataHolder(dh); + modbus.listen(); + return true; + } catch (SerialPortException e) { + raise_log("OpenSerial_RTU failed, Msg : "+e.getMessage()); + return false; + } catch (ModbusIOException e) { + raise_log("Modbus Listen failed, Msg : "+e.getMessage()); + return false; + } + } + + /** + * Open Modbus Slave in TCP + * @param hostaddress : network adapter IP address, or empty string for default ip address + * @param port : port number, if negative number, will use default TCP port 502 + * @param enable_keepalive : set true to enable keep alive connection + * @return true if success + */ + public boolean OpenTCP(String hostaddress, int port, boolean enable_keepalive) { + InetAddress host; + try { + if (hostaddress.isEmpty()) { + host = InetAddress.getLocalHost(); + } else { + host = InetAddress.getByName(hostaddress); + } + } catch (UnknownHostException | SecurityException e) { + raise_log("Unable to get InetAddress for host="+hostaddress+", Msg : "+e.getMessage()); + return false; + } + + if (port<1) port = Modbus.TCP_PORT; + + TcpParameters tp = new TcpParameters(); + tp.setPort(port); + tp.setHost(host); + tp.setKeepAlive(enable_keepalive); + + modbus = ModbusSlaveFactory.createModbusSlaveTCP(tp); + if (dh==null) dh = new DataHolder(); + + modbus.setDataHolder(dh); + modbus.addListener(listener); + try { + modbus.listen(); + } catch (ModbusIOException e) { + raise_log("Unable to start Modbus Listen, Msg : "+e.getMessage()); + return false; + } + return true; + } + + /** + * Get / Set Slave ID. + * On getting, when returning -1, means modbus not yet opened + */ + public void setID(int value) { + if (modbus!=null) { + modbus.setServerAddress(value); + } + } + + public int getID() { + if (modbus!=null) { + return modbus.getServerAddress(); + } else return -1; + } + + /** + * Initialize coils + * coil is binary (true/false) value, 1 bit + * @param size : how many coils want to initialize. If < 1, default to 1024 + */ + public void Initialize_Coils(int size) { + if (size>0) + coil = new jModbusCoil(size); + else + coil = new jModbusCoil(); + + if (dh==null) dh = new DataHolder(); + dh.setCoils(coil); + } + + /** + * Get Coils that already been initialized + * @return null if failed + */ + public jModbusCoil getCoils() { + return coil; + } + + /** + * Get Registers that already been initialized + * @return null if failed + */ + public jModbusHoldingRegister getRegisters() { + return reg; + } + + /** + * Initialize Register + * Register is 16 bit value, can read and write + * @param size : how many registers want to initialize. If < 1, default to 1024 + */ + public void Initialize_Registers(int size) { + if (size>0) + reg = new jModbusHoldingRegister(size); + else + reg = new jModbusHoldingRegister(); + + if (dh==null) dh = new DataHolder(); + dh.setHoldingRegisters(reg); + } + + /** + * Close Modbus Serial Slave RTU + * @return true if success + */ + public boolean Close() { + if (modbus!=null) { + try { + modbus.removeListeners(); + modbus.shutdown(); + } catch (ModbusIOException e) { + raise_log("Modbus Close failed, Msg : "+e.getMessage()); + return false; + } + } + return true; + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_datasent(byte[] value) { + if (need_datasent_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_datasent", false, new Object[] {value}); + } + + private void raise_datareceived(byte[] value) { + if (need_datareceived_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_datareceived", false, new Object[] {value}); + } + + private String printbytes(byte[] bb) { + if (bb!=null) { + if (bb.length>0) { + StringBuilder str = new StringBuilder(); + for(byte xx: bb) { + str.append(Bit.ToHexString(Bit.And(xx, 0xFF))).append(" "); + } + return str.toString(); + } + } + return ""; + } + + FrameEventListener listener = new FrameEventListener() { + + @Override + public void frameSentEvent(FrameEvent event) { + + raise_log("Data Sent : "+printbytes(event.getBytes())); + raise_datasent(event.getBytes()); + } + + @Override + public void frameReceivedEvent(FrameEvent event) { + raise_log("Data Received : "+printbytes(event.getBytes())); + raise_datareceived(event.getBytes()); + } + + + }; + + +} diff --git a/src/androgpio/ScreenRotater.java b/src/androgpio/ScreenRotater.java new file mode 100644 index 0000000..6979b4c --- /dev/null +++ b/src/androgpio/ScreenRotater.java @@ -0,0 +1,145 @@ +package androgpio; + +import android.content.ContentResolver; +import android.content.Intent; +import android.net.Uri; +import android.provider.Settings; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.IOnActivityResult; + +@BA.ShortName("ScreenRotater") +@BA.Permissions(values= { + "android.permission.WRITE_SETTINGS" +}) +@BA.Events(values= { + "log(msg as string)", + "waitpermission(result as int)" +}) +public class ScreenRotater { + private BA ba; + private String event; + private Object Me; + private boolean need_log_event = false; + private boolean need_waitpermission_event = false; + + + /** + * Initialize Screen Rotater + * @param eventname Eventname + */ + + public void Initialize(BA ba, String eventname) { + this.ba = ba; + this.event = eventname; + Me = this; + check_events(); + + } + + @SuppressWarnings("static-access") + private String getPackageName() { + return ba.packageName; + } + + @SuppressWarnings("static-access") + private ContentResolver getContentResolver() { + return ba.applicationContext.getContentResolver(); + } + + public boolean PermissionAllowed() { + + return Settings.System.canWrite(ba.context); + } + + public void RequestPermission() { + Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); + + intent.setData(Uri.parse("package:" + getPackageName())); + ba.startActivityForResult(new IOnActivityResult() { + + @Override + public void ResultArrived(int resultCode, Intent intent) { + raise_log("Result code = "+resultCode); + raise_waitpermission(resultCode); + } + + }, intent); + + } + + /** + * Get Rotation value + * @return -1 if not defined, 0 / 90 / 180 / 270 if defined + */ + public int GetRotation() { + int value = android.provider.Settings.System.getInt(getContentResolver(), android.provider.Settings.System.USER_ROTATION, -1); + switch(value) { + case 0 : + raise_log("GetRotation user_rotation=0"); + return 0; + case 1 : + raise_log("GetRotation user_rotation=1"); + return 90; + case 2 : + raise_log("GetRotation user_rotation=2"); + return 180; + case 3 : + raise_log("GetRotation user_rotation=3"); + return 270; + default : + raise_log("GetRotation user_rotation undefined"); + return -1; + } + } + + /** + * Set Screen Rotation + * @param value 0 / 90 / 180 / 270 + * @return true if valid + */ + public boolean SetRotation(int value) { + // disable accelerator rotation + android.provider.Settings.System.putInt(getContentResolver(), android.provider.Settings.System.ACCELEROMETER_ROTATION, 0); + + switch(value) { + case 0 : + android.provider.Settings.System.putInt(getContentResolver(), android.provider.Settings.System.USER_ROTATION, 0); + raise_log("SetRotation user_rotation=0"); + return true; + case 90 : + android.provider.Settings.System.putInt(getContentResolver(), android.provider.Settings.System.USER_ROTATION, 1); + raise_log("SetRotation user_rotation=1"); + return true; + case 180 : + android.provider.Settings.System.putInt(getContentResolver(), android.provider.Settings.System.USER_ROTATION, 2); + raise_log("SetRotation user_rotation=2"); + return true; + case 270 : + android.provider.Settings.System.putInt(getContentResolver(), android.provider.Settings.System.USER_ROTATION, 3); + raise_log("SetRotation user_rotation=3"); + return true; + default : + raise_log("rotate failed, invalid value="+value); + return false; + } + } + + private void check_events() { + if (ba!=null) { + if (event!=null) { + if (event.length()>0) { + need_log_event = ba.subExists(event+"_log"); + need_waitpermission_event =ba.subExists(event+"_waitpermission"); + } + } + } + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_waitpermission(int result) { + if (need_waitpermission_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_waitpermission", false, new Object[] {result}); + } +} diff --git a/src/androgpio/SerialPort/SerialComm.java b/src/androgpio/SerialPort/SerialComm.java new file mode 100644 index 0000000..0f0ebf2 --- /dev/null +++ b/src/androgpio/SerialPort/SerialComm.java @@ -0,0 +1,505 @@ +package androgpio.SerialPort; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; + +import com.fazecast.jSerialComm.*; + + +@BA.ShortName("SerialComm") +@BA.Events(values= { + "log(msg as string)", + "newdata(bb() as byte)" + +}) +@BA.DependsOn(values = { "jSerialComm-2.9.2" }) + +public class SerialComm { + + private BA ba; + private Object myobject; + private String event; + private boolean need_log_event = false; + private boolean need_newdata_event = false; + + + private jSerialPort_Event javaevent; + private boolean isReady = false; + private String version=""; + private long writtencount = 0; + private long readcount = 0; + + private SerialPort myport; + + private String myportname; + + + /** + * Initialize jSerialPort (B4J / B4A) + * @param caller : caller object + * @param eventname : event name + */ + public void Initialize(BA bax, Object caller, String eventname) { + isReady = false; + ba = bax; + event = eventname; + myobject = caller; + need_log_event = ba.subExists(event+"_log"); + need_newdata_event = ba.subExists(event+"_newdata"); + + version = SerialPort.getVersion(); + if (version!="") isReady = true; + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + ClosePort(); + } + }); + } + + /** + * Check Version String of SerialPort Library + * @return version string + */ + public String GetVersion() { + return version; + } + + /** + * Get all available port names in this system + * @return List of Serial Port Names + */ + public List GetPortNames(){ + List result = new List(); + result.Initialize(); + if (isReady) { + for(SerialPort xx:SerialPort.getCommPorts()) { + if (xx != null) { + + String portname = xx.getSystemPortName(); + if (portname!="") { + result.Add(portname); + } + } + } + } else { + raise_log("GetPortNames failed, SerialPort Library not ready"); + } + return result; + } + + /** + * Check if SerialComm is Ready to work + * @return true if ready + */ + public boolean PortIsReady() { + if (myport!=null) { + if (myport instanceof SerialPort) { + return myport.isOpen(); + } + } + return false; + + } + + /** + * Open Serial Port, with default value 1000 bytes RX buffer, and 1000 bytes TX Buffer + * @param portname : portname (a member of GetPortNames) + * @return true if port opened + */ + public boolean OpenPort(String portname) { + myportname=""; + writtencount = 0; + readcount = 0; + if (isReady) { + myport = SerialPort.getCommPort(portname); + if (myport instanceof SerialPort) { + if (myport.openPort()) { + + if (myport.addDataListener(new SerialPortDataListener() { + + @Override + public int getListeningEvents() { + return SerialPort.LISTENING_EVENT_DATA_AVAILABLE; + } + + @Override + public void serialEvent(SerialPortEvent event) { + if (event instanceof SerialPortEvent) { + if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) { + int available = myport.bytesAvailable(); + if (available>0) { + byte[] bb = new byte[available]; + int readbytes = myport.readBytes(bb, available); + + raise_newdata(bb); + readcount+= readbytes; + } + } + } + } + + })) { + + } else { + raise_log("Failed to add DataListener to SerialComm"); + } + + + myportname = portname; + raise_log("SerialPort name="+myportname+" is opened"); + return true; + } + } + + } + raise_log("Failed to open SerialPort name="+portname); + return false; + } + + @BA.Hide + /** + * Read serial port + * @param count : maximum bytes wanted to read + * @return array of bytes, or null if failed + */ + public byte[] Read(int count) { + if (isReady) { + if (myport instanceof SerialPort) { + if (myport.isOpen()) { + int available = myport.bytesAvailable(); + if (available>0) { + byte[] bb = new byte[count]; + myport.readBytes(bb, bb.length); + + readcount+= bb.length; + return bb; + } + } + } + } + return null; + } + + /** + * Close Serial Port + */ + public void ClosePort() { + if (isReady) { + if (myport!=null) { + if (myport instanceof SerialPort) { + try { + myport.removeDataListener(); + if (myport.isOpen()) myport.closePort(); + } catch(Exception e) { + raise_log("ClosePort exception, Msg : "+e.getMessage()); + } + } + + myport = null; + raise_log("SerialPort name="+myportname+" is closed"); + } + } + isReady = false; + myportname = ""; + + } + + /** + * Get current used Port Name + * @return port name, or empty string if closed + */ + public String CurrentPortName() { + return myportname; + } + + /** + * Get Baud Rate + * @return -1 if failed + */ + public int GetBaudRate() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.getBaudRate(); + } + } + return -1; + } + + /** + * Set Baud Rate + * @param value : baud rate value + * @return true if success + */ + public boolean SetBaudRate(int value) { + if (isReady) { + if (myport instanceof SerialPort) { + myport.setBaudRate(value); + return true; + } + } + return false; + } + + /** + * Get Data bits + * @return -1 if failed + */ + public int GetDataBits() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.getNumDataBits(); + } + } + + return -1; + } + + /** + * Set Data bits + * @param value : data bits value + * @return true if success + */ + public boolean SetDataBits(int value) { + if (isReady) { + if (myport instanceof SerialPort) { + myport.setNumDataBits(value); + return true; + } + } + return false; + } + + /** + * Get / Set Stop bit + * @return -1 = failed, 1 = 1 stop bit, 2 = 2 stop bit, 3 = 1.5 stop bit + */ + public int GetStopBits() { + if (isReady) { + if (myport instanceof SerialPort) { + int value = myport.getNumStopBits(); + switch(value) { + case SerialPort.ONE_STOP_BIT : + return 1; + case SerialPort.ONE_POINT_FIVE_STOP_BITS : + return 3; + case SerialPort.TWO_STOP_BITS : + return 2; + } + } + } + return -1; + } + + /** + * Set Stop bit + * @param stopbitcode : 1 = 1 stop bit, 2 = 2 stop bit, 3 = 1.5 stop bit, other = 1 stop bit + * @return true if success + */ + public boolean SetStopBits(int stopbitcode) { + if (isReady) { + if (myport instanceof SerialPort) { + switch(stopbitcode) { + case 3 : + myport.setNumStopBits(SerialPort.ONE_POINT_FIVE_STOP_BITS); + break; + case 2 : + myport.setNumStopBits(SerialPort.TWO_STOP_BITS); + break; + default : + myport.setNumStopBits(SerialPort.ONE_STOP_BIT); + break; + + } + return true; + } + } + return false; + } + + /** + * Get Parity Code + * @return -1 if failed, 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + */ + public int GetParityBits() { + if (isReady) { + if (myport instanceof SerialPort) { + int value = myport.getParity(); + switch(value) { + case SerialPort.ODD_PARITY : + return 1; + case SerialPort.EVEN_PARITY : + return 2; + case SerialPort.MARK_PARITY : + return 3; + case SerialPort.SPACE_PARITY : + return 4; + default : + return 0; + } + } + } + return -1; + } + + /** + * Set Parity + * @param paritycode : 1 = odd, 2 = even, 3 = mark, 4 = space, default (0) = None + * @return true if success + */ + public boolean SetParityBits(int paritycode) { + if (isReady) { + if (myport instanceof SerialPort) { + switch(paritycode) { + case 1 : + myport.setParity(SerialPort.ODD_PARITY); + break; + case 2 : + myport.setParity(SerialPort.EVEN_PARITY); + break; + case 3 : + myport.setParity(SerialPort.MARK_PARITY); + break; + case 4 : + myport.setParity(SerialPort.SPACE_PARITY); + break; + default : + myport.setParity(SerialPort.NO_PARITY); + break; + } + return true; + } + } + return false; + } + + /** + * Get available bytes to read in internal buffer + * @return -1 if failed + */ + public int getAvailableBytesToRead() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.bytesAvailable(); + } + } + return -1; + } + + /** + * Get number of bytes stil waiting to send in internal buffer + * @return -1 if failed + */ + public int getWaitingBytesToSend() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.bytesAwaitingWrite(); + } + } + return -1; + } + + /** + * Get Clear-To-Send bit value + * @return true if CTS set + */ + public boolean getCTS() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.getCTS(); + } + } + return false; + } + + /** + * Set Request-To-Send bit + * @param value : if true, RTS is ON + */ + public void setRTS(boolean value) { + if (isReady) { + if (myport instanceof SerialPort) { + + if (value) + myport.setRTS(); + else + myport.clearRTS(); + } + } + } + + /** + * Write bytes to Serial + * @param bb : bytes to write + * @return number of bytes written to internal buffer, or -1 if failed + */ + public int Write(byte[] bb) { + if (isReady) { + if (myport instanceof SerialPort) { + int writeresult = myport.writeBytes(bb, bb.length); + writtencount+=writeresult; + return writeresult; + } + } + return -1; + } + + /** + * Set Read and Write blocking / timeout. Positive value means block until specific timeout + * @param readblocking_ms : 0 = no read block, positive = milliseconds to block waiting for read + * @param writeblocking_ms : 0 = no write block, positive = milliseconds to block until writing complete + */ + public void SetBlockingMode(int readblocking_ms, int writeblocking_ms) { + if (isReady) { + if (myport instanceof SerialPort) { + int blockvalue = 0; // default is NON_BLOCKING + if (readblocking_ms>0) blockvalue |= SerialPort.TIMEOUT_READ_BLOCKING; + if (writeblocking_ms>0) blockvalue |= SerialPort.TIMEOUT_WRITE_BLOCKING; + + myport.setComPortTimeouts(blockvalue, readblocking_ms, writeblocking_ms); + } + } + } + + /** + * Get how many bytes written since opening + * @return bytes written + */ + public long getBytesWritten() { + return writtencount; + } + + /** + * Get how many bytes read since opening + * @return bytes read + */ + public long getBytesRead() { + return readcount; + } + + @BA.Hide + public void SetJavaEvent(jSerialPort_Event event) { + javaevent = event; + } + + private void raise_log(String msg) { + if (need_log_event) { + ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg}); + } + if (javaevent!=null) { + javaevent.Log(msg); + } + } + + private void raise_newdata(byte[] bb) { + if (need_newdata_event) { + ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_newdata", false, new Object[] {bb}); + } + if (javaevent!=null) { + javaevent.newdata(bb); + } + } + + +} diff --git a/src/androgpio/SerialPort/jSerialPort_Event.java b/src/androgpio/SerialPort/jSerialPort_Event.java new file mode 100644 index 0000000..f7ff80c --- /dev/null +++ b/src/androgpio/SerialPort/jSerialPort_Event.java @@ -0,0 +1,6 @@ +package androgpio.SerialPort; + +public interface jSerialPort_Event { + void Log(String msg); + void newdata(byte[] bb); +} diff --git a/src/androgpio/androgpio.java b/src/androgpio/androgpio.java new file mode 100644 index 0000000..c80374d --- /dev/null +++ b/src/androgpio/androgpio.java @@ -0,0 +1,113 @@ +package androgpio; + +import java.io.File; + +import com.sun.jna.Platform; + +import android.content.pm.ApplicationInfo; +import anywheresoftware.b4a.BA; + +@BA.Author("Rudy Darmawan") +@BA.Version(0.31f) +@BA.ShortName("androgpio") +@BA.DependsOn(values = { "jna-min","jna-platform"}) +public class androgpio { + BA ba; + String eventname; + String rundirectory; + ApplicationInfo pi; + public void Initialize(BA ba, String eventname) { + this.ba = ba; + this.eventname = eventname; + File xxx = new File(""); + rundirectory = xxx.getAbsolutePath(); + pi = ba.context.getApplicationInfo(); + + } + + /** + * Get UserID assigned by kernel + * @return user ID number, or XX if androgpio not initialized before + */ + public String getUserID() { + return (pi != null ? String.valueOf(pi.uid) : "XX"); + } + + /** + * Get Native JNI Library Directory + * @return "N/A" kalau gak ada + */ + public String getNativeLibraryDir() { + return (pi != null ? pi.nativeLibraryDir : "N/A"); + } + + + /** + * Get Shared Library Files + * @return array string + */ + public String[] getSharedLibraryFiles() { + return (pi != null ? pi.sharedLibraryFiles : new String[] {}); + } + + /** + * Get Data Directory + * @return empty string if not initialized + */ + public String getDataDir() { + return (pi != null ? pi.dataDir : ""); + } + + /** + * Get Device's Protected Data Dir + * @return empty string if not initialized + */ + public String getProtectedDataDir() { + return (pi!=null ? pi.deviceProtectedDataDir : ""); + } + + public boolean isAndroid() { + return Platform.isAndroid(); + } + + public boolean is64bit() { + return Platform.is64Bit(); + } + + public String PlatformArchitecture() { + return System.getProperty("os.arch"); + } + + /** + * Will return Linux instead of Android + * @return OS Name in string + */ + public String OSName() { + return System.getProperty("os.name"); + } + + /** + * Will return Kernel Version + * @return Kernel Version in String + */ + public String KernelVersion() { + return System.getProperty("os.version"); + } + + public String PathSeparator() { + return System.getProperty("path.separator"); + } + + public String JavaLibraryPath() { + return System.getProperty("java.library.path"); + } + + public String JavaVMName() { + return System.getProperty("java.vm.name"); + } + + public static void main(String[] args) { + System.out.println("androgpio"); + } + +} diff --git a/src/androgpio/customsocket/AndroFTPClient.java b/src/androgpio/customsocket/AndroFTPClient.java new file mode 100644 index 0000000..72e3326 --- /dev/null +++ b/src/androgpio/customsocket/AndroFTPClient.java @@ -0,0 +1,1036 @@ +package androgpio.customsocket; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.time.Duration; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; + +import org.apache.commons.net.ProtocolCommandEvent; +import org.apache.commons.net.ProtocolCommandListener; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPClientConfig; +import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPSClient; +import org.apache.commons.net.io.CopyStreamEvent; +import org.apache.commons.net.io.CopyStreamListener; +import org.apache.commons.net.util.TrustManagerUtils; + + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; + +// Source : https://medium.com/bliblidotcom-techblog/java-ftp-integration-using-apache-commons-net-5efb3d300829 +// Source : https://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/ftp/FTPClient.html + +@BA.ShortName("AndroFTPClient") +@BA.Events(values= { + "log(msg as string)", + "configurecomplete(success as boolean)", + "listcomplete(path as string, success as boolean, files() as AndroFTPEntry, folders() as AndroFTPEntry)", + "makedirectorycomplete(path as string, success as boolean)", + "deletefilecomplete(path as string, success as boolean)", + "removedirectorycomplete(path as string, success as boolean)", + "uploadcomplete(localfile as string, ftppath as string, success as boolean)", + "downloadcomplete(ftppath as string, localfile as string, success as boolean)", + "downloadprogress(ftppath as string, localfile as string, totalbytestransfered as long, bytestransfered as long, size as long, speedKbps as double)", + "uploadprogress(localfile as string, ftppath as string, totalbytestransfered as long, bytestransfered as long, size as long)", + "androftpresult(path as string, success as boolean, entry as AndroFTPEntry)", + "renamefilecomplete(oldname as string, newname as string, success as boolean)", + "appendfilecomplete(localpath as string, ftppath as string, success as boolean)", + "appendfileprogress(localpath as string, ftppath as string, totalbytestransfered as long, bytestransfered as long, size as long)" + +}) + +@BA.DependsOn(values = { "commons-net-3.10.0" }) +@BA.Permissions(values = { "android.permission.INTERNET" }) +public class AndroFTPClient { + + private BA ba; + private String eventname; + private Object Me; + private boolean need_log_event = false; + private boolean need_listcomplete_event = false; + + private boolean need_makedirectorycomplete_event = false; + private boolean need_deletefilecomplete_event = false; + private boolean need_removedirectorycomplete_event = false; + private boolean need_uploadcomplete_event = false; + private boolean need_downloadcomplete_event = false; + + private boolean serverconfirmed = false; + private String mServer = null; + private int mPort = 21; + private String mUser = null; + private String mPassword = null; + private boolean mPassiveMode = false; + + private boolean mSecured = false; + private FTPClientConfig mConfig = null; + + + /** + * Initialize AndroFTPClient + * @param eventname event name + */ + public void Initialize(BA ba, String eventname) { + this.ba = ba; + this.eventname = eventname; + Me = this; + mConfig = new FTPClientConfig(FTPClientConfig.SYST_L8); + //mConfig.setUnparseableEntries(true); + + if (ba!=null) { + if (mycodes.valid_string(eventname)) { + need_log_event = ba.subExists(eventname + "_log"); + need_listcomplete_event = ba.subExists(eventname + "_listcomplete"); + need_makedirectorycomplete_event = ba.subExists(eventname + "_makedirectorycomplete"); + need_deletefilecomplete_event = ba.subExists(eventname + "_deletefilecomplete"); + need_uploadcomplete_event = ba.subExists(eventname + "_uploadcomplete"); + need_downloadcomplete_event = ba.subExists(eventname + "_downloadcomplete"); + } + } + } + + /** + * Check if Server Address and Port is confirmed and connectable + * @return true if confirmed + */ + public boolean ServerConfirmed() { + return serverconfirmed; + } + + // Helper to get either FTPClient or FTPSClient based on boolean secured +// private T getftpclient(boolean secured, T iftrue, T iffalse) { +// return secured ? iftrue : iffalse; +// } + + + + + + // Helper to create new Object either FTPClient or FTPSClient based on boolean secured + // FTPSClient is extending from FTPClient + private FTPClient CreateFTPClient(boolean secured) { + if (secured) { + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + FTPSClient cl = new FTPSClient(); + cl.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager()); + try { + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(null, null); + KeyManager km = kmf.getKeyManagers()[0]; + cl.setKeyManager(km); + } catch (NoSuchAlgorithmException e) { + raise_log("CreateFTPClient [secured=true] failed, Exception=" + e.getMessage()); + } catch (UnrecoverableKeyException e) { + raise_log("CreateFTPClient [secured=true] failed, Exception=" + e.getMessage()); + } catch (KeyStoreException e) { + raise_log("CreateFTPClient [secured=true] failed, Exception=" + e.getMessage()); + } + + cl.setBufferSize(8*1024); + return cl; + } else { + + FTPClient cl = new FTPClient(); + cl.setBufferSize(8*1024); + return cl; + } + + } + + + /** + * Configure FTP Connection + * Will raise event configurecomplete(success as boolean) + * @param server server address + * @param port server port + * @param user username + * @param password password + * @param passive true for passive mode, false for active mode + * @param binary true for binary mode, false for ascii mode + * @param secured true for secured connection (FTPS), false for unsecured (FTP) + * @return Object to use in Wait For + */ + public Object Configure_Connection(BA bax,String server, int port, String user, String password, boolean passive, boolean binary, boolean secured) { + serverconfirmed = false; + mServer = null; + mPort = 0; + mUser = null; + mPassword = null; + mPassiveMode = false; + mSecured = false; + Object sender = new Object(); + BA.runAsync(bax, sender, eventname+"_configurecomplete", new Object[] {false}, new Callable() { + + @Override + public Object[] call() throws Exception { + FTPClient ftp = CreateFTPClient(secured); + ftp.configure(mConfig); + + //if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + try { + + ftp.setConnectTimeout(5000); + ftp.connect(server, port); + if (ftp.login(user, password)){ + + ftp.logout(); + serverconfirmed = true; + mServer = server; + mPort = port; + mUser = user; + mPassword = password; + mPassiveMode = passive; + + mSecured = secured; + } else raise_log("Unable to login to server"); + ftp.disconnect(); + } catch (IOException e) { + raise_log("Unable to FTP Connect to Server " + server + ":" + port + ", Exception=" + e.getMessage()); + } + return new Object[] {serverconfirmed}; + } + + }); + return sender; + } + + /** + * Check if using FTPS (secured) or FTP (unsecured) + * @return true if secured + */ + public boolean getSecured() { + return mSecured; + } + + /** + * Get FTP Server Address + * @return null if not assigned + */ + public String getServer() { + return mServer; + } + + /** + * Get FTP Server Port + * + * @return 0 if not assigned + */ + public int getPort() { + return mPort; + } + + + /** + * Get FTP Username + * + * @return null if not assigned + */ + public String getUser() { + return mUser; + } + + + /** + * Get FTP Password + * + * @return null if not assigned + */ + public String getPassword() { + return mPassword; + } + + /** + * Get Passive Mode + * + * @return true if passive mode + */ + public boolean getPassiveMode() { + return mPassiveMode; + } + + + + + /** + * Make Directory in FTP Server + * Will raise event makedirectorycomplete(path as string, success as boolean) + * @param path path to create + * @return Object to use in Wait For + */ + public Object MakeDirectory(BA bax, final String path) { + Object sender = new Object(); + if (serverconfirmed) { + BA.runAsync(bax, sender, eventname+"_makedirectorycomplete", new Object[] {path, false}, new Callable(){ + + @Override + public Object[] call() throws Exception{ + boolean result = false; + FTPClient ftp = CreateFTPClient(mSecured); + ftp.configure(mConfig); + + //if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + try { + ftp.connect(mServer, mPort); + if (ftp.login(mUser, mPassword)) { + // setelah login, secured connection butuh set PBSZ dan PROT + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + if (mSecured) { + ((FTPSClient)ftp).execPBSZ(0); + ((FTPSClient)ftp).execPROT("P"); + } + result = ftp.makeDirectory(path); + ftp.logout(); + } + ftp.disconnect(); + } catch (IOException e) { + raise_log("MakeDirectory failed, Exception=" + e.getMessage()); + } + return new Object[] {path, result}; + } + + }); + + } else { + raise_log("MakeDirectory failed, Server not confirmed"); + raise_makedirectorycomplete(sender,path, false); + } + return sender; + + } + + /** + * Delete File in FTP Server + * Will raise event deletefilecomplete(path as string, success as boolean) + * + * @param path path to delete + * @return Object to use in Wait For + */ + public Object DeleteFile(BA bax,final String path) { + Object sender = new Object(); + if (serverconfirmed) { + BA.runAsync(bax, sender, eventname+"_deletefilecomplete", new Object[] {path, false}, new Callable() { + + @Override + public Object[] call() throws Exception { + boolean result = false; + FTPClient ftp = CreateFTPClient(mSecured); + ftp.configure(mConfig); + + // if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + try { + ftp.connect(mServer, mPort); + if (ftp.login(mUser, mPassword)) { + // setelah login, secured connection butuh set PBSZ dan PROT + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + if (mSecured) { + ((FTPSClient)ftp).execPBSZ(0); + ((FTPSClient)ftp).execPROT("P"); + } + result = ftp.deleteFile(path); + + ftp.logout(); + } + ftp.disconnect(); + } catch (IOException e) { + raise_log("DeleteFile failed, Exception=" + e.getMessage()); + } + return new Object[] {path, result}; + } + + }); + + } else { + raise_log("DeleteFile failed, Server not confirmed"); + raise_deletefilecomplete(sender, path, false); + } + return sender; + } + + /** + * Remove Directory in FTP Server + * Will raise event removedirectorycomplete(path as string, success as boolean) + * @param path path to remove + * @return Object to use in Wait For + */ + public Object RemoveDirectory(BA bax,final String path) { + Object sender = new Object(); + if (serverconfirmed) { + BA.runAsync(bax, sender, eventname+"_removedirectorycomplete", new Object[] {path, false}, new Callable() { + + @Override + public Object[] call() throws Exception { + boolean result = false; + FTPClient ftp = CreateFTPClient(mSecured); + ftp.configure(mConfig); + + // if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + try { + ftp.connect(mServer, mPort); + if (ftp.login(mUser, mPassword)) { + // setelah login, secured connection butuh set PBSZ dan PROT + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + if (mSecured) { + ((FTPSClient)ftp).execPBSZ(0); + ((FTPSClient)ftp).execPROT("P"); + } + result = ftp.removeDirectory(path); + + ftp.logout(); + } + ftp.disconnect(); + } catch (IOException e) { + raise_log("RemoveDirectory failed, Exception=" + e.getMessage()); + } + return new Object[] {path, result}; + } + + }); + + } else { + raise_log("RemoveDirectory failed, Server not confirmed"); + raise_removedirectorycomplete(sender, path, false); + } + return sender; + } + + /** + * Append File to FTP Server + * Will raise event appendfilecomplete(localpath as string, ftppath as string, success as boolean) + * @param localpath local file as source to append + * @param ftppath target ftp path to append + * @return Object to use in Wait For + */ + public Object AppendFile(BA bax, final String localpath, final String ftppath) { + Object sender = new Object(); + if (serverconfirmed) { + BA.runAsync(bax, sender, eventname+"_appendfilecomplete", new Object[] {localpath, ftppath, false}, new Callable(){ + + @Override + public Object[] call() throws Exception { + boolean result = false; + final boolean need_progress = bax.subExists(eventname+"_appendfileprogress"); + try { + File fsource = new File(localpath); + if (fsource.isFile()) { + if (fsource.length()>0) { + // sampai sini file local ada dan size > 0 + //BufferedInputStream fis = new BufferedInputStream(new FileInputStream(fsource)); + FileInputStream fis = new FileInputStream(fsource); + // konfigurasi FTP Client + FTPClient ftp = CreateFTPClient(mSecured); + ftp.configure(mConfig); + if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + ftp.connect(mServer, mPort); + + if (mPassiveMode) { + ftp.enterLocalPassiveMode(); + ftp.setIpAddressFromPasvResponse(true); + } + if (ftp.login(mUser, mPassword)) { + + //Harus set ini setelah login. + //Source : https://stackoverflow.com/questions/70395475/apache-commons-ftpclient-not-retrieving-all-bytes-from-source-file + ftp.setFileType(FTPClient.BINARY_FILE_TYPE); + // setelah login, secured connection butuh set PBSZ dan PROT + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + if (mSecured) { + ((FTPSClient)ftp).execPBSZ(0); + ((FTPSClient)ftp).execPROT("P"); + } + // check apakah ftppath exist? + FTPFile ftarget = null; + FTPFile[] list = ftp.listFiles(ftppath); + + if (list!=null && list.length>0) ftarget = list[0]; + + if (ftarget != null) { // exists + if (ftarget.isFile()) { // berupa file + if (need_progress) ftp.setCopyStreamListener(new CopyStreamListener() { + @Override + public void bytesTransferred(CopyStreamEvent event) { + raise_log("Append Progress A : Source [" + + event.getSource().toString() + "] " + + event.getTotalBytesTransferred() + " / " + + event.getBytesTransferred() + " / " + + event.getStreamSize()); + raise_appendprogress(bax, sender, localpath, ftppath, + event.getTotalBytesTransferred(), + event.getBytesTransferred(), event.getStreamSize()); + } + + @Override + public void bytesTransferred(long totalBytesTransferred, + int bytesTransferred, long streamSize) { + raise_log("Append Progress B : " + totalBytesTransferred + " / " + + bytesTransferred + " / " + streamSize); + raise_appendprogress(bax, sender, localpath, ftppath, + totalBytesTransferred, bytesTransferred, streamSize); + } + }); + + // kasih keep alive biar koneksi terjaga + ftp.setKeepAlive(true); + // keep alive tiap 10 detik + ftp.setControlKeepAliveTimeout(Duration.ofSeconds(10)); + + result = ftp.appendFile(ftppath, fis); + + } else raise_log("AppendFile failed, FTP path [" + ftppath + "] is not File"); + } else raise_log("AppendFile failed, FTP path [" + ftppath + "] not exists"); + ftp.logout(); + } else raise_log("AppendFile [" + ftppath + "] failed, Unable to login to server"); + // disconnect FTP + ftp.disconnect(); + // close Input File + fis.close(); + } else raise_log("AppendFile failed, Local file [" + localpath + "] size is 0"); + } else raise_log("AppendFile failed, Local file [" + localpath + "] not exists"); + + + } catch (IOException e) { + raise_log("AppendFile [" + ftppath + "] to [" + localpath + "] failed, Exception=" + + e.getMessage()); + } + return new Object[] {localpath, ftppath, result}; + } + + }); + } else { + raise_log("AppendFile failed, Server not confirmed"); + bax.raiseEventFromDifferentThread(sender, null, 0, eventname+"_appendfilecomplete", false, new Object[] {localpath, ftppath, false}); + } + return sender; + } + + /** + * Download File from FTP Server + * will raise event downloadcomplete(ftppath as string, localfile as string, success as boolean) + * will raise event downloadprogress(ftppath as string, localfile as string, totalbytestransfered as long, bytestransfered as long, size as long, speedKbps as double) + * @param ftppath path to download + * @param localfile filename to save + * @return Object to use in Wait For + */ + public Object DownloadFile(BA bax,final String ftppath, final String localfile) { + Object sender = new Object(); + if (serverconfirmed) { + BA.runAsync(bax, sender, eventname+"_downloadcomplete", new Object[] {ftppath, localfile, false}, new Callable() { + + @Override + public Object[] call() throws Exception { + final boolean need_progress = bax.subExists(eventname+"_downloadprogress"); + boolean result = false; + final long sourcesize; // untuk progress, sebab event bytesTransferred streamSize return -1 + FTPClient ftp = CreateFTPClient(mSecured); + ftp.configure(mConfig); + + if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + + try { + + ftp.connect(mServer, mPort); + + if (mPassiveMode) { + ftp.enterLocalPassiveMode(); + ftp.setIpAddressFromPasvResponse(true); + } + + if (ftp.login(mUser, mPassword)) { + //Harus set ini setelah login. + //Source : https://stackoverflow.com/questions/70395475/apache-commons-ftpclient-not-retrieving-all-bytes-from-source-file + ftp.setFileType(FTPClient.BINARY_FILE_TYPE); + // setelah login, secured connection butuh set PBSZ dan PROT + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + if (mSecured) { + ((FTPSClient)ftp).execPBSZ(0); + ((FTPSClient)ftp).execPROT("P"); + } + // check apakah ftppath exist? + FTPFile fsource = null; + FTPFile[] list = ftp.listFiles(ftppath); + if (list!=null && list.length>0) fsource = list[0]; + + if (fsource!=null) { // exists + if (fsource.isFile()) { // berupa file + sourcesize = fsource.getSize(); + if (sourcesize>0) { // ada ukuran nya + // buat file di local storage + File ftarget = new File(localfile); + //BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(ftarget)); + FileOutputStream fos = new FileOutputStream(ftarget); + + // untuk download progress + if (need_progress) { + final AtomicLong transfered = new AtomicLong(0); + final AtomicLong tick = new AtomicLong(System.currentTimeMillis()); + final AtomicReference KBps = new AtomicReference(0.0); + + // FTP transfer status + ftp.setCopyStreamListener(new CopyStreamListener() { + + @Override + public void bytesTransferred(CopyStreamEvent event) { + // event ini gak trigger + //raise_log("Download Progress A : Source ["+event.getSource().toString()+"] "+event.getTotalBytesTransferred()+" / "+event.getBytesTransferred()+" / "+event.getStreamSize()); + //raise_downloadprogress(bax, sender, ftppath, localfile, event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize()); + } + + @Override + public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, + long streamSize) { + //bytetransfered tergantung buffer size, default di 1024 + //raise_log("Download Progress B : "+totalBytesTransferred+" / "+bytesTransferred+" / "+targetsize); + + transfered.set(transfered.get()+bytesTransferred); + long millis = System.currentTimeMillis(); + if ((millis-tick.get())>=1000) { + // update speed + long delta = millis-tick.get(); + tick.set(millis); + // dapat berapa bytes per second + double speed = transfered.get()/(delta/1000.0); + // reset + transfered.set(0); + KBps.set(speed/1024.0); + + + } + raise_downloadprogress(bax, sender, ftppath, localfile, totalBytesTransferred, bytesTransferred, sourcesize, KBps.get()); + } + }); + } + // kasih keep alive biar koneksi terjaga + ftp.setKeepAlive(true); + // keep alive tiap 10 detik + ftp.setControlKeepAliveTimeout(Duration.ofSeconds(10)); + + // perintah download, true = success, false = failed + result = ftp.retrieveFile(ftppath, fos); + // tutup file + fos.flush(); + fos.close(); + + long newsize = ftarget.length(); + raise_log("DownloadFile from ["+ftppath+"] to ["+localfile+"] "+(result ? "success":"failed")); + raise_log("FTP Size : "+sourcesize+" / Local Size : "+newsize); + } else raise_log("DownloadFile failed, FTP path ["+ftppath+"] size is 0"); + } else raise_log("DownloadFile failed, FTP path ["+ftppath+"] is not File"); + } else raise_log("DownloadFile failed, FTP path ["+ftppath+"] not exists"); + ftp.logout(); + } else raise_log("DownloadFile ["+ftppath+"] failed, Unable to login to server"); + ftp.disconnect(); + } catch (IOException e) { + raise_log("DownloadFile ["+ftppath+"] to ["+localfile+"] failed, Exception=" + e.getMessage()); + } + return new Object[] {ftppath, localfile, result}; + } + + }); + + } else { + raise_log("DownloadFile failed, Server not confirmed"); + raise_downloadcomplete(sender, ftppath, localfile, false); + } + return sender; + } + + /** + * Rename File in FTP Server + * Will raise event renamefilecomplete(oldname as string, newname as string, success as boolean) + * @param oldname old file name + * @param newname new file name + * @return Object to use in Wait For + */ + public Object RenameFile(BA bax, final String oldname, final String newname) { + Object sender = new Object(); + if (serverconfirmed) { + BA.runAsync(bax, sender, eventname+"_renamefilecomplete", new Object[] {oldname, newname, false}, new Callable() { + + @Override + public Object[] call() throws Exception { + boolean result = false; + FTPClient ftp = CreateFTPClient(mSecured); + ftp.configure(mConfig); + + //if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + + try { + + ftp.connect(mServer, mPort); + + if (mPassiveMode) { + ftp.enterLocalPassiveMode(); + ftp.setIpAddressFromPasvResponse(true); + } + + if (ftp.login(mUser, mPassword)) { + // setelah login, secured connection butuh set PBSZ dan PROT + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + if (mSecured) { + ((FTPSClient)ftp).execPBSZ(0); + ((FTPSClient)ftp).execPROT("P"); + } + // perintah rename, true = success, false = failed + result = ftp.rename(oldname, newname); + ftp.logout(); + } else raise_log("RenameFile ["+oldname+"] to ["+newname+"] failed, Unable to login to server"); + ftp.disconnect(); + } catch (IOException e) { + raise_log("RenameFile ["+oldname+"] to ["+newname+"] failed, Exception=" + e.getMessage()); + } + return new Object[] {oldname, newname, result}; + } + + }); + } else { + raise_log("RenameFile failed, Server not confirmed"); + bax.raiseEventFromDifferentThread(sender, null, 0, eventname+"_renamefilecomplete", false, new Object[] {oldname, newname, false}); + } + return sender; + } + + /** + * Upload File to FTP Server + * will raise event uploadcomplete(localfile as string, ftppath as string, success as boolean) + * will raise event uploadprogress(localfile as string, ftppath as string, totalbytestransfered as long, bytestransfered as long, size as long) + * @param localpath local file to upload + * @param ftppath target FTP path + * @return Object to use in Wait For + */ + public Object UploadFile(BA bax,final String localpath, final String ftppath) { + Object sender = new Object(); + if (serverconfirmed) { + + BA.runAsync(bax, sender, eventname + "_uploadcomplete", new Object[] { localpath, ftppath, false }, + new Callable() { + + @Override + public Object[] call() throws Exception { + boolean result = false; + final long targetsize; // untuk progress, sebab event bytesTransferred streamSize return -1 + final boolean need_progress = bax.subExists(eventname + "_uploadprogress"); + File fsource = new File(localpath); + if (fsource.isFile()) { + targetsize = fsource.length(); + if (targetsize>0) { + // buat FTP Client + FTPClient ftp = CreateFTPClient(mSecured); + ftp.configure(mConfig); + + // untuk logging + if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + + try { + + ftp.connect(mServer, mPort); + + if (mPassiveMode) { + ftp.enterLocalPassiveMode(); + ftp.setIpAddressFromPasvResponse(true); + } + + if (ftp.login(mUser, mPassword)) { + //Harus set ini setelah login. + //Source : https://stackoverflow.com/questions/70395475/apache-commons-ftpclient-not-retrieving-all-bytes-from-source-file + ftp.setFileType(FTPClient.BINARY_FILE_TYPE); + // setelah login, secured connection butuh set PBSZ dan PROT + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + if (mSecured) { + ((FTPSClient)ftp).execPBSZ(0); + ((FTPSClient)ftp).execPROT("P"); + } + // untuk progress + if (need_progress) ftp.setCopyStreamListener(new CopyStreamListener() { + @Override + public void bytesTransferred(CopyStreamEvent event) { + // event ini gak trigger + //raise_log("Upload Progress A : Source ["+event.getSource().toString()+"] "+event.getTotalBytesTransferred()+" / "+event.getBytesTransferred()+" / "+event.getStreamSize()); + //raise_uploadprogress(bax, sender, localpath, ftppath, event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize()); + } + + @Override + public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, + long streamSize) { + //bytetransfered tergantung buffer size, default di 1024 + raise_log("Upload Progress B : "+totalBytesTransferred+" / "+bytesTransferred+" / "+targetsize); + raise_uploadprogress(bax, sender, localpath, ftppath, totalBytesTransferred, bytesTransferred, targetsize); + } + }); + // kasih keep alive biar koneksi terjaga + ftp.setKeepAlive(true); + // keep alive durasi 10 detik + ftp.setControlKeepAliveTimeout(Duration.ofSeconds(10)); + + //BufferedInputStream fis = new BufferedInputStream(new FileInputStream(fsource)); + FileInputStream fis = new FileInputStream(fsource); + result = ftp.storeFile(ftppath, fis); + fis.close(); + ftp.logout(); + raise_log("UploadFile ["+localpath+"] to ["+ftppath+"] "+(result?"success":"failed")); + } else raise_log("UploadFile ["+localpath+"] failed, Unable to login to server"); + ftp.disconnect(); + } catch (IOException e) { + raise_log("UploadFile ["+localpath+"] to ["+ftppath+"] failed, Exception=" + e.getMessage()); + } + } else raise_log("UploadFile failed, local file ["+localpath+"] size is 0"); + } else raise_log("UploadFile failed, local file ["+localpath+"] not exists"); + + return new Object[] { localpath, ftppath, result }; + } + + }); + + } else { + raise_log("UploadFile failed, Server not confirmed"); + raise_uploadcomplete(sender, localpath, ftppath, false); + } + return sender; + } + + /** + * Get AndroFTPEntry from FTP Server + * Can be used to check if a path is exists, if it is a file or a folder, or to get file size + * Will raise event androftpresult(path as string, success as boolean, entry as AndroFTPEntry) + * If not exists or failed to get, success will be false and entry will be null + * @param path path to get + * @return Object to use in Wait For + */ + public Object GetAndroFTPEntry(BA bax, final String path) { + Object sender = new Object(); + if (serverconfirmed) { + BA.runAsync(bax, sender, eventname+"_androftpresult", new Object[] {path, false, null}, new Callable() { + + @Override + public Object[] call() throws Exception { + AndroFTPEntry result = null; + boolean success = false; + FTPClient ftp = CreateFTPClient(mSecured); + ftp.configure(mConfig); + + // if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + try { + ftp.connect(mServer, mPort); + if (ftp.login(mUser, mPassword)) { + // setelah login, secured connection butuh set PBSZ dan PROT + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + if (mSecured) { + ((FTPSClient)ftp).execPBSZ(0); + ((FTPSClient)ftp).execPROT("P"); + } + FTPFile[] list = ftp.listFiles(path); + if (list!=null && list.length>0) { + result = new AndroFTPEntry(list[0]); + success = true; + } + + ftp.logout(); + } + ftp.disconnect(); + } catch (IOException e) { + raise_log("GetAndroFTPEntry failed, Exception=" + e.getMessage()); + } + return new Object[] {path, success, result}; + } + + }); + } else { + raise_log("GetAndroFTPEntry failed, Server not confirmed"); + bax.raiseEventFromDifferentThread(sender, null, 0, eventname+"_androftpresult", false, new Object[] {path, false, null}); + } + return sender; + } + + + + + /** + * List Files and Folders in FTP Server + * Will raise event listcomplete(path as string, success as boolean, files() as AndroFTPEntry, folders() as AndroFTPEntry) + * @param path path to list + * @return Object to use in Wait For + */ + public Object ListFiles(BA bax,final String path) { + Object sender = new Object(); + + if (serverconfirmed) { + BA.runAsync(bax, sender, eventname+"_listcomplete", new Object[] {path, false,null, null}, new Callable() { + + + + @Override + public Object[] call() throws Exception { + boolean result = false; + AndroFTPEntry[] files = null; + AndroFTPEntry[] folders = null; + // new FTPClient + FTPClient ftp = CreateFTPClient(mSecured); + ftp.configure(mConfig); + + //if (BA.debugMode) ftp.addProtocolCommandListener(pcl); + try { + + ftp.connect(mServer, mPort); + // harus setelah connect, kalau sebelum connect, akan direset menjadi ASCII_FILE_TYPE + + // harus setelah connect, kalau sebelum connect, akan direset menjadi LOCAL_ACTIVE_MODE + if (mPassiveMode) { + ftp.enterLocalPassiveMode(); + ftp.setIpAddressFromPasvResponse(true); + } + + if (ftp.login(mUser, mPassword)) { + // setelah login, secured connection butuh set PBSZ dan PROT + // Source : https://stackoverflow.com/questions/21453716/android-ftp-file-tranfer-over-explicit-tls/21460934#21460934 + if (mSecured) { + ((FTPSClient)ftp).execPBSZ(0); + ((FTPSClient)ftp).execPROT("P"); + } + //if (BA.debugMode) raise_log("ListFiles FTP Login Success, charset="+ftp.getCharsetName()+", control encoding="+ftp.getControlEncoding()); + + if (mycodes.valid_string(path)) { + if (ftp.changeWorkingDirectory(path)) { + //if (BA.debugMode) raise_log("ListFiles ChangeWorkingDirectory to ["+path+"] Success"); + } else { + //if (BA.debugMode) raise_log("ListFiles ChangeWorkingDirectory to ["+path+"] Failed"); + } + } + + //if (BA.debugMode) raise_log("PWD : "+ftp.printWorkingDirectory()); + + + FTPFile[] _files = ftp.listFiles(); + //if (BA.debugMode) raise_log("ListFiles ["+path+"] result: "+_files.length+" items"); + + + //if (BA.debugMode) Stream.of(_files).forEach(ff-> BA.Log(ff.getRawListing())); + + files = Stream.of(_files) + .filter(ff-> ff.isValid()) + .filter(ff-> ff.isFile()) + .map(ff-> new AndroFTPEntry(ff)) + .toArray(AndroFTPEntry[]::new); + folders = Stream.of(_files) + .filter(ff-> ff.isValid()) + .filter(ff -> ff.isDirectory()) + .map(ff-> new AndroFTPEntry(ff)) + .toArray(AndroFTPEntry[]::new); + + ftp.logout(); + ftp.disconnect(); + + //if (BA.debugMode) raise_log("FTP Logout and Disconnect"); + result = true; + } else raise_log("ListFiles failed, Unable to login to server"); + } catch (IOException e) { + raise_log("ListFiles failed, Exception=" + e.getMessage()); + } + return new Object[] {path, result, files, folders}; + } + + }); + + } else { + raise_log("ListFiles failed, Server not confirmed"); + raise_listcomplete(sender, path, false, null, null); + } + return sender; + } + + // for FTP Debugging + private ProtocolCommandListener pcl = new ProtocolCommandListener() { + @Override + public void protocolCommandSent(ProtocolCommandEvent event) { + if (BA.debugMode) + BA.Log("FTP Command Sent: " + event.getMessage()); + } + + @Override + public void protocolReplyReceived(ProtocolCommandEvent event) { + if (BA.debugMode) + BA.Log("FTP Reply Code: " + event.getReplyCode() + ", Message: " + event.getMessage()); + } + }; + + + + + + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, eventname+"_log", false, new Object[] {msg}); + } + + private void raise_listcomplete(Object sender, String path, boolean success, AndroFTPEntry[] files, AndroFTPEntry[] folders) { + if (need_listcomplete_event) ba.raiseEventFromDifferentThread(sender, null, 0, eventname+"_listcomplete", false, new Object[] {path, success, files, folders}); + } + + + + private void raise_makedirectorycomplete(Object sender, String path, boolean success) { + if (need_makedirectorycomplete_event) + ba.raiseEventFromDifferentThread(sender, null, 0, eventname + "_makedirectorycomplete", false, + new Object[] { path, success }); + } + + private void raise_deletefilecomplete(Object sender, String path, boolean success) { + if (need_deletefilecomplete_event) + ba.raiseEventFromDifferentThread(sender, null, 0, eventname + "_deletefilecomplete", false, + new Object[] { path, success }); + } + + private void raise_removedirectorycomplete(Object sender, String path, boolean success) { + if (need_removedirectorycomplete_event) + ba.raiseEventFromDifferentThread(sender, null, 0, eventname + "_removedirectorycomplete", false, + new Object[] { path, success }); + } + + private void raise_uploadcomplete(Object sender, String localfile, String ftppath, boolean success) { + if (need_uploadcomplete_event) + ba.raiseEventFromDifferentThread(sender, null, 0, eventname + "_uploadcomplete", false, + new Object[] { localfile, ftppath, success }); + } + + private void raise_downloadcomplete(Object sender, String ftppath, String localfile, boolean success) { + if (need_downloadcomplete_event) + ba.raiseEventFromDifferentThread(sender, null, 0, eventname + "_downloadcomplete", false, + new Object[] { ftppath, localfile, success }); + } + + private void raise_downloadprogress(BA bax, Object sender, String ftppath, String localfile, long totalbytestransfered, + long bytestransfered, long size, double speedKBps) { + if (bax!=null) { + bax.raiseEventFromDifferentThread(sender, null, 0, eventname + "_downloadprogress", false, + new Object[] { ftppath, localfile, totalbytestransfered, bytestransfered, size, speedKBps }); + } + + + } + + private void raise_uploadprogress(BA bax, Object sender, String localfile, String ftppath, + long totalbytestransfered, long bytestransfered, long size) { + if (bax!=null) { + bax.raiseEventFromDifferentThread(sender, null, 0, eventname + "_uploadprogress", false, + new Object[] { localfile, ftppath, totalbytestransfered, bytestransfered, size }); + } + + } + + private void raise_appendprogress(BA bax, Object sender, String localfile, String ftppath, long totalbytestransfered, long bytestransfered, long size) { + if (bax!=null) { + bax.raiseEventFromDifferentThread(sender, null, 0, eventname + "_appendfileprogress", false, + new Object[] { localfile, ftppath, totalbytestransfered, bytestransfered, size }); + } + } + +} diff --git a/src/androgpio/customsocket/AndroFTPEntry.java b/src/androgpio/customsocket/AndroFTPEntry.java new file mode 100644 index 0000000..25bd0f6 --- /dev/null +++ b/src/androgpio/customsocket/AndroFTPEntry.java @@ -0,0 +1,109 @@ +package androgpio.customsocket; +import org.apache.commons.net.ftp.FTPFile; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; + +@BA.ShortName("AndroFTPEntry") +public class AndroFTPEntry { + final String name; + final String user; + final String group; + final long size; + final long timestamp; + final boolean isFile; + final boolean isDirectory; + public AndroFTPEntry() { + name = ""; + user = ""; + group = ""; + size = 0; + timestamp = 0; + isFile = false; + isDirectory = false; + } + + public AndroFTPEntry(FTPFile f) { + name = f.getName(); + user = f.getUser(); + group = f.getGroup(); + size = f.getSize(); + //f.getType() // 0 means file, 1 means directory, 2 means symbolic link file, and 3 means unknown + timestamp = f.getTimestamp().getTimeInMillis(); + isFile = f.isFile(); + isDirectory = f.isDirectory(); + } + + /** + * Check if valid AndroFTPEntry + * Name, user, and group must have value + * if isFile=true, then size and timestamp must have value + * @return true if valid + */ + public boolean isValid() { + if (mycodes.valid_string(name)) { + if (mycodes.valid_string(user)) { + if (mycodes.valid_string(group)) { + if (isFile) { + if (size > 0) { + if (timestamp > 0) { + return true; + } + } + } else if (isDirectory) { + return true; + } + + } + } + } + return false; + } + + public String getName() { + return name; + } + + public String getUser() { + return user; + } + + public String getGroup() { + return group; + } + + public long getSize() { + return size; + } + + public long getTimestamp() { + return timestamp; + } + + public String getTimestamp_DDMMYYYY_HHMMSS() { + return mycodes.Tick_To_DDMMYYYY_HHMMSS(timestamp); + } + + public String getTimestamp_DDMMYYYY() { + return mycodes.Tick_To_DDMMYYYY(timestamp); + } + + public String getTimestamp_HHMMSS() { + return mycodes.Tick_To_HHMMSS(timestamp); + } + + public boolean getIsFile() { + return isFile; + } + + public boolean getIsDirectory() { + return isDirectory; + } + + /** + * Print to String + */ + public String toString() { + return "Name: " + name + ", User: " + user + ", Group: " + group + ", Size: " + size + ", Timestamp: " + timestamp + ", isFile: " + isFile + ", isDirectory: " + isDirectory; + } +} diff --git a/src/androgpio/customsocket/JsonArray.java b/src/androgpio/customsocket/JsonArray.java new file mode 100644 index 0000000..816d465 --- /dev/null +++ b/src/androgpio/customsocket/JsonArray.java @@ -0,0 +1,806 @@ +package androgpio.customsocket; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.streams.File; +@BA.ShortName("JsonArray") +public class JsonArray { + + private JSONArray ja; + + public JsonArray() { + ja = null; + } + + @BA.Hide + public JsonArray(JSONArray value) { + ja = value; + } + + @BA.Hide + /** + * Get Native JSONArray Object + * @return JSONArray object + */ + public JSONArray getObject() { + return ja; + } + + @BA.Hide + /** + * Set Native JSONArray object + * @param value : source value + */ + public void setObject(JSONArray value) { + ja = value; + } + + /** + * Initialize empty JsonArray + */ + public void InitializeEmpty() { + ja = new JSONArray(); + + } + + /** + * Initialize JsonArray from String + * @param value : string with format of Json Array + * @return true if can be initialized + */ + public boolean InitializeFromString(String value) { + if (mycodes.valid_string(value)) { + try { + + ja = new JSONArray(value); + + return true; + } catch(JSONException e) { + if (BA.debugMode) BA.Log("Unable to InitializeFromString, Msg : "+e.getMessage()); + } + } + return false; + } + + /*** + * Initialize JsonArray from a file containing plain string + * Reading result from file, will be used in InitializeFromString function + * @param directory : directory path + * @param filename : filename + * @return true if value can be translated to JsonArray + */ + public boolean InitializeFromFile(String directory, String filename) { + try { + if (File.Exists(directory, filename)) { + String value = File.ReadString(directory, filename); + return InitializeFromString(value); + } + } catch (IOException e) { + if (BA.debugMode) BA.Log("Unable to InitializeFromFile, Msg : "+e.getMessage()); + } + return false; + } + + /** + * Initialize JsonArray from B4X List + * @param value : B4X List + * @return true if value can be translated to JsonArray + */ + public boolean InitializeFromList(List value) { + if (value instanceof List) { + if (value.IsInitialized()) { + if (value.getSize()>0) { + ja = new JSONArray(value.getObject()); + return true; + + } + } + } + return false; + } + + /** + * Initialize JsonArray from array of Object + * @param value : array of object, which can be anything from String, int, long, etc. + * @return true if value can be translated to JsonArray + */ + public boolean InitializeFromObject(Object... value) { + if (value != null) { + if (value.length>0) { + try { + ja = new JSONArray(value); + return true; + } catch(JSONException e) { + if (BA.debugMode) BA.Log("Unable to InitializeFromObject, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Check if JsonArray is initialized + * @return true if initialized + */ + public boolean IsInitialized() { + if (ja != null) { + if (ja instanceof JSONArray) { + return true; + } + } + return false; + } + + /** + * Clear content of JsonArray + */ + public void Clear() { + if (IsInitialized()) { + Object oo; + do { + oo = ja.remove(0); + } while(oo != null); + } + } + + /** + * Check if JsonArray has some value + * @return true if has value + */ + public boolean HasSomeValue() { + if (IsInitialized()) { + + return ja.length()>0; + } + return false; + } + + /** + * Get Size of JsonArray + * @return 0 if empty or not initialized + */ + public int GetSize() { + if (HasSomeValue()) { + return ja.length(); + } + return 0; + } + + /** + * Get JsonArray in string format + * @return String value + */ + public String toString() { + if (ja!=null) { + return mycodes.repair_json(ja.toString()); + } + + return ""; + } + + /** + * Get JsonArray in byte array format + * @return byte array + */ + public byte[] toBytes() { + return toString().getBytes(); + } + + /** + * Get JsonArray in pretty string format + * @param indentvalue how many space to indent string + * @return String value + */ + public String ToStringWithIndent(int indentvalue) { + if (ja!=null) { + + try { + return mycodes.repair_json(ja.toString(indentvalue)) ; + } catch (JSONException e) { + if (BA.debugMode) BA.Log("Exception on ToStringWithIndent, Msg : "+e.getMessage() ); + } + } + return ""; + } + + /** + * Get JsonArray in pretty byte array format + * @param indentvalue how many space to indent string + * @return byte array + */ + public byte[] ToBytesWithIndent(int indentvalue) { + return ToStringWithIndent(indentvalue).getBytes(); + } + + /** + * Write JsonArray to a File + * @param directory : directory path + * @param filename : filename + * @return true if can be written + */ + public boolean WriteToFile(String directory, String filename) { + if (HasSomeValue()) { + try { + Writer ww = new FileWriter(File.Combine(directory, filename)); + ww.write(toString()); + ww.close(); + return true; + } catch (IOException e) { + if (BA.debugMode) BA.Log("Unable to WriteToFile, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Write JsonArray to a file with indentation for pretty string + * @param directory : directory path + * @param filename : filename + * @param indentvalue : how many spaces added for indentation + * @return true if can be written + */ + public boolean WriteToFileWithIndent(String directory, String filename, int indentvalue) { + if (HasSomeValue()) { + try { + Writer ww = new FileWriter(File.Combine(directory, filename)); + ww.write(ToStringWithIndent(indentvalue)); + ww.close(); + return true; + }catch (IOException e) { + if (BA.debugMode) BA.Log("Unable to WriteToFileWithIndent, Msg : "+e.getMessage()); + } + } + return false; + } + + /** + * Get an Object at index + * @param index : zero-based index + * @return null if index not found + */ + public Object Get(int index) { + if (HasSomeValue()) { + if (index>=0 && index=0 && index=0 && index=0 && index=0 && index=0 && index=0 && index=0 && index=0 && index=0 && index0) { + ja.put(value.getObject()); + return true; + } + } + } + } + return false; + } + + @SuppressWarnings("unchecked") + /** + * Get a B4X List at index + * @param index : zero-based index + * @param defaultvalue : if index not found, will return this value + * @return List value + */ + public List GetList(int index, List defaultvalue) { + if (HasSomeValue()) { + if (index>=0 && index) { + java.util.List mm = (java.util.List) xx; + mm.forEach((vv)->{ + result.Add(vv); + }); + } else { + result.Add(xx); + } + + } catch(JSONException e) { + BA.Log("Unable to GetList, Msg : "+e.getMessage()); + } + + } + } + return defaultvalue; + } + + /** + * Convert to B4X List + * @return List + */ + public List toList() { + List result = new List(); + result.Initialize(); + if (HasSomeValue()) { + for (int ii = 0; ii < ja.length(); ii++) { + try { + Object xx = ja.get(ii); + result.Add(xx); + } catch (JSONException e) { + if (BA.debugMode) BA.Log("Failed in toList, Msg : " + e.getMessage()); + } + } + } + return result; + + } + + /** + * Append a boolean value to current JsonArray + * @param value value to append + * @return current JsonArray + */ + public JsonArray Add_Boolean(boolean value) { + PutBoolean(value); + return this; + } + + /** + * Append a double value to current JsonArray + * @param value value to append + * @return current JsonArray + */ + public JsonArray Add_Double(double value) { + PutDouble(value); + return this; + } + + /** + * Append an int value to current JsonArray + * + * @param value value to append + * @return current JsonArray + */ + public JsonArray Add_Int(int value) { + PutInt(value); + return this; + } + + /** + * Append a long value to current JsonArray + * + * @param value value to append + * @return current JsonArray + */ + public JsonArray Add_Long(long value) { + PutLong(value); + return this; + } + + /** + * Append a String value to current JsonArray + * + * @param value value to append + * @return current JsonArray + */ + public JsonArray Add_String(String value) { + PutString(value); + return this; + } + + /** + * Append a JsonObject value to current JsonArray + * + * @param value value to append + * @return current JsonArray + */ + public JsonArray Add_JsonObject(JsonObject value) { + PutJsonObject(value); + return this; + } + + /** + * Append a JsonArray value to current JsonArray + * @param value value to append + * @return current JsonArray + */ + public JsonArray Add_JsonArray(JsonArray value) { + PutJsonArray(value); + return this; + } + + /** + * Append a List value to current JsonArray + * + * @param value value to append + * @return current JsonArray + */ + public JsonArray Add_List(List value) { + PutList(value); + return this; + } + + /** + * Append a String array to current JsonArray + * + * @param value value to append + * @return current JsonArray + */ + public JsonArray Add_ArrayString(String[] value) { + FromArrayString(value); + return this; + } + + +} diff --git a/src/androgpio/customsocket/JsonObject.java b/src/androgpio/customsocket/JsonObject.java new file mode 100644 index 0000000..3d1353d --- /dev/null +++ b/src/androgpio/customsocket/JsonObject.java @@ -0,0 +1,858 @@ +package androgpio.customsocket; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Iterator; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.collections.Map; +import anywheresoftware.b4a.objects.collections.Map.MyMap; +import anywheresoftware.b4a.objects.streams.File; + +@BA.ShortName("JsonObject") +/** + * JsonObject is almost like a Map + * it contain key and value pair + * used mainly in SocketIO javascript + * + * Source : https://github.com/stleary/JSON-java + * @author rdkartono + * + */ +public class JsonObject { + private JSONObject jo; + + public JsonObject() { + jo = null; + + } + + @BA.Hide + public JsonObject(JSONObject value) { + jo = value; + } + + + @BA.Hide + /** + * Get Native JSONObject object from org.json java + * @return JSONObject + */ + public JSONObject getObject() { + return jo; + } + + @BA.Hide + /** + * Set Native JSONObject + * @param vv : other value + */ + public void setObject(JSONObject vv) { + jo = vv; + } + + /** + * Initialize empty JsonObject + */ + public void InitializeEmpty() { + jo = new JSONObject(); + } + + /** + * Initialize JsonObject from B4X Map + * @param value : B4X Map + * @return true if value can be translated to JsonObject + */ + public boolean InitializeFromMap(Map value) { + if (value instanceof Map) { + if (value.IsInitialized()) { + if (value.getSize()>0) { + jo = new JSONObject(value.getObject()); + return (jo.length()>0); // kalau empty, return false. kalau gak empty return true + } + } + } + return false; + } + + /** + * Initialize JsonObject from JSON String + * @param value : String + * @return true if value can be translated to JsonObject + */ + public boolean InitializeFromString(String value) { + if (mycodes.valid_string(value)) { + try { + jo = new JSONObject(value); + return (jo.length()>0); + } catch(JSONException e) { + if (BA.debugMode) BA.Log("Unable to Initialize JsonObject from String, Msg : "+e.getMessage()); + } + } + + return false; + } + + /** + * Initialize JsonObject from a file containing plain string + * Reading result from file, will be used in InitializeFromString function + * @param directory : directory path + * @param filename : filename + * @return true if value can be translated to JsonObject + */ + public boolean InitializeFromFile(String directory, String filename) { + try { + if (File.Exists(directory, filename)) { + String value = File.ReadString(directory, filename); + return InitializeFromString(value); + } + } catch (IOException e) { + if (BA.debugMode) BA.Log("InitializeFromFile failed, Msg : "+e.getMessage()); + } + return false; + } + + /*** + * Initialize JsonObject from another JsonObject . + * @param othervalue : other JsonObject as copy source + * @param keys : array of string, containing keys to copy from other JsonObject + * @return true if othervalue can be copied + */ + public boolean InitializeFromOtherJsonObject(JsonObject othervalue, String... keys) { + if (keys!=null) { + if (keys.length>0) { + if (othervalue instanceof JsonObject) { + if (othervalue.HasSomeValue()) { + try { + jo = new JSONObject(othervalue.getObject(), keys); + return true; + } catch (JSONException e) { + if (BA.debugMode) BA.Log("InitializeFromOtherJsonObject failed, msg: "+e.getMessage()); + } + + } + } + } + } + return false; + } + + /** + * Check if JsonObject is initialized + * @return true if inited + */ + public boolean IsInitialized() { + if (jo instanceof JSONObject) { + return true; + } + return false; + } + + /** + * Check if JsonObject has been initialized and has some value + * @return true if has value + */ + public boolean HasSomeValue() { + if (jo instanceof JSONObject) { + return (jo.length()>0); + } + return false; + } + + /** + * Get List of Keys in this JsonObject + * @return B4X List + */ + public List GetListofKeys() { + List result = new List(); + result.Initialize(); + if (HasSomeValue()) { + Iterator iter = jo.keys(); + iter.forEachRemaining(value->{ + if (value instanceof String) { + if (!value.isEmpty()) { + result.Add(value); + } + } + + }); + } + return result; + } + + /** + * Check if this JsonObject contain spesific key + * @param value : String key to search + * @return true if it has + */ + public boolean ContainKey(String value) { + if (mycodes.valid_string(value)) { + if (HasSomeValue()) { + return jo.has(value); + } + } + return false; + } + + /** + * Get JsonObject size + * @return -1 if not initialized yet, 0 if empty, positive numbers if has value + */ + public int Size() { + if (jo instanceof JSONObject) { + return jo.length(); + } + return -1; + } + + /** + * Clear JsonObject contents and keys + * @return true if can be cleared + */ + public boolean Clear() { + if (IsInitialized()) { + while(jo.length()>0) { + jo.remove(jo.keys().next()); + } + return true; + } + return false; + } + + /** + * Remove a content with specific key + * @param key : String key to remove + * @return Object removed from JsonObject, or null if not available or not initialized + */ + public Object Remove(String key) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + return jo.remove(key); + } + } + + } + return null; + } + + + + /** + * Get JsonObject key-value in string + * @return empty string if JsonObject not initialized, or is empty + */ + public String toString() { + if (jo!=null) { + return mycodes.repair_json( jo.toString()); + } + return ""; + } + + /** + * Getet JsonObject key-value in byte array + * @return empty byte array if JsonObject not initialized or is empty + */ + public byte[] toBytes() { + return toString().getBytes(); + } + + /** + * Get JsonObject key-value in pretty string + * @param indentvalue : how many spaces for indentation to be added + * @return empty string if JsonObject not initialized or is empty + */ + public String ToStringWithIndent(int indentvalue) { + + if (jo!=null) { + try { + return mycodes.repair_json( jo.toString(indentvalue)); + } catch (JSONException e) { + if (BA.debugMode) BA.Log("ToStringWithIndent failed, msg : "+e.getMessage()); + } + } + return ""; + } + + /** + * Get JsonObject key-value in pretty byte array + * @param indentvalue : how many spaces for indentation to be added + * @return empty byte array if JsonObject not initialized or is empty + */ + public byte[] ToBytesWithIndent(int indentvalue) { + return ToStringWithIndent(indentvalue).getBytes(); + } + + /** + * Write JsonObject to a file , in plain string value + * @param directory : directory path + * @param filename : filename + * @return true if write operation succesful + */ + public boolean WriteToFile(String directory, String filename) { + if (HasSomeValue()) { + try { + Writer ww = new FileWriter(File.Combine(directory, filename)); + ww.write(toString()); + ww.close(); + return true; + } catch(IOException e) { + if (BA.debugMode) BA.Log("WriteToFile failed, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Write JsonObject to a file, in plain string value + * @param directory : directory path + * @param filename : filename + * @param indentvalue : how many spaces for indentation to be added + * @return true if write operation succesful + */ + public boolean WriteToFileWithIndent(String directory, String filename, int indentvalue) { + if (HasSomeValue()) { + + try { + Writer ww = new FileWriter(File.Combine(directory, filename)); + ww.write(ToStringWithIndent(indentvalue)); + ww.close(); + return true; + } catch (IOException e) { + if (BA.debugMode) BA.Log("WriteToFileWithIndent failed, Msg : "+e.getMessage()); + } + } + return false; + } + + /** + * Put a boolean value in JsonObject with specific key + * @param key : String key + * @param value : boolean value + * @return true if can be added + */ + public boolean PutBoolean(String key, boolean value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + if (BA.debugMode) BA.Log("Unable to PutBoolean, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Put a double value in JsonObject with specific key + * @param key : String key + * @param value : double value + * @return true if can be added + */ + public boolean PutDouble(String key, double value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + if (BA.debugMode) BA.Log("Unable to PutDouble, Msg : "+e.getMessage()); + } + } + } + return false; + } + + + + /** + * Put an Int value in JsonObject with specific key + * @param key : String key + * @param value : Int value + * @return true if can be added + */ + public boolean PutInt(String key, int value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + if (BA.debugMode) BA.Log("Unable to PutInt, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Put a long value in JsonObject with specific key + * @param key : String key + * @param value : long value + * @return true if can be added + */ + public boolean PutLong(String key, long value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + if (BA.debugMode) BA.Log("Unable to PutLong, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Put an Object value in JsonObject with specific key + * @param key : String key + * @param value : Object value + * @return true if can be added + */ + public boolean PutObject(String key, Object value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + if (BA.debugMode) BA.Log("Unable to PutObject, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Put a String value in JsonObject with specific key + * @param key : String key + * @param value : String value + * @return true if can be added + */ + public boolean PutString(String key, String value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + if (BA.debugMode) BA.Log("Unable to PutString, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Put a B4X Map value to JsonObject with specific kety + * @param key : String key + * @param value : B4X Map value + * @return true if can be added + */ + public boolean PutMap(String key, Map value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + if (value!=null && value.IsInitialized()) { + MyMap obj = (MyMap) value.getObject(); + if (obj instanceof MyMap) { + try { + + jo.put(key, obj); + return true; + } catch(JSONException | NullPointerException e) { + if (BA.debugMode) BA.Log("Unable to PutMap, Msg : "+e.getMessage()); + } + } + } + } + } + return false; + } + + /** + * Put a B4X List value to JsonObject with specific key + * @param key : String key + * @param value : B4X List value + * @return true if can be added + */ + public boolean PutList(String key, List value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + if (value != null && value.IsInitialized()) { + try { + jo.put(key, value.getObject()); + return true; + } catch (JSONException | NullPointerException e) { + if (BA.debugMode) + BA.Log("Unable to PutList, Msg : " + e.getMessage()); + } + } + } + } + return false; + } + + /** + * Put other JsonObject to this JsonObject with specific key + * @param key : String key + * @param value : other JsonObject + * @return true if can be added + */ + public boolean PutJsonObject(String key, JsonObject value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + if (value != null && value.IsInitialized()) { + try { + jo.put(key, value.getObject()); + return true; + }catch(JSONException | NullPointerException e) { + if (BA.debugMode) BA.Log("Unable to PutJsonObject, Msg : "+e.getMessage()); + } + } + } + } + return false; + } + + /** + * Put a JsonArray value to JsonObject with specific key + * @param key : String key + * @param value : JsonArray value + * @return true if can be added + */ + public boolean PutJsonArray(String key, JsonArray value) { + if (IsInitialized()) { + if (mycodes.valid_string(key)) { + if (value!=null && value.IsInitialized()) { + try { + jo.put(key, value.getObject()); + return true; + } catch (JSONException e) { + if (BA.debugMode) BA.Log("Unable to PutJsonArray , Msg : "+e.getMessage()); + } + } + } + } + return false; + } + + /** + * Check if this JsonObject is similar key-value with other JsonObject + * @param othervalue : other JsonObject to compare + * @return true if similar + */ + @SuppressWarnings("unlikely-arg-type") + public boolean IsSimilar(JsonObject othervalue) { + if (IsInitialized()) { + jo.equals(othervalue); + + } + return false; + } + + /** + * Get a String value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return String value + */ + public String GetStringValue(String key, String defaultvalue) { + if (ContainKey(key)) { + try { + String value = jo.getString(key) ; + return value; + } catch(JSONException e) { + if (BA.debugMode) BA.Log("Unable to GetStringValue, Msg : "+e.getMessage()); + } + + } + return defaultvalue; + } + + /** + * Get a Long value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return long value + */ + public long GetLongValue(String key, long defaultvalue) { + if (ContainKey(key)) { + try { + long value = jo.getLong(key); + return value; + } catch(JSONException e) { + if (BA.debugMode) BA.Log("Unable to GetLongValue, Msg : "+e.getMessage()); + } + + } + return defaultvalue; + } + + public JsonArray GetJsonArray(String key, JsonArray defaultvalue) { + if (ContainKey(key)) { + try { + Object xx = jo.get(key); + if (xx instanceof JsonArray) { + return (JsonArray) xx; + } else if (xx instanceof JSONArray) { + return new JsonArray((JSONArray) xx); + } else { + if (BA.debugMode) + BA.Log("GetJsonArray key : " + key + " is not a JsonArray"); + } + + } catch (JSONException e) { + if (BA.debugMode) + BA.Log("Unable to GetJsonArray, Msg : " + e.getMessage()); + } + + } + return defaultvalue; + } + + /** + * Get a JsonObject value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return JsonObject value + */ + public JsonObject GetJsonObject(String key, JsonObject defaultvalue) { + if (ContainKey(key)) { + try { + Object xx = jo.get(key); + if (xx instanceof JsonObject) { + return (JsonObject) xx; + } else if (xx instanceof JSONObject) { + return new JsonObject((JSONObject) xx); + } else { + if (BA.debugMode) BA.Log("GetJsonObject key : "+key+" is not a JsonObject"); + } + + } catch(JSONException e) { + if (BA.debugMode) BA.Log("Unable to GetJsonObject, Msg : "+e.getMessage()); + } + + } + return defaultvalue; + } + + /** + * Get an int value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return int value + */ + public int GetInt(String key, int defaultvalue) { + if (ContainKey(key)) { + try { + int value = jo.getInt(key); + return value; + } catch(JSONException e) { + if (BA.debugMode) BA.Log("Unable to GetInt, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + + + /** + * Get an double value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return double value + */ + public double GetDouble(String key, double defaultvalue) { + if (ContainKey(key)) { + try { + double value = jo.getDouble(key); + return value; + } catch(JSONException e) { + if (BA.debugMode) BA.Log("Unable to GetDouble, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Get an boolean value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return boolean value + */ + public boolean GetBoolean(String key, boolean defaultvalue) { + if (ContainKey(key)) { + try { + boolean value = jo.getBoolean(key); + return value; + } catch(JSONException e) { + if (BA.debugMode) BA.Log("Unable to GetBoolean, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Get an Object value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return Object value + */ + public Object GetObject(String key, Object defaultvalue) { + if (ContainKey(key)) { + try { + Object value = jo.get(key); + return value; + } catch(JSONException e) { + BA.Log("Unable to GetObject, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + public Map toMap() { + Map m = new Map(); + m.Initialize(); + if (HasSomeValue()) { + + Iterator iter = jo.keys(); + iter.forEachRemaining(key->{ + if (key instanceof String) { + if (!key.isEmpty()) { + try { + m.Put(key, jo.get(key)); + } catch (JSONException e) { + if (BA.debugMode) BA.Log("toMap failed, Msg : "+e.getMessage()); + } + } + } + + }); + } + return m; + } + + /** + * Add a boolean value to JsonObject with specific key + * @param key : String key + * @param value : boolean value to add + * @return current JsonObject + */ + public JsonObject Add_Boolean(String key, boolean value) { + PutBoolean(key, value); + return this; + } + + /** + * Add a double value to JsonObject with specific key + * @param key : String key + * @param value : double value to add + * @return current JsonObject + */ + public JsonObject Add_Double(String key, double value) { + PutDouble(key, value); + return this; + } + + /** + * Add an int value to JsonObject with specific key + * @param key : String key + * @param value : int value to add + * @return current JsonObject + */ + public JsonObject Add_Int(String key, int value) { + PutInt(key, value); + return this; + } + + /** + * Add a long value to JsonObject with specific key + * @param key : String key + * @param value : long value to add + * @return current JsonObject + */ + public JsonObject Add_Long(String key, long value) { + PutLong(key, value); + return this; + } + + /** + * Add an Object value to JsonObject with specific key + * @param key : String key + * @param value : Object value to add + * @return current JsonObject + */ + public JsonObject Add_Object(String key, Object value) { + PutObject(key, value); + return this; + } + + /** + * Add a String value to JsonObject with specific key + * @param key : String key + * @param value : String value to add + * @return current JsonObject + */ + public JsonObject Add_String(String key, String value) { + PutString(key, value); + return this; + } + + /** + * Add a B4X Map value to JsonObject with specific key + * @param key : String key + * @param value : B4X Map value to add + * @return current JsonObject + */ + public JsonObject Add_Map(String key, Map value) { + PutMap(key, value); + return this; + } + + /** + * Add a B4X List value to JsonObject with specific key + * @param key : String key + * @param value : B4X List value to add + * @return current JsonObject + */ + public JsonObject Add_List(String key, List value) { + PutList(key, value); + return this; + } + + /** + * Add another JsonObject to this JsonObject with specific key + * @param key : String key + * @param value : other JsonObject to add + * @return current JsonObject + */ + public JsonObject Add_JsonObject(String key, JsonObject value) { + PutJsonObject(key, value); + return this; + } + + /** + * Add another JsonArray to this JsonObject with specific key + * @param key : String key + * @param value : JsonArray to add + * @return current JsonObject + */ + public JsonObject Add_JsonArray(String key, JsonArray value) { + PutJsonArray(key, value); + return this; + } + + +} diff --git a/src/androgpio/customsocket/NTPClient.java b/src/androgpio/customsocket/NTPClient.java new file mode 100644 index 0000000..a6f8250 --- /dev/null +++ b/src/androgpio/customsocket/NTPClient.java @@ -0,0 +1,132 @@ +package androgpio.customsocket; +import java.io.IOException; +import java.net.InetAddress; +import java.util.concurrent.Callable; + +import org.apache.commons.net.ntp.NTPUDPClient; +import org.apache.commons.net.ntp.TimeInfo; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; + +@BA.ShortName("NTPClient") +@BA.Events(values= { + "log(msg as string)", + "ntpresult(host as string, success as boolean, timeinfo as NTPInfo)" +}) +@BA.DependsOn(values = { "commons-net-3.10.0" }) +@BA.Permissions(values = { "android.permission.INTERNET" }) +public class NTPClient { + + private BA ba; + private String eventname; + private Object Me; + private boolean need_log_event = false; + + private NTPUDPClient client; + + /** + * Initialize NTP Client + * Implement Network Time Protocol (NTP) RFC 1305 and Simple Network Time Protocol (SNTP) RFC-2030 + * @param eventname event name + */ + public void Initialize(BA ba, String eventname) { + this.ba = ba; + this.eventname = eventname; + Me = this; + check_events(); + client = new NTPUDPClient(); + } + + private void check_events() { + if (ba!=null) { + if (mycodes.valid_string(eventname)) { + need_log_event = ba.subExists(eventname+"_log"); + } + } + } + + /** + * Get NTP Protocol Version Number that client sets on request packet that is sent to remote host + * Example 3 = NTPv3, 4 = NTPv4, etc + * @return version number + */ + public int getNTPVersion() { + return client.getVersion(); + } + + /** + * Set NTP Protocol Version Number that client sets on request packet that is sent to remote host + * @param version example 3 = NTPv3, 4 = NTPv4, etc + */ + public void setNTPVersion(int version) { + client.setVersion(version); + } + + /** + * Get time information from specified NTP Server, using default Port + * will raise event ntpresult(host as string, success as boolean, timeinfo as NTPInfo) + * if success is false, timeinfo will be null + * @param host NTP Server Host + * @return Object to use in Wait For + */ + public Object GetTime(BA bax, String host) { + Object sender = new Object(); + BA.runAsync(bax, sender, eventname+"_ntpresult", new Object[] {host, false, null}, new Callable() { + + @Override + public Object[] call() { + boolean success = false; + NTPInfo ntpi = null; + try { + TimeInfo info = client.getTime(InetAddress.getByName(host)); + ntpi = new NTPInfo(info); + success = true; + } catch (IOException e) { + raise_log("GetTime exception: "+e.getMessage()); + } + return new Object[] {host, success, ntpi}; + } + + }); + return sender; + } + + /** + * Get time information from specified NTP Server, using specified Port + * Will raise event ntpresult(host as string, success as boolean, timeinfo as NTPInfo) + * @param host NTP Server Host + * @param port NTP Server Port + * @return Object to use in Wait For + */ + public Object GetTimeWithPort(BA bax, String host, int port) { + Object sender = new Object(); + BA.runAsync(bax, sender, eventname + "_ntpresult", new Object[] { host, false, null }, + new Callable() { + + @Override + public Object[] call() { + boolean success = false; + NTPInfo ntpi = null; + try { + TimeInfo info = client.getTime(InetAddress.getByName(host), port); + ntpi = new NTPInfo(info); + success = true; + } catch (IOException e) { + raise_log("GetTimeWithPort exception: " + e.getMessage()); + } + return new Object[] { host, success, ntpi }; + } + + }); + return sender; + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, eventname+"_log", false, new Object[] {msg}); + } + + + + +} diff --git a/src/androgpio/customsocket/NTPInfo.java b/src/androgpio/customsocket/NTPInfo.java new file mode 100644 index 0000000..2a5d77c --- /dev/null +++ b/src/androgpio/customsocket/NTPInfo.java @@ -0,0 +1,74 @@ +package androgpio.customsocket; +import org.apache.commons.net.ntp.TimeInfo; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("NTPInfo") +public class NTPInfo { + // Source : https://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/ntp/TimeInfo.html + private final TimeInfo ti; + + public NTPInfo() { + this.ti = null; + } + + public NTPInfo(TimeInfo ti) { + this.ti = ti; + + if (this.ti != null) this.ti.computeDetails(); + } + + /** + * Check if NTPInfo is valid + * @return true if valid + */ + public boolean isValid() { + if (ti!=null) { + if (ti.getOffset()!=null) { + return true; + } + } + return false; + } + + /** + * Returns time at which time message packet was received by local machine + * So it is local machine time, not remote host time + * @return 0 if not valid + */ + public long getReturnTime() { + if (ti!=null) { + return ti.getReturnTime(); + } + return 0; + } + + /** + * Get clock offset needed to adjust local clock to match remote clock + * Value is in milliseconds, compared with local machine Time in milliseconds + * To correct local machine time, add local machine Time (in milliseconds) with this value, and convert to DateTime + * @return 0 if not valid + */ + public long getOffset() { + if (ti != null) { + // return nya Long Object, bukan primitive long, jadi bisa null + Long x = ti.getOffset(); + if (x != null) return x.longValue(); + } + return 0; + } + + /** + * Get round-trip network delay. + * Value in milliseconds + * @return 0 if not valid + */ + public long getDelay() { + if (ti!=null) { + // return nya Long Object, bukan primitive long, jadi bisa null + Long x = ti.getDelay(); + if (x != null) return x.longValue(); + } + return 0; + } +} diff --git a/src/androgpio/customsocket/SocketIoObject.java b/src/androgpio/customsocket/SocketIoObject.java new file mode 100644 index 0000000..4465596 --- /dev/null +++ b/src/androgpio/customsocket/SocketIoObject.java @@ -0,0 +1,364 @@ +package androgpio.customsocket; + + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.json.JSONArray; +import org.json.JSONObject; + + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; + + +@BA.ShortName("SocketIoObject") +public class SocketIoObject { + private String _event; + private Object _value; + private Object _reply; + private int _valuecount; + private int _ackindex; + + private AtomicBoolean isnotified = new AtomicBoolean(false); + + public SocketIoObject() { + _event = ""; + _value = null; + _reply = null; + _valuecount = -1; + _ackindex = -1; + } + + @BA.Hide + public void setEvent(String event) { + _event = event; + } + + public String getEvent() { + return _event; + } + + public void setValue(Object value) { + _value = value; + } + + public Object getValue() { + return _value; + } + + public String ValueToString() { + if (_value!=null) { + if (_value instanceof JsonObject) { + return ((JsonObject) _value).toString(); + } else if (_value instanceof JsonArray) { + return ((JsonArray) _value).toString(); + } else if (_value instanceof JSONObject) { + return ((JSONObject) _value).toString(); + } else if (_value instanceof JSONArray) { + return ((JSONArray) _value).toString(); + } else return String.valueOf(_value); + } + return "null"; + } + + public void setReply(Object value) { + _reply = value; + } + + /** + * if NeedReply is true, this method should be called after setting the reply + * value + */ + public void Notify() { + synchronized(isnotified) { + isnotified.set(true); + isnotified.notify(); + } + } + + @BA.Hide + public void Wait(int value) { + // value = 0 , wait forever, other value, wait for value milliseconds + int xx =0; + synchronized (isnotified) { + isnotified.set(false); + while(!isnotified.get()) { + try { + isnotified.wait(value<1 ? 5000: value); + } catch (InterruptedException e) { + xx+=1; + BA.Log("InterruptException ke "+xx ); + } + } + + + + } + } + + public Object getReply() { + return _reply; + } + + public String ReplyToString() { + if (_reply != null) { + if (_reply instanceof JsonObject) { + return ((JsonObject) _reply).toString(); + } else if (_reply instanceof JsonArray) { + return ((JsonArray) _reply).toString(); + } else if (_reply instanceof JSONObject) { + return ((JSONObject) _reply).toString(); + } else if (_reply instanceof JSONArray) { + return ((JSONArray) _reply).toString(); + } else + return String.valueOf(_reply); + } + return "null"; + } + + + + @BA.Hide + public void setValueCount(int value) { + _valuecount = value; + } + + + public int getValueCount() { + return _valuecount; + } + + public boolean HaveValue() { + return _valuecount > 0; + } + + @BA.Hide + public void setAckIndex(int value) { + _ackindex = value; + } + + @BA.Hide + public int getAckIndex() { + return _ackindex; + } + + /** + * Check if a reply is expected + * @return true if a reply is expected + */ + public boolean NeedReply() { + return _ackindex > -1; + } + + /** + * Get Value Object as JsonObject + * @return null if value not a JsonObject + */ + public JsonObject ValueToJsonObject() { + if (_value!=null) { + if (_value instanceof JsonObject) { + return (JsonObject) _value; + + } else if (_value instanceof JSONObject) { + return new JsonObject((JSONObject) _value); + } + } + return null; + } + + /** + * Get Reply Object as JsonObject + * + * @return null if reply not a JsonObject + */ + public JsonObject ReplyToJsonObject() { + if (_reply != null) { + if (_reply instanceof JsonObject) { + return (JsonObject) _reply; + } else if (_reply instanceof JSONObject) { + return new JsonObject((JSONObject) _reply); + } + } + return null; + } + + /** + * Get Value Object as JsonArray + * + * @return null if value not a JsonArray + */ + public JsonArray ValueToJsonArray() { + if (_value!=null) { + if (_value instanceof JsonArray) { + return (JsonArray) _value; + } else if (_value instanceof JSONArray) { + return new JsonArray((JSONArray) _value); + } + } + return null; + } + + /** + * Get Reply Object as JsonArray + * + * @return null if reply not a JsonArray + */ + public JsonArray ReplyToJsonArray() { + if (_reply != null) { + if (_reply instanceof JsonArray) { + return (JsonArray) _reply; + } else if (_reply instanceof JSONArray) { + return new JsonArray((JSONArray) _reply); + } + } + return null; + } + + /** + * Check if Value is a JsonObject + * @return true if value is a JsonObject + */ + public boolean ValueIsJsonObject() { + + return ValueToJsonObject()!=null; + } + + /** + * Check if Reply is a JsonObject + * @return true if reply is a JsonObject + */ + public boolean ReplyIsJsonObject() { + return ReplyToJsonObject()!=null; + + } + + /** + * Check if Value is a JsonArray + * @return true if value is a JsonArray + */ + public boolean ValueIsJsonArray() { + return ValueToJsonArray()!=null; + } + + /** + * Check if Reply is a JsonArray + * @return true if reply is a JsonArray + */ + public boolean ReplyIsJsonArray() { + return ReplyToJsonArray()!=null; + } + + /** + * Check if Value is a String + * @return true if value is a String + */ + public boolean ValueIsString() { + return mycodes.ObjectisString(_value); + } + + /** + * Check if Value is a Boolean + * + * @return true if value is a Boolean + */ + public boolean ValueIsBoolean() { + return mycodes.ObjectisBoolean(_value); + } + + /** + * Check if Value is a Byte + * + * @return true if value is a Byte + */ + public boolean ValueIsByte() { + return mycodes.ObjectisByte(_value); + } + + /** + * Check if Value is a Integer + * + * @return true if value is a Integer + */ + public boolean ValueIsInteger() { + return mycodes.ObjectisInteger(_value); + } + + /** + * Check if Value is a Double + * + * @return true if value is a Double + */ + public boolean ValueIsDouble() { + return mycodes.ObjectisDouble(_value); + } + + /** + * Check if Value is a Float + * + * @return true if value is a Float + */ + public boolean ValueIsFloat() { + return mycodes.ObjectisFloat(_value); + } + + public boolean ValueIsLong() { + return mycodes.ObjectisLong(_value); + } + + + /** + * Check if Reply is a String + * @return true if reply is a String + */ + public boolean ReplyIsString() { + return mycodes.ObjectisString(_reply); + } + + /** + * Check if Reply is a Boolean + * @return true if reply is a Boolean + */ + public boolean ReplyIsBoolean() { + return mycodes.ObjectisBoolean(_reply); + } + + /** + * Check if Reply is a Byte + * @return true if reply is a Byte + */ + public boolean ReplyIsByte() { + return mycodes.ObjectisByte(_reply); + } + + /** + * Check if Reply is a Integer + * @return true if reply is a Integer + */ + public boolean ReplyIsInteger() { + return mycodes.ObjectisInteger(_reply); + } + + /** + * Check if Reply is a Double + * @return true if reply is a Double + */ + public boolean ReplyIsDouble() { + return mycodes.ObjectisDouble(_reply); + } + + /** + * Check if Reply is a Float + * @return true if reply is a Float + */ + public boolean ReplyIsFloat() { + return mycodes.ObjectisFloat(_reply); + } + + /** + * Check if Reply is a Long + * @return true if reply is a Long + */ + public boolean ReplyIsLong() { + return mycodes.ObjectisLong(_reply); + } +} \ No newline at end of file diff --git a/src/androgpio/customsocket/TCPSocket.java b/src/androgpio/customsocket/TCPSocket.java new file mode 100644 index 0000000..a23957a --- /dev/null +++ b/src/androgpio/customsocket/TCPSocket.java @@ -0,0 +1,773 @@ +package androgpio.customsocket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; + +import java.net.Socket; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Timer; +import java.util.TimerTask; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.keywords.DateTime; +import anywheresoftware.b4a.objects.collections.Map; +import anywheresoftware.b4a.objects.collections.Map.MyMap; +import anywheresoftware.b4a.randomaccessfile.B4XSerializator; + +@BA.ShortName("TCPSocket") +@BA.Events(values= { + "log(msg as string)", + "connected(targetip as string, targetport as int)", + "disconnected(targetip as string, targetport as int, duration as long)", + "newmapdata(value as Map)", + "newbytesdata(value() as byte)", + "newstringdata(value as String)", + "newintsdata(value() as int)", + +}) + + +public class TCPSocket { + private BA bax; + private Object caller; + private Object Me = this; + private String event; + private TcpSocketJavaEvent _javaevent = null; + + private boolean _inited = false; + private boolean need_log_event = false; + private boolean need_connected_event = false; + private boolean need_disconnected_event = false; + private boolean need_newmapdata_event = false; + private boolean need_newbytesdata_event = false; + private boolean need_newstringdata_event = false; + private boolean need_newintsdata_event = false; + + + private Socket _socket; + private ObjectOutputStream _output; + private ObjectInputStream _input; + private B4XSerializator _converter; + + private String _remoteip = ""; + private int _remoteport = 0; + private String _localip = ""; + private int _localport = 0; + + private long _connected_tick = 0; + + private int _txokcount = 0; + private long _txbytescount = 0; + private int _rxokcount = 0; + private long _rxbytescount = 0; + + private Timer onesecondtimer; + private int _rxtimercount; + private int _txtimercount; + + private String _mapkey = ""; + + public TCPSocket() { + // nothing to do here + _converter = new B4XSerializator(); + + } + + @BA.Hide + public TCPSocket(Socket newsocket, Object event) { + this(); + _javaevent = (TcpSocketJavaEvent) event; + Thread tx = new Thread(new tcpsocketrun(newsocket)); + tx.start(); + } + + /** + * Get / Set Identifier Key + * by default, identifier key is RemoteIPAddress:RemotePort + * if identifier key is empty, then TCPSocket has never been connected yet + * User may change IdentifierKey to any String value , and used it for matching Map + * @return key as string + */ + public String getIdentifierKey() { + return _mapkey; + } + + /** + * Get / Set Identifier Key + * by default, identifier key is RemoteIPAddress:RemotePort + * if identifier key is empty, then TCPSocket has never been connected yet + * User may change IdentifierKey to any String value , and used it for matching Map + * @param value as string + */ + public void setIdentifierKey(String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + if (_javaevent instanceof TcpSocketJavaEvent) { + _javaevent.renameidentifier(_mapkey, value); + } + _mapkey = value; + } + } + } + + /** + * Initialize TCPSocket events + * Necessary to call this, so event log, connected, disconnected will be called + * @param callerobject : caller object + * @param eventname : eventname + */ + public void InitializeEvents(BA ba, final Object callerobject, final String eventname) { + + bax = ba; + caller = callerobject; + event = eventname; + if (bax instanceof BA) { + if (caller != null) { + if (event instanceof String) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_connected_event = bax.subExists(event+"_connected"); + need_disconnected_event = bax.subExists(event+"_disconnected"); + need_newmapdata_event = bax.subExists(event+"_newmapdata"); + need_newbytesdata_event = bax.subExists(event+"_newbytesdata"); + need_newstringdata_event = bax.subExists(event+"_newstringdata"); + need_newintsdata_event = bax.subExists(event+"_newintsdata"); + + _inited = true; + } + } + } + } + + } + + /** + * Check if Events already Initialized for this TCPSocket + * @return true if already initialized + */ + public boolean EventsInitialized() { + return _inited; + } + + /** + * Check if TCPSocket is connected + * @return true if connected + */ + public boolean IsConnected() { + if (_socket instanceof Socket) { + if (!_socket.isClosed()) { + return _socket.isConnected(); + } + } + return false; + } + + /** + * Connect to Target Ip and Target Port + * Will raise Connected(remoteip as string, remoteport as int) event + * if TCPSocket connected, remoteip has ip address, and remoteport != 0; + * @param targetip : target ip + * @param port : target port + */ + public void ConnectTo(String targetip, int port) { + if (targetip instanceof String) { + if (!targetip.isEmpty()) { + if (port>0) { + try { + Socket trysock = new Socket(targetip, port); + Thread tx = new Thread(new tcpsocketrun(trysock)); + tx.start(); + return; + } catch (UnknownHostException e) { + raise_log_error("UnknownHostException on ConnectTo ["+targetip+":"+port+"]",e); + + } catch (IOException e) { + raise_log_error("IOException on ConnectTo ["+targetip+"]:["+port+"]",e); + } + } + } + } + raise_connected("",0); + } + + private class tcpsocketrun implements Runnable{ + private boolean validrun = false; + private ByteBuffer bytebuf ; + public tcpsocketrun(Socket xx) { + // reset + _remoteip = ""; + _remoteport = 0; + _localip = ""; + _localport = 0; + _connected_tick = 0; + _rxbytescount = 0; + _rxokcount = 0; + _txbytescount = 0; + _txokcount = 0; + _txtimercount = 0; + _rxtimercount = 0; + _mapkey = ""; + if (onesecondtimer instanceof Timer) { + onesecondtimer.cancel(); + } + onesecondtimer = null; + + try { + if (_socket instanceof Socket) Disconnect(); + _socket = xx; + ObjectOutputStream _out = new ObjectOutputStream(_socket.getOutputStream()); + ObjectInputStream _in = new ObjectInputStream(_socket.getInputStream()); + bytebuf = ByteBuffer.allocate(1024); + + _output = _out; + _input = _in; + + _remoteip = _socket.getInetAddress() == null ? "" : _socket.getInetAddress().getHostAddress(); + _localip = _socket.getLocalAddress() == null ? "" : _socket.getLocalAddress().getHostAddress(); + _remoteport = _socket.isClosed() ? 0 : _socket.getPort(); + _localport = _socket.getLocalPort(); + _mapkey = _remoteip+":"+String.valueOf(_remoteport); + validrun = true; + + } catch(IOException e) { + raise_log_error("IOException on tcpsocketrun",e); + } + + if (validrun) { + /// shutdownhook taruh di sini, buat auto close ketika socket connected + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + BA.Log("ShutdownHook on TCPSocket identifier="+_mapkey); + Disconnect(); + } + }); + } + + } + + public void run() { + if (validrun) { + raise_log("tcpsocketrun running"); + raise_connected(_socket.getInetAddress().getHostAddress(), _socket.getPort()); + _connected_tick = DateTime.getNow(); + // create new timer + onesecondtimer = new Timer(); + onesecondtimer.scheduleAtFixedRate(checktxrxtimercounter, 1000, 1000); + + while(true) { + // selama _input masih ObjectInputStream, coba looping lagi + // Kalau Disconnect dipanggil, _input akan jadi null + // maka while ini jadi break + + if (_input instanceof ObjectInputStream) { + int readcount = 0; + while(true) { + // coba ambil data di sini , loop terus sampe break + // kondisi terus, kalau readcount = buf.length, artinya bisa ambil data banyak + // mungkin masih ada data lagi setelahnya + // + // kondisi break, kalau readcount < buf.length, artinya buffernya aja gak sampe penuh + // berarti datanya cuma dikit + + byte[] buf = new byte[1024]; + + try { + readcount = _input.read(buf); // ngeblok di sini + } catch (IOException e) { + raise_log_error("IOException from input.read", e); + + // kalau gak online, connection closed + String msg = e.getMessage(); + + // Network disconnect + if (msg.equalsIgnoreCase("connection reset")) { + Disconnect(); // Disconnect di sini nutup _input, _output, dan _socket + break; // keluar dari while(true) dalam + } + break; + } + + if (readcount>0) { // perlu disimpen apa nggak + _rxtimercount = 0; // kalau berhasil terima something, reset jadi zero + if (readcount>bytebuf.remaining()) { + // gak cukup, gedein byte buffer + ByteBuffer newbytebuf = ByteBuffer.allocate(bytebuf.capacity()+readcount); + newbytebuf.put(bytebuf); + bytebuf = newbytebuf; // alokasikan ke sini + + } + + // sampe sini , size cukup + bytebuf.put(buf,0,readcount); + + // kalau readcount = 1024, artinya datanya cukup banyak, mungkin perlu baca lagi + // kalau readcount < 1024, artinya datanya sedikit, cukup sekali baca aja + if (readcount!=buf.length) break; + }; + + } + + if (bytebuf.position()>0) { + bytebuf.flip(); + byte[] totalbytes = new byte[bytebuf.remaining()]; + bytebuf.get(totalbytes); + //printbytes("TotalBytes",totalbytes); + try { + Object rxobject = _converter.ConvertBytesToObject(totalbytes); + if (rxobject instanceof MyMap) { + // MyMap adalah class asli Map Java , diwrapping oleh Erel + Map result = new Map(); // Map ini adalah B4X object + result.setObject((MyMap)rxobject); + raise_newmapdata(result); + } else if (rxobject instanceof String) { + String result = (String) rxobject; + raise_newstringdata(result); + } else if (rxobject instanceof ArrayList) { + ArrayList tempresult = (ArrayList) rxobject; + if (tempresult.size()>0) { + Object _firstobject = tempresult.get(0); + if (_firstobject instanceof Byte) { + byte[] result = new byte[tempresult.size()]; + for(int ii=0;ii0) { + for (int ii=0;ii0) { + try { + return WriteObject(value); + } catch (IOException e) { + raise_log_error("IOException on WriteMap",e); + Disconnect(); + } + } else raise_log("WriteMap canceled, value Size = 0"); + } else raise_log("WriteMap canceled, value is not initialized"); + } else raise_log("WriteMap canceled, value is not Map"); + return false; + } + + /** + * Write bytes array to TCPSocket + * @param value : bytes array + * @return true if can be sent + */ + public boolean WriteBytes(byte[] value) { + if (value instanceof byte[]) { + if (value.length>0) { + try { + ArrayList result = new ArrayList(); + for(int ii=0;ii0) { + try { + ArrayList result = new ArrayList(); + for(int ii=0;ii0) { + long now = DateTime.getNow(); + if ((now - _connected_tick)>0) { + return ((now - _connected_tick) / DateTime.TicksPerSecond); + } + } + return 0; + } + + /** + * Get Date and Time when the socket is connected + * String will formatted as DD-MM-YYYY HH:MM:SS + * @return empty string if not yet connected + */ + public String Connected_DateTime_String() { + if (_connected_tick>0) { + return mycodes.Tick_To_DDMMYYYY_HHMMSS(_connected_tick); + } + return ""; + } + + /** + * Get how many succesful Sending package + * @return zero to 2,147,483,647 + */ + public int getTX_OK_Counter() { + + return _txokcount; + } + + /** + * Get how many succesful Receiving package + * @return zero to 2,147,483,647 + */ + public int getRX_OK_Counter() { + return _rxokcount; + } + + /** + * Get how much bytes has been sucesfully sent + * @return value in bytes, from zero to 9,223,372,036,854,775,807 + */ + public long getTX_Bytes_Counter() { + return _txbytescount; + } + + /** + * Get how much bytes has been sucesfully received + * @return value in bytes, from zero to 9,223,372,036,854,775,807 + */ + public long getRX_Bytes_Counter() { + return _rxbytescount; + } + + /** + * TimerTask to execute, for checking txtimercounter and rxtimercounter + * this TimerTask will execute every 1000 ms + */ + private TimerTask checktxrxtimercounter = new TimerTask() { + + @Override + public void run() { + if (_txtimercount>5) { + raise_log("Not sending more than 5 seconds, sending [PING] to check connection"); + WriteString("[PING]"); // pancingan aja, gak penting hasilnya + } + + if (_rxtimercount>10) { + raise_log("Not receiving more than 10 seconds, connection have problem"); + Disconnect(); + } + } + + }; + + private void Close_ObjectOutputStream() { + if (_output instanceof ObjectOutputStream) { + try { + _output.close(); + raise_log("ObjectOutputStream closed"); + } catch (IOException e) { + raise_log_error("IOException on Close_ObjectOutputStream",e); + } + } + _output = null; + } + + private void Close_ObjectInputStream() { + if (_input instanceof ObjectInputStream) { + try { + _input.close(); + raise_log("ObjectInputStream closed"); + } catch (IOException e) { + raise_log_error("IOException on Close_ObjectInputStream",e); + } + } + _input = null; + } + + private void raise_log_error(String msg, Exception e) { + StringBuffer sbf = new StringBuffer(); + sbf.append(msg); + if (e instanceof Exception) { + if (e.getMessage() instanceof String) { + sbf.append("\r\n"); + sbf.append("Msg : "+e.getMessage()); + } + if (e.getCause() instanceof Throwable) { + sbf.append("\r\n"); + sbf.append(e.getCause()); + } + } + raise_log(sbf.toString()); + } + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_connected(String remoteip, int remoteport) { + if (need_connected_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_connected", false, new Object[] {remoteip, remoteport}); + } + } + + private void raise_disconnected(String remoteip, int remoteport) { + if (need_disconnected_event) { + long tt = 0; + if (_connected_tick > 0) { + tt = (DateTime.getNow() - _connected_tick) / DateTime.TicksPerSecond; + } + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_disconnected", false, new Object[] {remoteip,remoteport, tt / DateTime.TicksPerSecond }); + } + } + + private void raise_newmapdata(Map value) { + if (need_newmapdata_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newmapdata", false, new Object[] {value}); + } + } + + private void raise_newbytesdata(byte[] value) { + if (need_newbytesdata_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newbytesdata", false, new Object[] {value}); + } + } + + private void raise_newstringdata(String value) { + if (need_newstringdata_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newstringdata", false, new Object[] {value}); + } + } + + private void raise_newintsdata(int[] value) { + if (need_newintsdata_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newintsdata", false, new Object[] {value}); + } + } + +} diff --git a/src/androgpio/customsocket/TCPSocketServer.java b/src/androgpio/customsocket/TCPSocketServer.java new file mode 100644 index 0000000..1922a35 --- /dev/null +++ b/src/androgpio/customsocket/TCPSocketServer.java @@ -0,0 +1,380 @@ +package androgpio.customsocket; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("TCPSocketServer") +@BA.Events(values= { + "log(msg as string)", + "newtcpsocket(value as TCPSocket)", + "listeningstatus(islistening as boolean)", + "connectedclients(value as int, keys as List)" +}) +public class TCPSocketServer implements TcpSocketJavaEvent { + + private BA bax; + private Object caller; + private Object Me = this; + private String event; + private boolean _inited = false; + private boolean need_log_event = false; + private boolean need_newtcpsocket_event = false; + private boolean need_listeningstatus_event = false; + private boolean need_connectedclients_event = false; + private ServerSocket _server; + + private Map _socketmap = new Map(); + + /** + * Initialize TCP Socket Server + * @param callerobject : caller object + * @param eventname : eventname + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname; + if (bax instanceof BA) { + if (caller != null) { + if (event instanceof String) { + if (!event.isEmpty()) { + _inited = true; + need_log_event = bax.subExists(event+"_log"); + need_newtcpsocket_event = bax.subExists(event+"_newtcpsocket"); + need_listeningstatus_event = bax.subExists(event+"_listeningstatus"); + need_connectedclients_event = bax.subExists(event+"_connectedclients"); + } + } + } + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + BA.Log("ShutdownHook on TCPSocketServer at "+getLocalIPAddress()+":"+getLocalPort()); + StopListening(); + Disconnect_All_TCPSocket(); + } + }); + + _socketmap.Initialize(); + } + + /** + * Check if TCPSocketServer is initialized + * @return true if inited + */ + public boolean Initialized() { + return _inited; + } + + /** + * Stop Listening for Connections + */ + public void StopListening() { + if (_server instanceof ServerSocket) { + try { + _server.close(); + } catch (IOException e) { + raise_log_error("IOException on StopListening",e); + } + } + _server = null; + } + + /** + * Get Local IP Address used for listening + * @return empty string if failed + */ + public String getLocalIPAddress() { + if (_server instanceof ServerSocket) { + if (_server.getInetAddress()!=null) { + return _server.getInetAddress().getHostAddress(); + } + } + return ""; + } + + /** + * Get Local Port used for Listening + * @return zero if failed + */ + public int getLocalPort() { + if (_server instanceof ServerSocket) { + if (_server.getLocalPort()>0) { + return _server.getLocalPort(); + } + + } + return 0; + } + + /** + * Start Listening + * @param port : listen port + * @return true if success + */ + public boolean StartListening(int port) { + if (_server instanceof ServerSocket) StopListening(); + try { + ServerSocket newserver = new ServerSocket(port); + Thread tx = new Thread(new tcpsocketserverrun(newserver)); + tx.start(); + return true; + } catch (IOException e) { + raise_log_error("IOException on StartListening at port "+port,e); + } + return false; + } + + /** + * Get how many TCPSockets has been received + * @return value as integer + */ + public int getConnectedTCPSocket() { + return _socketmap.IsInitialized() ? _socketmap.getSize() : 0; + } + + /** + * Get List of IdentifierKey from all connected TCPSockets + * @return List of IdentifierKey, or empty List of none connected + */ + public List getListOfTCPSocketKeys() { + List result = new List(); + result.Initialize(); + if (_socketmap.IsInitialized()) { + if (_socketmap.getSize()>0) { + for(int ii=0;ii<_socketmap.getSize();ii++) { + result.Add((String)_socketmap.GetKeyAt(ii)); + } + } + } + return result; + } + + /** + * Disconnect all TCPSocket in Map + */ + public void Disconnect_All_TCPSocket() { + List result = getListOfTCPSocketKeys(); + if (result.getSize()>0) { + for(int ii=0;ii0) { + for (int ii=0;ii<_socketmap.getSize();ii++) { + String key = (String) _socketmap.GetKeyAt(ii); + if (key.startsWith(ipvalue)) { + Object xx = _socketmap.Get(key); + if (xx instanceof TCPSocket) { + return (TCPSocket)xx; + } + + } + } + } + } + } + return null; + } + + /** + * Disconnect TCPSocket and remove it from Map + * @param identifierkey : identifierkey in string + * @return true if identifierkey can be found + */ + public boolean Disconnect_TCPSocket(String identifierkey) { + if (identifierkey instanceof String) { + if (!identifierkey.isEmpty()) { + if (_socketmap.ContainsKey(identifierkey)) { + Object xx = _socketmap.Get(identifierkey); + if (xx instanceof TCPSocket) { + TCPSocket tcp = (TCPSocket)xx; + tcp.Disconnect(); + tcp = null; + } + xx = null; + _socketmap.Remove(identifierkey); + raise_connectedclients(_socketmap.getSize(), getListOfTCPSocketKeys()); + return true; + } + } + } + return false; + } + + /** + * Rename Map identifier from old_id to new_id, but retain the same TCPSocket object + * @param old_id : old identifier + * @param new_id : new identifier + * @return true if Map contain old_id and succesfully renamed, or false if otherwise + */ + public boolean Rename_Identifier(String old_id, String new_id) { + if (_socketmap instanceof Map) { + if (_socketmap.IsInitialized()) { + if (_socketmap.ContainsKey(old_id)) { + Object xx = _socketmap.Get(old_id); + if (xx instanceof TCPSocket) { + TCPSocket tcp = (TCPSocket) xx; + + _socketmap.Remove(old_id); + _socketmap.Put(new_id, tcp); + if (tcp.getIdentifierKey().equals(new_id)==false) { + tcp.setIdentifierKey(new_id); + } + return true; + } + } + } + } + return false; + } + + private class tcpsocketserverrun implements Runnable{ + private boolean validrun = false; + public tcpsocketserverrun(ServerSocket xx) { + _server = xx; + if (_server instanceof ServerSocket) { + if (!_server.isClosed()) { + validrun = true; + } + } + } + + public void run() { + if (validrun) { + raise_listeningstatus(true); + while(true) { + if (_server instanceof ServerSocket) { + if (!_server.isClosed()) { + try { + Socket newsocket = _server.accept(); + raise_log("New connection from "+newsocket.getInetAddress().getHostAddress()+":"+newsocket.getPort()+" to TCPSocketServer"); + TCPSocket newtcp = new TCPSocket(newsocket, Me); + + raise_newtcpsocket(newtcp); + } catch (IOException e) { + raise_log_error("IOException on server.accept", e); + } + } else break; + } else break; + } + + } + raise_listeningstatus(false); + } + } + + private void raise_log_error(String msg, Exception e) { + StringBuffer sbf = new StringBuffer(); + sbf.append(msg); + if (e instanceof Exception) { + if (e.getMessage() instanceof String) { + sbf.append("\r\n"); + sbf.append("Msg : "+e.getMessage()); + } + if (e.getCause() instanceof Throwable) { + sbf.append("\r\n"); + sbf.append(e.getCause()); + } + } + raise_log(sbf.toString()); + } + + private void raise_newtcpsocket(TCPSocket value) { + if (need_newtcpsocket_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newtcpsocket", false, new Object[] {value}); + } + } + + private void raise_log(String msg) { + if (need_log_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + } + + private void raise_listeningstatus(boolean value) { + if (need_listeningstatus_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_listeningstatus", false, new Object[] {value}); + } + } + + private void raise_connectedclients(int value, List keys) { + if (need_connectedclients_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_connectedclients", false, new Object[] {value, keys}); + } + } + + //////////////// Event from TcpSocketJavaEvent ///////////////////////// + @Override + @BA.Hide + public void newidentifierkey(String identifier, TCPSocket Sender) { + if (!_socketmap.IsInitialized()) _socketmap.Initialize(); + if (identifier instanceof String) { + if (!identifier.isEmpty()) { + Object prev_in_map = null; + if (identifier.contains(":")) { + // mengandung ip:port + // coba cek apakah ip nya pernah ada + String ipnya = identifier.substring(0, identifier.indexOf(":")); + prev_in_map = Have_TCPSocket_with_IPAddress(ipnya); + if (prev_in_map instanceof TCPSocket) { + TCPSocket oldsock = (TCPSocket) prev_in_map; + // ada + raise_log("Found another TCPSocket with same IP. ID 1 = "+identifier+", ID 2="+ oldsock.getIdentifierKey()); + _socketmap.Remove(oldsock.getIdentifierKey()); + raise_log("Removing Old TCPSocket with identifier="+oldsock.getIdentifierKey()); + _socketmap.Put(identifier, Sender); + raise_log("Save New TCPSocket with identifer="+Sender.getIdentifierKey()); + } + } else { + prev_in_map = _socketmap.Put(identifier, Sender); + raise_log("Save TCPSocket with identifier="+identifier+" to Map"); + } + + + if (prev_in_map != null) { + if (prev_in_map instanceof TCPSocket) { + TCPSocket prevsock = (TCPSocket) prev_in_map; + prevsock.Disconnect(); + prevsock = null; + raise_log("Disconnect old TCPSocket with the identifier="+identifier); + } + prev_in_map = null; + } + } + } + + + raise_connectedclients(_socketmap.getSize(), getListOfTCPSocketKeys()); + } + + @Override + @BA.Hide + public void disconnected(String identifier) { + Disconnect_TCPSocket(identifier); + } + + @Override + @BA.Hide + public void renameidentifier(String oldvalue, String newvalue) { + Rename_Identifier(oldvalue, newvalue); + + } +} diff --git a/src/androgpio/customsocket/TcpSocketJavaEvent.java b/src/androgpio/customsocket/TcpSocketJavaEvent.java new file mode 100644 index 0000000..fc58278 --- /dev/null +++ b/src/androgpio/customsocket/TcpSocketJavaEvent.java @@ -0,0 +1,8 @@ +package androgpio.customsocket; + +public interface TcpSocketJavaEvent { + public void newidentifierkey(String identifier, TCPSocket Sender); + public void disconnected(String identifier); + public void renameidentifier(String oldvalue, String newvalue); + +} diff --git a/src/androgpio/customsocket/jSocketIOClientV1.java b/src/androgpio/customsocket/jSocketIOClientV1.java new file mode 100644 index 0000000..c5f0911 --- /dev/null +++ b/src/androgpio/customsocket/jSocketIOClientV1.java @@ -0,0 +1,1227 @@ +package androgpio.customsocket; + +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.regex.Pattern; + +import com.google.gson.Gson; + +import androgpio.mycodes; +import io.socket.client.Ack; +import io.socket.client.AckWithTimeout; +import io.socket.client.IO; +import io.socket.client.Manager; +import io.socket.client.Socket; +import io.socket.client.SocketIOException; +import io.socket.emitter.Emitter; +import io.socket.engineio.client.EngineIOException; +import anywheresoftware.b4a.BA; + + + +@BA.ShortName("jSocketIOClientV1") +@BA.Events(values= { + "log(msg as string)", + "connected(socketid as string, remote as string)", + "disconnected(socketid as string, remote as string, reason as string)", + "message(socketid as string, remote as string, value as string)", + "reconnecting(uri as String, attempt as int)", + "reconnected(uri as string, attempt as int)", + "success(replyvalue as Object)", + "success(replyvalue as String)", + "success(replyvalue as byte[])", + "timeout(msg as string)" +}) + +@BA.DependsOn(values= {"socket.io-client-2.1.0","engine.io-client-2.1.0","okhttp-3.12.12","okio-1.15.0","gson-2.10.1"}) +@BA.Permissions(values= {"android.permission.INTERNET"}) + + +/** + * Ini adalah Socket.io client yang inisiatif konek ke Socket.io server + * butuh masukin android:usesCleartextTraffic="true" di AndroidManifest.xml + * Baca : https://socketio.github.io/socket.io-client-java/android.html + * @author rdkartono + * + */ +public class jSocketIOClientV1 { + + private final Object Me = this; + + private BA bax; + private Object caller; + private String event; + private boolean _inited = false; + private boolean need_log_event = false; + private boolean need_connected_event = false; + private boolean need_disconnected_event = false; + private boolean need_message_event = false; + private boolean need_reconnecting_event = false; + private boolean need_reconnected_event = false; + private Socket _client; + + private String _remote; + private String _id; + private Gson gson = new Gson(); + + + /** + * Initialize Events for Socket.IO Client + * This is client side Socket that to be use to connect with SocketIO Server + * @param callerobject : caller object + * @param eventname : event name + */ + public void Initialize(final BA ba, final Object callerobject, final String eventname) { + bax = ba; + caller = callerobject; + event = eventname; + + if (bax instanceof BA) { + if (caller != null) { + if (event instanceof String) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_connected_event = bax.subExists(event+"_connected"); + need_disconnected_event = bax.subExists(event+"_disconnected"); + need_message_event = bax.subExists(event+"_message"); + need_reconnecting_event = bax.subExists(event+"_reconnecting"); + need_reconnected_event = bax.subExists(event+"_reconnected"); + _inited = true; + } + } + } + } + + + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + if (BA.debugMode) BA.Log("ShutdownHook for SocketIOClient"); + Disconnect(); + } + }); + + _id = ""; + _remote = ""; + } + + /** + * Check if Socket.IO Client is initialized + * @return true if inited + */ + public boolean IsInitialized() { + return _inited; + } + + + + /** + * Connect to SocketIO Server + * Will raise connected event or disconnected event + * @param uri : valid uri string, example ws://192.168.31.2:8080 + * @param method : "polling", "websocket" + * @param events : array of string, containing event name that want to be intercepted as soon as possible + * @return true if socket can be created or false if parameters invalid + */ + public boolean ConnectTo(final String uri, String method, String...events ) { + //if (BA.debugMode) raise_log("ConnecTo will using socket.io-client protocol version = "+IO.protocol); + if (!mycodes.valid_string(method)) { + if (BA.debugMode) raise_log("ConnectTo, method is empty, will use polling"); + method = "polling"; + } + if (!_id.isEmpty()) { + if (BA.debugMode) raise_log("Already connected to "+_remote+", will disconnect first"); + Disconnect(); //kalau ID sudah ada, berarti dah connected. Kalau gitu, disconnect dulu + } + + if (uri instanceof String) { + if (!uri.isEmpty()) { + try { + IO.Options opts = new IO.Options(); + // pilihan "polling", "websocket" + opts.transports = new String[] { method }; + + + if (BA.debugMode) { + raise_log("Will connect to "+uri+" with method = "+method); + + } + + Socket newsock = IO.socket(uri, opts); + + Manager manager = newsock.io(); + + manager.on(Manager.EVENT_RECONNECT, (value)->{ + // fired upon a succesful reconnection + int attempt = (int)value[0]; + if (BA.debugMode) raise_log("Reconnected to "+uri+" after attempt number = "+attempt); + raise_reconnected(uri, attempt); + + }); + + manager.on(Manager.EVENT_RECONNECT_ATTEMPT, (value)->{ + // fired upon an attempt to reconnect + int attempt = (int)value[0]; + if (BA.debugMode) raise_log("Reconnect Attempt to "+uri+", attempt number = "+attempt); + raise_reconnecting(uri, attempt); + }); + + manager.on(Manager.EVENT_ERROR, (err)->{ + + EngineIOException error = (EngineIOException)err[0]; + if (BA.debugMode) raise_log("Error : "+String.valueOf(error.getMessage())); + }); + + manager.on(Manager.EVENT_RECONNECT_ERROR, (err)->{ + + SocketIOException error = (SocketIOException)err[0]; + if (BA.debugMode) raise_log("Reconnect Error : "+String.valueOf(error.getMessage())); + }); + + + + newsock.on(Socket.EVENT_CONNECT,(value)->{ + // di sini, gak ada value, tandanya value.length = 0 + + + + // kalau sebelumhya terkoneksi ke tempat lain, disconnect dulu + Disconnect(); + + _id = newsock.id(); + _remote = uri; + _client = newsock; + if (BA.debugMode) raise_log("Socket.io client url="+uri+" connected, id = "+_id); + + raise_connected(_id, _remote); + + // Tambah events + if (events!=null) { + if (events.length>0) { + for(String ev : events) { + if (!ev.isEmpty()) { + Add_Event(ev, BA.debugMode); + } + + } + } + } + }); + newsock.on(Socket.EVENT_CONNECT_ERROR, (value)->{ + // di sini, ada 1 value, tandanya value.length = 1 + // isinya keterangan kenapa gagal connect + + _remote = uri; + String _reason = ""; + if (value!=null) { + + if (value.length>0) { + _reason = String.valueOf(value[0]); + } + + } + + if (BA.debugMode) raise_log("Socket.io client url="+uri+" failed, reason = "+_reason); + + raise_disconnected(_id, _remote,_reason); + // Revisi 06062020, kalau connect error, disconnect saja + Disconnect(); + + }); + newsock.on(Socket.EVENT_DISCONNECT, (value)->{ + // di sini ada 1 value, tandanya value.length = 1 + // isinya keterangan kenapa disconnect + // disconnect artinya sudah pernah connect, beda dengan connect_error + //if (debugmode) printvaluedebug(Socket.EVENT_DISCONNECT, value); + + String _reason = ""; + _remote = uri; + if (value!=null) { + if (value.length>0) { + _reason = String.valueOf(value[0]); + } + } + if (BA.debugMode) raise_log("Socket.io client url="+uri+" disconnected, reason = "+_reason); + + raise_disconnected(_id, _remote,_reason); + // Revisi 06062020, kalau disconnect, langsung disconnect saja + Disconnect(); + + }); + + + + newsock.on("message", (value)->{ + + if (value!=null) { + if (value.length>0) { + String msg = String.valueOf(value[0]); + raise_message(_id, _remote, msg); + } + + } + }); + + + + + + + newsock.connect(); + return true; + } catch (URISyntaxException e) { + raise_log("ConnectTo failed, URISyntaxException for "+uri+", Msg : "+e.getMessage()); + } + + } else raise_log("ConnectTo failed, uri is empty"); + } else raise_log("ConnectTo failed, uri is not String"); + return false; + } + + /** + * Disconnect socket + */ + public void Disconnect() { + + if (_client instanceof Socket) { + Remove_All_Events(); // remove semua event + _client.disconnect(); // disconnect + } + _client = null; + + _remote = ""; + _id = ""; + } + + /** + * Remove all Socket Events + */ + public void Remove_All_Events() { + if (_client instanceof Socket) { + _client.off(); + } + } + + + public boolean IsConnected() { + if (_client != null) { + if (_client instanceof Socket) { + return _client.connected(); + } + } + return false; + } + + + private java.util.Set _debug_ignore = new java.util.HashSet(); + + /** + * Add ignored debug for specified socket.io event + * @param eventname eventname to ignore + */ + public void Add_Ignored_Debug_Event(String eventname) { + _debug_ignore.add(eventname); + } + + private boolean Is_Ignored_Debug(String event) { + return _debug_ignore.contains(event); + } + + + /** + * Add Custom Event to Socket + * If data arrived, will raise event_eventname(value as Object) as Object + * return Object to reply this event + * + * + * Sub event_eventname(value as Object) as Object + * return "reply value" + * End Sub + * + * @param eventname : eventname, lowercase characters and numbers only + * @param debugmode : if true, will give debug output + * @return true if event added + */ + public boolean Add_Event(final String eventname, boolean debugmode) { + if (is_correct_eventname(eventname)) { + if (_client instanceof Socket) { + if (_client.hasListeners(eventname)) { + _client.off(eventname); + raise_log("Add_Event with eventname = "+eventname+" already exist, removing the previous"); + } + + final String eventtoraise = event+"_"+eventname; + final boolean need_event = bax.subExists(eventtoraise); + if (need_event && !Is_Ignored_Debug(eventname)) raise_log("Will raise "+eventtoraise); + + + _client.on(eventname, new Emitter.Listener() { + @Override + public void call(Object... args) { + if (args!=null) { + SocketIoObject sio = new SocketIoObject(); + sio.setEvent(eventname); + sio.setValueCount(ValuesCount(args)); + if (sio.getValueCount()>0) { + if (sio.getValueCount() == 1) + sio.setValue(args[0]); + else { + sio.setValue(Arrays.copyOf(args, sio.getValueCount())); + } + } + sio.setAckIndex(have_Acknowledge(args)); + + if (debugmode && !Is_Ignored_Debug(eventname)) raise_log("Receive Event "+eventname+ (sio.HaveValue() ? " with value = "+sio.ValueToString():"")); + + if (need_event) { + + bax.raiseEventFromDifferentThread(Me, null, 0, eventtoraise, false, new Object[] {sio}); + if ("resync_content".equals(eventname)) { + // resync_content bisa lama, kalau lagi download file, jadi wait 0 = wait forever + //TODO nunggu lama di sini , socket.io server kirim ping, tidak direspon pong oleh client, dianggap disconnected + sio.Wait(0); + } else { + // yang lain , wait 5 detik + sio.Wait(5000); + } + +// if (debugmode && !Is_Ignored_Debug(eventname)) { +// raise_log("Event "+eventname+", Return Data = " + sio.ReplyToString()); +// } + + + if (sio.NeedReply()) { + Ack ack = (Ack) args[sio.getAckIndex()]; + if (sio.getReply()!=null) { + if (debugmode && !Is_Ignored_Debug(eventname)) raise_log("Event "+eventname+", Acknowledge reply = "+sio.ReplyToString()); + if (sio.ReplyIsJsonArray()) { + ack.call(sio.ReplyToJsonArray().getObject()); + } + else if (sio.ReplyIsJsonObject()) { + ack.call(sio.ReplyToJsonObject().getObject()); + } + else { + ack.call(sio.getReply()); + } + } else { + if (debugmode && !Is_Ignored_Debug(eventname)) raise_log("Event "+eventname+", Return Data is null, Acknowledge with empty string"); + ack.call(""); + } + } else if (sio.getReply() != null) { + if (debugmode && !Is_Ignored_Debug(eventname)) raise_log("Event "+eventname+" have Return Data but dont need Acknowledge"); + } + } + } + + } + } + + ); + + return true; + } else raise_log("Add_Event failed, Socket not connected"); + } else raise_log("Add_Event failed, eventname "+eventname+" is invalid. Valid eventname only contains lowercase characters, numbers, and underscore"); + return false; + } + + /** + * Remove specified eventname from socket + * @param eventname : eventname to remove, lowercase characters and numbers only + * @return true if removed + */ + public boolean Remove_Event(String eventname) { + if (is_correct_eventname(eventname)) { + if (_client instanceof Socket) { + if (_client.hasListeners(eventname)) { + _client.off(eventname); + return true; + } else raise_log("Remove_Event failed, Socket dont have event : "+eventname); + } else raise_log("Remove_Event failed, Socket not connected"); + } else raise_log("Remove_Event failed, eventname "+eventname+" is invalid. Valid eventname only contains lowercase characters, numbers, and underscore"); + return false; + } + + /** + * Emit simple String to target socket + * + * @param emitname : event name + * @param value : String to send + * @return true if value can be send + */ + public boolean Emit_String(String emitname, String value) { + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_string(value)) { + _client.emit(emitname, value); + return true; + } + return false; + } + + /** + * Emit simple String to target socket and wait for acknowledge + * @param emitname : event name + * @param value : value to send + * @param ackevent : prefix of acknowledge event + * On success, will raise event ackevent_success(replyvalue as String) + * On timeout, will raise event ackevent_timeout(msg as string) + * @return true if value can be send + */ + public boolean Emit_String_WithAcknowledge(String emitname, String value, String ackevent) { + final boolean need_success = bax.subExists(ackevent+"_success"); + final boolean need_timeout = bax.subExists(ackevent+"_timeout"); + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_string(value) && mycodes.valid_string(ackevent)) { + + _client.emit(emitname, value, new AckWithTimeout(5000) { + + @Override + public void onSuccess(Object... args) { + if (args!=null && args.length>0) { + //TODO cek tipe args dengan log + for (int ii = 0; ii < args.length; ii++) { + raise_log("Emit_String_WithAcknowledge onSuccess, args["+ii+"] = "+args[ii].getClass().getTypeName()); + } + + //String result = mycodes.Combine("\n", args); + String result = gson.toJson(args); + if (need_success) bax.raiseEventFromDifferentThread(Me, null, 0, ackevent+"_success", false, new Object[] {result}); + + } + + } + + @Override + public void onTimeout() { + if (need_timeout) bax.raiseEventFromDifferentThread(Me, null, 0, ackevent+"_timeout", false, new Object[] {"Emitname=["+emitname+"] value=["+value+"] has no reply in 5 seconds"}); + + } + + }); + + //TODO kalau sudah benar, hapus ini +// _client.emit(emitname,value, new Ack() { +// +// @Override +// public void call(Object... args) { +// if (args!=null && args.length>0) { +// //TODO cek tipe args dengan log +// for (int ii = 0; ii < args.length; ii++) { +// raise_log("Emit_String_WithAcknowledge onSuccess, args["+ii+"] = "+args[ii].getClass().getTypeName()); +// } +// +// //String result = mycodes.Combine("\n", args); +// String result = gson.toJson(args); +// if (need_success) bax.raiseEventFromDifferentThread(Me, null, 0, ackevent+"_success", false, new Object[] {result}); +// +// } +// } +// +// }); + + return true; + } + return false; + } + + /** + * Emit byte array to target socket and wait for acknowledge + * @param emitname : event name + * @param value : byte array + * @param ackevent : prefix of acknowledge event + * On success, will raise event ackevent_success(replyvalue as byte[]) + * On timeout, will raise event ackevent_timeout(msg as string) + * @return true if value can be send + */ + public boolean Emit_Bytes_WithAcknowledge(String emitname, byte[] value, String ackevent) { + final boolean need_success = bax.subExists(ackevent + "_success"); + final boolean need_timeout = bax.subExists(ackevent + "_timeout"); + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_bytearray(value) + && mycodes.valid_string(ackevent)) { + + _client.emit(emitname, value, new AckWithTimeout(5000) { + + @Override + public void onSuccess(Object... args) { + if (args != null && args.length>0) { + //TODO cek tipe args dengan log + for (int ii = 0; ii < args.length; ii++) { + raise_log("Emit_Bytes_WithAcknowledge onSuccess, args[" + ii + "] = " + + args[ii].getClass().getTypeName()); + } + + if (args[0]==byte[].class) { + byte[] result = (byte[]) args[0]; + if (need_success) + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { result }); + } else if (args[0]==int[].class) { + int[] result = (int[]) args[0]; + if (need_success) + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { result }); + } else if (args[0]==String.class) { + String result = (String) args[0]; + if (need_success) + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { result }); + } + } + } + + @Override + public void onTimeout() { + if (need_timeout) + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_timeout", false, new Object[] { + "Emitname=[" + emitname + "] value=[" + value + "] has no reply in 5 seconds" }); + + } + + }); + + //TODO kalau sudah benar, hapus ini +// _client.emit(emitname, value, new Ack() { +// +// @Override +// public void call(Object... args) { +// if (args != null && args.length>0) { +// //TODO cek tipe args dengan log +// for (int ii = 0; ii < args.length; ii++) { +// raise_log("Emit_Bytes_WithAcknowledge onSuccess, args[" + ii + "] = " +// + args[ii].getClass().getTypeName()); +// } +// +// if (args[0]==byte[].class) { +// byte[] result = (byte[]) args[0]; +// if (need_success) +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { result }); +// } else if (args[0]==int[].class) { +// int[] result = (int[]) args[0]; +// if (need_success) +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { result }); +// } else if (args[0]==String.class) { +// String result = (String) args[0]; +// if (need_success) +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { result }); +// } +// } +// +// } +// +// }); + + return true; + } + return false; + } + + + /** + * Emit byte array to target socket + * + * @param emitname : event name + * @param value : byte array + * @return true if value can be send + */ + public boolean Emit_Bytes(String emitname, byte[] value) { + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_bytearray(value)) { + _client.emit(emitname, value); + return true; + } + return false; + } + + /** + * Emit Map to target socket + * On Server side, will be received as String in JSON format + * @param emitname : event name + * @param value : Map value to send + * @return true if value can be send + */ + public boolean Emit_Map(String emitname, anywheresoftware.b4a.objects.collections.Map value) { + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_Map(value)) { + //JSONObject json = new JSONObject(value.getObject()); + + _client.emit(emitname, gson.toJson(value.getObject())); + return true; + } + return false; + } + + /** + * Emit Map to target socket and wait for acknowledge + * On Server side, will be received as String in JSON format + * On success, will raise event ackevent_success(replyvalue as Object) + * On timeout, will raise event ackevent_timeout(msg as string) + * @param emitname : event name + * @param value : value to send + * @param ackevent : prefix of acknowledge event + * @return true if value can be send + */ + public boolean Emit_Map_WithAcknowledge(String emitname, anywheresoftware.b4a.objects.collections.Map value, + String ackevent) { + final boolean need_success = bax.subExists(ackevent + "_success"); + final boolean need_timeout = bax.subExists(ackevent + "_timeout"); + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_Map(value) + && mycodes.valid_string(ackevent)) { + + _client.emit(emitname, gson.toJson(value.getObject()), new AckWithTimeout(5000) { + + @Override + public void onSuccess(Object... args) { + if (args != null && args.length>0) { + //TODO cek tipe args dengan log + for (int ii = 0; ii < args.length; ii++) { + raise_log("Emit_Map_WithAcknowledge onSuccess, args[" + ii + "] = " + + args[ii].getClass().getTypeName()); + } + + + + //String rr = mycodes.Combine("\n", args); + String rr = gson.toJson(args); + if (need_success) { + if (mycodes.String_Is_JSONArray(rr)) { + JsonArray jarr = new JsonArray(); + jarr.InitializeFromString(rr); + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { jarr }); + } else if (mycodes.String_Is_JSONObject(rr)) { + JsonObject jobj = new JsonObject(); + jobj.InitializeFromString(rr); + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { jobj }); + } else { + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { rr }); + } + } + + } + } + + @Override + public void onTimeout() { + if (need_timeout) + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_timeout", false, new Object[] { + "Emitname=[" + emitname + "] value=[" + value + "] has no reply in 5 seconds" }); + + } + + }); + + //TODO kalau sudah benar, hapus ini +// _client.emit(emitname, gson.toJson(value.getObject()), new Ack() { +// +// @Override +// public void call(Object... args) { +// if (args != null && args.length>0) { +// //TODO cek tipe args dengan log +// for (int ii = 0; ii < args.length; ii++) { +// raise_log("Emit_Map_WithAcknowledge onSuccess, args[" + ii + "] = " +// + args[ii].getClass().getTypeName()); +// } +// +// +// +// //String rr = mycodes.Combine("\n", args); +// String rr = gson.toJson(args); +// if (need_success) { +// if (mycodes.String_Is_JSONArray(rr)) { +// JsonArray jarr = new JsonArray(); +// jarr.InitializeFromString(rr); +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { jarr }); +// } else if (mycodes.String_Is_JSONObject(rr)) { +// JsonObject jobj = new JsonObject(); +// jobj.InitializeFromString(rr); +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { jobj }); +// } else { +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { rr }); +// } +// } +// +// } +// +// } +// +// }); + + + + return true; + } + return false; + } + + /** + * Emit List to target socket On Server side, will be received as String in JSON + * format + * + * @param emitname : event name + * @param value : List value to send + * @return true if value can be send + */ + public boolean Emit_List(String emitname, anywheresoftware.b4a.objects.collections.List value) { + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_List(value)) { + //JSONArray json = new JSONArray(value.getObject()); + _client.emit(emitname, gson.toJson(value.getObject())); + return true; + } + return false; + } + + /** + * Emit List to target socket and wait for acknowledge + * On Server side, will be received as String in JSON format + * On success, will raise event ackevent_success(replyvalue as Object) + * on timeout, will raise event ackevent_timeout(msg as string) + * @param emitname : event name + * @param value : value to send + * @param ackevent : prefix of acknowledge event + * @return true if value can be send + */ + public boolean Emit_List_WithAcknowledge(String emitname, anywheresoftware.b4a.objects.collections.List value, + String ackevent) { + final boolean need_success = bax.subExists(ackevent + "_success"); + final boolean need_timeout = bax.subExists(ackevent + "_timeout"); + + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_List(value) + && mycodes.valid_string(ackevent)) { + + _client.emit(emitname, gson.toJson(value.getObject()), new AckWithTimeout(5000) { + + @Override + public void onSuccess(Object... args) { + if (args != null && args.length>0) { + // TODO cek tipe args dengan log + for (int ii = 0; ii < args.length; ii++) { + raise_log("Emit_List_WithAcknowledge onSuccess, args[" + ii + "] = " + + args[ii].getClass().getTypeName()); + } + + //String rr = mycodes.Combine("\n", args); + String rr = gson.toJson(args); + if (need_success) { + if (mycodes.String_Is_JSONArray(rr)) { + JsonArray jarr = new JsonArray(); + jarr.InitializeFromString(rr); + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { jarr }); + } else if (mycodes.String_Is_JSONObject(rr)) { + JsonObject jobj = new JsonObject(); + jobj.InitializeFromString(rr); + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { jobj }); + } else { + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { rr }); + } + } + + } + } + + @Override + public void onTimeout() { + if (need_timeout) + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_timeout", false, new Object[] { + "Emitname=[" + emitname + "] value=[" + value + "] has no reply in 5 seconds" }); + + } + + }); + + //TODO kalau sudah benar, hapus ini +// _client.emit(emitname, gson.toJson(value.getObject()), new Ack() { +// +// @Override +// public void call(Object... args) { +// if (args != null && args.length>0) { +// // TODO cek tipe args dengan log +// for (int ii = 0; ii < args.length; ii++) { +// raise_log("Emit_List_WithAcknowledge onSuccess, args[" + ii + "] = " +// + args[ii].getClass().getTypeName()); +// } +// +// //String rr = mycodes.Combine("\n", args); +// String rr = gson.toJson(args); +// if (need_success) { +// if (mycodes.String_Is_JSONArray(rr)) { +// JsonArray jarr = new JsonArray(); +// jarr.InitializeFromString(rr); +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { jarr }); +// } else if (mycodes.String_Is_JSONObject(rr)) { +// JsonObject jobj = new JsonObject(); +// jobj.InitializeFromString(rr); +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { jobj }); +// } else { +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { rr }); +// } +// } +// +// } +// +// } +// +// }); + + + return true; + } + return false; + } + + + /** + * Emit JsonObject to target socket + * On Server side, will be received as String in JSON format + * @param emitname : event name + * @param value : JsonObject + * @return true if value can be send + */ + public boolean Emit_JsonObject(String emitname, JsonObject value) { + if (IsConnected()) { + if (mycodes.valid_string(emitname)) { + if (mycodes.valid_JsonObject(value)) { + //if (BA.debugMode) raise_log("About to emit JsonObject, emitname = "+emitname+", value = "+value.getObject().toString()); + _client.emit(emitname, value.getObject()); + return true; + } else if (BA.debugMode) raise_log("Emit_JsonObject failed, invalid JsonObject value"); + } else if (BA.debugMode) raise_log("Emit_JsonObject failed, invalid emitname"); + } else if (BA.debugMode) raise_log("Emit_JsonObject failed, not connected"); + return false; + } + + /** + * emit JsonObject to target socket and wait for acknowledge + * On Server side, will be received as String in JSON format + * On success, will raise event ackevent_success(replyvalue as Object) + * on timeout, will raise event ackevent_timeout(msg as string) + * @param emitname : event name + * @param value : JsonObject + * @param ackevent : prefix of acknowledge event + * @return true if value can be send + */ + public boolean Emit_JsonObject_WithAcknowledge(String emitname, JsonObject value, String ackevent) { + final boolean need_success = bax.subExists(ackevent + "_success"); + final boolean need_timeout = bax.subExists(ackevent + "_timeout"); + + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_JsonObject(value) + && mycodes.valid_string(ackevent)) { + + _client.emit(emitname, value.getObject(), new AckWithTimeout(5000) { + + @Override + public void onSuccess(Object... args) { + if (args != null && args.length>0) { + // TODO cek tipe args dengan log + for (int ii = 0; ii < args.length; ii++) { + raise_log("Emit_JsonObject_WithAcknowledge onSuccess, args[" + ii + "] = " + + args[ii].getClass().getTypeName()); + } + + //String rr = mycodes.Combine("\n", args); + String rr = gson.toJson(args); + if (need_success) { + if (mycodes.String_Is_JSONArray(rr)) { + JsonArray jarr = new JsonArray(); + jarr.InitializeFromString(rr); + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { jarr }); + } else if (mycodes.String_Is_JSONObject(rr)) { + JsonObject jobj = new JsonObject(); + jobj.InitializeFromString(rr); + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { jobj }); + } else { + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { rr }); + } + } + + } + } + + @Override + public void onTimeout() { + if (need_timeout) + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_timeout", false, new Object[] { + "Emitname=[" + emitname + "] value=[" + value + "] has no reply in 5 seconds" }); + + } + + }); + + //TODO kalau sudah benar, hapus ini +// _client.emit(emitname, value.getObject().toString(), new Ack() { +// +// @Override +// public void call(Object... args) { +// if (args != null && args.length>0) { +// // TODO cek tipe args dengan log +// for (int ii = 0; ii < args.length; ii++) { +// raise_log("Emit_JsonObject_WithAcknowledge onSuccess, args[" + ii + "] = " +// + args[ii].getClass().getTypeName()); +// } +// +// //String rr = mycodes.Combine("\n", args); +// String rr = gson.toJson(args); +// if (need_success) { +// if (mycodes.String_Is_JSONArray(rr)) { +// JsonArray jarr = new JsonArray(); +// jarr.InitializeFromString(rr); +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { jarr }); +// } else if (mycodes.String_Is_JSONObject(rr)) { +// JsonObject jobj = new JsonObject(); +// jobj.InitializeFromString(rr); +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { jobj }); +// } else { +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { rr }); +// } +// } +// +// } +// } +// +// }); + + + return true; + } + return false; + } + + + /** + * Emit JsonArray to target socket + * On Server side, will be received as String in JSON format + * @param emitname : event name + * @param value : JsonArray + * @return true if value can be send + */ + public boolean Emit_JsonArray(String emitname, JsonArray value) { + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_JsonArray(value)) { + _client.emit(emitname, value.getObject()); + return true; + } + return false; + } + + /** + * Emit JsonArray to target socket and wait for acknowledge + * On Server side, will be received as String in JSON format + * On success, will raise event ackevent_success(replyvalue as Object) + * on timeout, will raise event ackevent_timeout(msg as string) + * @param emitname : event name + * @param value : JsonArray + * @param ackevent : prefix of acknowledge event + * @return true if value can be send + */ + public boolean Emit_JsonArray_WithAcknowledge(String emitname, JsonArray value, String ackevent) { + final boolean need_success = bax.subExists(ackevent + "_success"); + final boolean need_timeout = bax.subExists(ackevent + "_timeout"); + if (IsConnected() && mycodes.valid_string(emitname) && mycodes.valid_JsonArray(value) + && mycodes.valid_string(ackevent)) { + + _client.emit(emitname, value.getObject(), new AckWithTimeout(5000) { + + @Override + public void onSuccess(Object... args) { + if (args != null && args.length>0) { + // TODO cek tipe args dengan log + for(int ii = 0; ii < args.length; ii++) { + raise_log("Emit_JsonArray_WithAcknowledge onSuccess, args[" + ii + "] = " + + args[ii].getClass().getTypeName()); + } + + //String rr = mycodes.Combine("\n", args); + String rr = gson.toJson(args); + if (need_success) { + if (mycodes.String_Is_JSONArray(rr)) { + JsonArray jarr = new JsonArray(); + jarr.InitializeFromString(rr); + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { jarr }); + } else if (mycodes.String_Is_JSONObject(rr)) { + JsonObject jobj = new JsonObject(); + jobj.InitializeFromString(rr); + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { jobj }); + } else { + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, + new Object[] { rr }); + } + } + + } + } + + @Override + public void onTimeout() { + if (need_timeout) + bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_timeout", false, new Object[] { + "Emitname=[" + emitname + "] value=[" + value + "] has no reply in 5 seconds" }); + } + + }); + + //TODO kalau sudah benar, hapus ini +// _client.emit(emitname, value.getObject().toString(), new Ack() { +// +// @Override +// public void call(Object... args) { +// if (args != null && args.length>0) { +// // TODO cek tipe args dengan log +// for(int ii = 0; ii < args.length; ii++) { +// raise_log("Emit_JsonArray_WithAcknowledge onSuccess, args[" + ii + "] = " +// + args[ii].getClass().getTypeName()); +// } +// +// //String rr = mycodes.Combine("\n", args); +// String rr = gson.toJson(args); +// if (need_success) { +// if (mycodes.String_Is_JSONArray(rr)) { +// JsonArray jarr = new JsonArray(); +// jarr.InitializeFromString(rr); +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { jarr }); +// } else if (mycodes.String_Is_JSONObject(rr)) { +// JsonObject jobj = new JsonObject(); +// jobj.InitializeFromString(rr); +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { jobj }); +// } else { +// bax.raiseEventFromDifferentThread(Me, null, 0, ackevent + "_success", false, +// new Object[] { rr }); +// } +// } +// +// } +// } +// +// }); + + return true; + } + return false; + } + + /** + * Send simple String to event 'message' + * @param value : String to send + * @return true if can be send + */ + public boolean SendMessage(String value) { + if (IsConnected()) { + if (value instanceof String) { + if (!value.isEmpty()) { + _client.emit(io.socket.engineio.client.Socket.EVENT_MESSAGE, value); + return true; + } else raise_log("SendMessage failed, value is zero length"); + } else raise_log("SendMessage failed, value is not String"); + } else raise_log("SendMessage failed, Socket not connected"); + return false; + } + + private boolean is_correct_eventname(String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + return Pattern.matches("^[a-z0-9_]+$", value);// hanya lowercase dan angka + } + } + return false; + } + + /** + * Get SessionID from socket + * @return empty string if socket not connected + */ + public String getID() { + return _id; + } + + /** + * Get RemoteSocket info, in form of [IP]:[Port] + * @return empty string if socket not connected + */ + public String getRemote() { + return _remote; + } + + + /////////// Events /////////////// + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_connected(String id, String remote){ + if (need_connected_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_connected", false, new Object[] {id, remote}); + } + + private void raise_disconnected(String id, String remote, String reason) { + if (need_disconnected_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_disconnected", false, new Object[] {id,remote, reason}); + } + + + private void raise_message(String id, String remote, String value) { + if (need_message_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_message", false, new Object[] {id,remote, value}); + } + + private void raise_reconnecting(String uri, int attempt) { + if (need_reconnecting_event) + bax.raiseEventFromDifferentThread(Me, null, 0, event + "_reconnecting", false, + new Object[] { uri, attempt }); + } + + private void raise_reconnected(String uri, int attempt) { + if (need_reconnected_event) + bax.raiseEventFromDifferentThread(Me, null, 0, event + "_reconnected", false, + new Object[] { uri, attempt }); + } + + /** + * Check if in arguments contain Socket.io.Acknowledge object + * @param objects : object to check + * @return -1 if not found, or index of Acknowledge object + */ + private int have_Acknowledge(Object...objects) { + if (objects!=null) { + int len = objects.length; + if (len>0) { + for(int ii=0;ii0) { + int count = 0; + for (int ii = 0; ii < args.length; ii++) { + Object obj = args[ii]; + if (obj!=null) { + if (obj instanceof Ack) { + break; + } else count++; + } + } + return count; + } + } + return 0; + } +} diff --git a/src/androgpio/customsocket/jUDPSocket.java b/src/androgpio/customsocket/jUDPSocket.java new file mode 100644 index 0000000..8318459 --- /dev/null +++ b/src/androgpio/customsocket/jUDPSocket.java @@ -0,0 +1,329 @@ +package androgpio.customsocket; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Arrays; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; + +//@BA.ShortName("UDPSocket") +@BA.Events(values= { + "log(msg as string)", + "socketstatus(isopen as boolean)", + "newdata(bb() as byte, sourceip as string, sourceport as int)", + "txrxstatistic(txcount as int, txfail as int, rxcount as int, rxfail as int)" +}) + +@Deprecated // Use jUDPSocket2 instead +@BA.Hide +/** + * UDP Socket by me, with more functions + * @author rdkartono + * + */ +public class jUDPSocket { + + private BA ba; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_socketstatus_event = false; + private boolean need_newdata_event = false; + private boolean need_txrxstatistic_event = false; + private DatagramSocket udp; + private boolean isopened = false; + + private int txfail=0, txcount=0, rxfail=0, rxcount=0; + + /** + * Initialize UDP Socket (made by RDK) + * @param callerobject : caller object + * @param eventname : event name + */ + public void Initialize(BA bax, Object callerobject, String eventname) { + ba = bax; + caller = callerobject; + event = eventname.trim(); + if (ba instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = ba.subExists(event+"_log"); + need_socketstatus_event = ba.subExists(event+"_socketstatus"); + need_newdata_event = ba.subExists(event+"_newdata"); + need_txrxstatistic_event = ba.subExists(event+"_txrxstatistic"); + } + } + } + } + + /** + * Check if UDP already opened + * @return true if opened + */ + public boolean getUDPIsOpened() { + return isopened; + } + + /** + * Close UDP Communication + * will raise socketstatus event + */ + public void Close() { + isopened = false; + if (udp instanceof DatagramSocket) { + udp.close(); + } + + udp = null; + raise_socketstatus(false); + } + + /** + * Get IPV4 bytes from ip string + * @param ipstring : ip string to translate + * @return 0.0.0.0 if failed + */ + public byte[] Get_IPV4_Bytes(String ipstring) { + if (!ipstring.isEmpty()) { + try { + InetAddress xx = InetAddress.getByName(ipstring); + return xx.getAddress(); + } catch (UnknownHostException e) { + raise_log("Unable to Get_IPV4_Bytes, UnknownHostException Msg : "+e.getMessage()); + } catch(SecurityException e) { + raise_log("Unable to Get_IPV4_Bytes, SecurityException Msg : "+e.getMessage()); + } + } + return new byte[4]; + } + + /** + * Ping a target IP + * @param ipstring : target IP + * @return true if reachable + */ + public boolean PingTo(String ipstring) { + try { + InetAddress target = InetAddress.getByName(ipstring); + return target.isReachable(1000); + } catch (UnknownHostException e) { + raise_log("Unable to PingTo "+ipstring+", UnknownHostException Msg : "+e.getMessage()); + } catch (IOException e) { + raise_log("Unable to PingTo "+ipstring+", IOException Msg : "+e.getMessage()); + } + return false; + } + + /** + * Get Local MAC address + * @return empty string if failed + */ + public String GetLocalMAC() { + + try { + InetAddress local = InetAddress.getLocalHost(); + NetworkInterface ni = NetworkInterface.getByInetAddress(local); + byte[] result = ni.getHardwareAddress(); + if (result!=null) { + if (result.length>0) { + StringBuilder str = new StringBuilder(); + for(byte xx : result) { + if (str.length()>0) str.append(":"); + int xt = Bit.And(xx, 0xFF); // jadiin positif dulu + if (xt<10) str.append("0"); // kalau di bawah 10, tambahin 0 , biar double digit + str.append(Bit.ToHexString(xt)); + } + return str.toString().toUpperCase(); + } + } + } catch (UnknownHostException e) { + raise_log("Unable to GetLocalMAC, UnknownHostException Msg : "+e.getMessage()); + } catch (SocketException e) { + raise_log("Unable to GetLocalMAC, SocketException Msg : "+e.getMessage()); + } catch(NullPointerException e) { + raise_log("Unable to GetLocalMAC, NullPointerException Msg : "+e.getMessage()); + } + return ""; + } + + /** + * Get Local IP Address + * @return empty string failed + */ + public String GetLocalIP() { + try { + InetAddress local = InetAddress.getLocalHost(); + return local.getHostAddress(); + } catch (UnknownHostException e) { + raise_log("Unable to GetLocalIP, UnknownHostException Msg : "+e.getMessage()); + } + + return ""; + } + + /** + * Get Local Port bounded/opened with UDP Socket + * @return -1 if UDP is not opened yet + */ + public int GetLocalPort() { + if (udp instanceof DatagramSocket) { + return udp.getLocalPort(); + } + return -1; + } + + /** + * Open UDP Socket + * will raise socketstatus event + * @param port : port number to listen + * @param maxpackagesize : max packagesize. Default to 1000 bytes + * @return true if port can be opened + */ + public boolean Open(int port, int maxpackagesize) { + if (maxpackagesize<1) maxpackagesize = 1000; + + if (udp instanceof DatagramSocket) Close(); // kalau sudah pernah open, close dulu + try { + udp = new DatagramSocket(port); + Thread tx = new Thread(new udprun(udp, maxpackagesize)); + tx.start(); + raise_socketstatus(true); + return true; + } catch (SocketException e) { + raise_log("Unable to Open UDP at port "+port+", SocketException Msg : "+e.getMessage()); + raise_socketstatus(false); + } catch(SecurityException e) { + raise_log("Unable to Open UDP at port "+port+", SecurityException Msg : "+e.getMessage()); + raise_socketstatus(false); + } + return false; + } + + private class udprun implements Runnable { + private final int maxudpsize; + private final DatagramSocket udp; + + public udprun(DatagramSocket udpsock, int maxsize) { + udp = udpsock; + maxudpsize = maxsize; + isopened = true; + } + + @Override + public void run() { + + while(isopened) { + if (udp instanceof DatagramSocket) { + byte[] bb = new byte[maxudpsize]; + DatagramPacket xx = new DatagramPacket(bb, maxudpsize); + try { + udp.receive(xx); + // kadang data yang real diterima (getLength), lebih kecil daripada getData + byte[] realdata = Arrays.copyOf(xx.getData(), xx.getLength()); + raise_newdata(realdata, xx.getAddress().getHostAddress(), xx.getPort()); + if (rxcount0) { + if (bb!=null) { + if (bb.length>0) { + if (udp instanceof DatagramSocket) { + try { + InetAddress t_ip = InetAddress.getByName(targetip); + DatagramPacket pp = new DatagramPacket(bb, bb.length, t_ip, targetport); + udp.send(pp); + if (txcount() { + + @Override + public Object[] call() { + boolean success = false; + NetworkInterface nif = null; + try { + if (mycodes.valid_string(localip)) nif = NetworkInterface.getByInetAddress(InetAddress.getByName(localip)); + InetAddress target = InetAddress.getByName(targetip); + success = target.isReachable(nif, 0, waitms < 10 ? 10 : waitms); + } catch (UnknownHostException | SecurityException e) { + // dari InetAddress.getByName + raise_log("PingTo ["+targetip+"] exception : "+e.getMessage()); + } catch (IOException e) { + // dari target.isReachable + raise_log("PingTo ["+targetip+"] exception : "+e.getMessage()); + } + + return new Object[] {targetip, success}; + } + + }); + return sender; + } + + /** + * Get Local IP Addresses in this machine + * @param IPV4Only if true, only return IPV4 address. If false, will return all address + * @return array of ip addresses, in string + */ + public String[] GetLocalIP(final boolean IPV4Only) { + List allip = new ArrayList(); + + try { + Enumeration e = NetworkInterface.getNetworkInterfaces(); + while(e.hasMoreElements()) { + NetworkInterface n = e.nextElement(); + Enumeration nn = n.getInetAddresses(); + while(nn.hasMoreElements()) { + InetAddress i = nn.nextElement(); + if (!i.isLoopbackAddress()) { + if (IPV4Only) { + if (i.getAddress().length == 4) { + allip.add(i.getHostAddress()); + } + } else { + allip.add(i.getHostAddress()); + } + } + } + } + } catch (SocketException err) { + raise_log("GetLocalIP exception : "+err.getMessage()); + } + + return allip.toArray(new String[allip.size()]); + } + + /** + * Get all MAC Addresses in this machine + * @return array of MAC addresses, in string + */ + public String[] GetLocalMAC() { + List allmac = new ArrayList(); + + try { + Enumeration e = NetworkInterface.getNetworkInterfaces(); + while (e.hasMoreElements()) { + NetworkInterface n = e.nextElement(); + byte[] mac = n.getHardwareAddress(); + if (mac != null) { + if (mac.length > 0) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < mac.length; i++) { + sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : "")); + } + allmac.add(sb.toString()); + } + } + } + } catch (SocketException err) { + raise_log("GetLocalMAC exception : " + err.getMessage()); + } + + return allmac.toArray(new String[allmac.size()]); + } + + /** + * Get Local Port assigned to this UDP Socket + * @return -1 if not available + */ + public int GetLocalPort() { + if (udp!=null) { + return udp.getLocalPort(); + } + return -1; + } + + private void reset_txrxstatistic() { + txcount = 0; + txfail = 0; + rxcount = 0; + rxfail = 0; + } + + /** + * Get Succesfull transmission counter + * @return number of successfull transmission + */ + public int getTX_Sent() { + return txcount; + } + + /** + * Get Failed transmission counter + * @return number of failed transmission + */ + public int getTX_Fail() { + return txfail; + } + + /** + * Get Successfull reception counter + * @return number of successfull reception + */ + public int getRX_Received() { + return rxcount; + } + + /** + * Get Failed reception counter + * @return number of failed reception + */ + public int getRX_Fail() { + return rxfail; + } + + @BA.Hide + /** + * Get DatagramSocket object + * @return DatagramSocket object + */ + public DatagramSocket getDataGramSocket() { + return udp; + } + + /** + * Connect to one remote UDP computer + * will raise event eventname_connectresult(targetip as string, targetport as int, result as boolean) + * on data receiving, will raise event eventname_newdata(bb() as byte, sourceip as string, sourceport as int) + * @param targetip target ip to connect + * @param targetport target port to connect + * @return Object to use in Wait For + */ + public Object ConnectTo(BA ba, final String targetip, final int targetport) { + Object sender = new Object(); + BA.runAsync(ba, sender, event+"_connectresult", new Object[] {targetip, targetport, false}, new Callable() { + + @Override + public Object[] call() { + boolean success = false; + + try { + InetAddress target = InetAddress.getByName(targetip); + DatagramSocket tt = new DatagramSocket(); + tt.connect(target, targetport); + if (tt.isConnected()) { + // Close previous UDP + if (getUDPIsOpened()) { + raise_log("Closing previous UDP Connection"); + Close(); + } + reset_txrxstatistic(); + raise_log("Connected to ["+targetip+":"+targetport+"]"); + raise_txrxstatistic(); + udp = tt; + BA.submitRunnable(new udpreceiver(ba, sender, 1500), null, 0); + success = true; + } else { + tt.close(); + } + } catch(IllegalArgumentException e) { + // dari DatagramSocket.connect + raise_log("ConnectTo ["+targetip+":"+targetport+"] exception : "+e.getMessage()); + } catch (UnknownHostException e) { + // dari InetAddress.getByName + raise_log("ConnectTo ["+targetip+":"+targetport+"] exception : "+e.getMessage()); + } catch (SocketException e) { + // dari DatagramSocket + raise_log("ConnectTo ["+targetip+":"+targetport+"] exception : "+e.getMessage()); + } catch (SecurityException e) { + // dari DatagramSocket.connect + raise_log("ConnectTo ["+targetip+":"+targetport+"] exception : "+e.getMessage()); + } + + return new Object[] {targetip, targetport, success}; + } + + }); + return sender; + } + + /** + * Send data to connected remote + * User must call method ConnectTo first before using this method + * @param bb data to send + * @return true if success + */ + public boolean SendDataToConnectedRemote(byte[] bb) { + if (udp!=null) { + if (!udp.isClosed()) { + if (!udp.isConnected()) { + if (bb!=null) { + if (bb.length>0) { + DatagramPacket dp = new DatagramPacket(bb, bb.length); + try { + udp.send(dp); + txcount++; + raise_txrxstatistic(); + return true; + } catch (IOException e) { + raise_log("SendDataToConnectedRemote failed, IOException Msg : "+e.getMessage()); + } + } else raise_log("SendDataToConnectedRemote failed, data is empty"); + } else raise_log("SendDataToConnectedRemote failed, data is null"); + } else raise_log("SendDataToConnectedRemote failed, UDP is not connected"); + } else raise_log("SendDataToConnectedRemote failed, UDP is closed"); + } else raise_log("SendDataToConnectedRemote failed, UDP is null"); + txfail++; + raise_txrxstatistic(); + return false; + } + + /** + * Send Data to many targets + * will raise event eventname_senddataresult(successtarget() as string, targetport as int, bb() as byte) + * Check at successtarget() to know which target is success + * if failed, successtarget() is null + * @param bb data to send + * @param targetip array of target IP address or host name + * @param targetport target UDP Port + * @return Object to use in Wait For + */ + public Object SendDataToMany(BA ba, final byte[] bb, final String[] targetip, final int targetport) { + Object sender = new Object(); + BA.runAsync(ba, sender, event + "_senddataresult", new Object[] { null, targetport, bb}, + new Callable() { + + @Override + public Object[] call() { + List successtarget = new ArrayList(); + for (String target : targetip) { + if (SendData(bb, target, targetport)) { + successtarget.add(target); + } + } + + return new Object[] { successtarget.size()>0 ? successtarget.toArray(new String[successtarget.size()]) : null, targetport, bb }; + } + + }); + return sender; + } + + /** + * Send Data to one Target + * user must call method Open first before using this method + * @param bb : data to send + * @param targetip : IP address or host name + * @param targetport target UDP Port + * @return true if success + */ + public boolean SendData(byte[] bb, String targetip, int targetport) { + if (mycodes.valid_string(targetip)) { + if (mycodes.valid_portnumber(targetport)) { + if (bb!=null && bb.length>0) { + if (udp!=null) { + if (!udp.isClosed()) { + try { + InetAddress inet = InetAddress.getByName(targetip); + DatagramPacket dp = new DatagramPacket(bb, bb.length, inet, targetport); + udp.send(dp); + txcount++; + raise_txrxstatistic(); + return true; + } catch(SecurityException e) { + // dari InetAddress.getByName + raise_log("SendData to ["+targetip+":"+targetport+"] failed, Exception : "+e.getMessage()); + } catch (UnknownHostException e) { + // dari InetAddress.getByName + raise_log("SendData to ["+targetip+":"+targetport+"] failed, Exception : "+e.getMessage()); + } catch (IOException e) { + // dari udp.send + raise_log("SendData to ["+targetip+":"+targetport+"] failed, Exception : "+e.getMessage()); + } + + } else raise_log("SendData to ["+targetip+":"+targetport+"] failed, UDP is closed"); + } else raise_log("SendData to ["+targetip+":"+targetport+"] failed, UDP is null"); + } else raise_log("SendData to ["+targetip+":"+targetport+"] failed, data is invalid "); + } else raise_log("SendData failed, targetport is invalid"); + } else raise_log("SendData failed, targetip is invalid"); + txfail++; + raise_txrxstatistic(); + return false; + } + + /** + * Open UDP Listening + * on success listening, will raise event eventname_socketstatus(isopen as boolean) + * on data receiving, will raise event eventname_newdata(bb() as byte, sourceip as string, sourceport as int) + * @param localport Local Port to listen + * @param maxpackagesize maximum package size + * @param localip Local IP to bind. Leave it empty/null to bind to any network interface + * @return Object to use in Wait For + */ + public Object Open(BA ba, int localport, final int maxpackagesize, String localip) { + + Object sender = new Object(); + BA.runAsync(ba, sender, event+"_socketstatus", new Object[] {false}, new Callable() { + + @Override + public Object[] call() { + boolean success = false; + InetAddress inet = null; + + try { + DatagramSocket tryudp = null; + if (mycodes.valid_string(localip)) { + inet = InetAddress.getByName(localip); + tryudp = new DatagramSocket(localport, inet); + } else { + tryudp = new DatagramSocket(localport); + } + + if (tryudp!=null) { + success = true; + if (getUDPIsOpened()) { + raise_log("Closing previous UDP Connection"); + Close(); + } + udp = tryudp; + reset_txrxstatistic(); + raise_txrxstatistic(); + raise_log("UDP Opened on port ["+localport+"]"); + BA.submitRunnable(new udpreceiver(ba,sender, maxpackagesize >0 ? maxpackagesize : 1500), null, 0); + } + + } catch(SecurityException e) { + // dari DatagramSocket dan dari InetAddress.getByName + raise_log("Open failed on port ["+localport+"], Exception : "+e.getMessage()); + } catch (SocketException e) { + // dari DatagramSocket + raise_log("Open failed on port ["+localport+"], Exception : "+e.getMessage()); + } catch (UnknownHostException e) { + // dari InetAddress.getByName + raise_log("Open failed on port ["+localport+"], Exception : "+e.getMessage()); + } + + + return new Object[] {success}; + } + + }); + return sender; + } + + + + /** + * Runnable class to receive UDP Data + */ + private class udpreceiver implements Runnable{ + final int maxsize; + final Object msender; + final BA mba; + boolean need_newdata_event = false; + /** + * Initialize UDP Receiver + * @param ba BA Object + * @param sender Sender object to use in Wait For + * @param packagemaxsize maximum package size + */ + public udpreceiver(BA ba, Object sender, int packagemaxsize) { + maxsize = packagemaxsize; + msender = sender; + mba = ba; + if (ba!=null) { + need_newdata_event = ba.subExists(event+"_newdata"); + } + } + @Override + public void run() { + while (getUDPIsOpened()) { + byte[] bb = new byte[maxsize]; + DatagramPacket dp = new DatagramPacket(bb, bb.length); + try { + udp.receive(dp); + rxcount++; + raise_txrxstatistic(); + byte[] data = new byte[dp.getLength()]; + System.arraycopy(dp.getData(), 0, data, 0, dp.getLength()); + String sourceip = dp.getAddress().getHostAddress(); + int sourceport = dp.getPort(); + if (need_newdata_event) mba.raiseEventFromDifferentThread(msender, null, 0, event+"_newdata", false, new Object[] {data, sourceip, sourceport }); + + } catch (IOException e) { + rxfail++; + raise_txrxstatistic(); + raise_log("UDPReceiver failed, IOException Msg : " + e.getMessage()); + } + } + + } + + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_txrxstatistic() { + if (need_txrxstatistic_event) + ba.raiseEventFromDifferentThread(Me, null, 0, event + "_txrxstatistic", false, + new Object[] { txcount, txfail, rxcount, rxfail }); + } + + +} diff --git a/src/androgpio/gps/AdzanCalculator.java b/src/androgpio/gps/AdzanCalculator.java new file mode 100644 index 0000000..03ab557 --- /dev/null +++ b/src/androgpio/gps/AdzanCalculator.java @@ -0,0 +1,72 @@ +package androgpio.gps; + + + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +@BA.ShortName("AdzanCalculator") +public class AdzanCalculator { + + private PrayTime pt; + + /** + * Initialize Adzan Calculator dengan parameter + * time format : 24H + * metode Safii + * + */ + public void Initialize() { + pt = new PrayTime(); + pt.setTimeFormat(pt.getTime24()); + pt.setCalcMethod(pt.getCustom()); // belum tau + pt.setAsrJuristic(pt.getShafii()); // Ashar Juristic --> Safii , dah betul + pt.setAdjustHighLats(pt.getAngleBased()); + int[] offsets = {0, 0, 0, 0, 0, 0, 0}; // {Fajr,Sunrise,Dhuhr,Asr,Sunset,Maghrib,Isha} + pt.tune(offsets); + } + + /** + * Ambil konstanta nama waktu sholat + * @return List + */ + public List GetPrayerNames(){ + List result = new List(); + result.Initialize(); + for(String xx : pt.getTimeNames()) { + result.Add(xx); + } + return result; + } + + /** + * Ambil waktu sholat + * @param year tahun + * @param month bulan + * @param day hari + * @param latitude latitude + * @param longitude longitude + * @param TimeZone timezone + * @return List + */ + public List GetPrayerTime(int year, int month, int day, double latitude, double longitude, double TimeZone){ + // batasi latitude dan longitude untuk 1 digit di belakang koma + List result = new List(); + result.Initialize(); + for(String xx:pt.getDatePrayerTimes(year, month, day, convert_1_desimal(latitude), convert_1_desimal(longitude), TimeZone)) { + result.Add(xx); + } + return result; + } + + + private double convert_1_desimal(double source) { + int xx = (int)(source * 10); + return xx/10.0; + } + + @SuppressWarnings("unused") + private double convert_2_desimal(double source) { + int xx = (int)(source * 100); + return xx / 100.0; + } +} diff --git a/src/androgpio/gps/DataKota.java b/src/androgpio/gps/DataKota.java new file mode 100644 index 0000000..2e1a8a4 --- /dev/null +++ b/src/androgpio/gps/DataKota.java @@ -0,0 +1,39 @@ +package androgpio.gps; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("DataKota") +public class DataKota { + public final String NamaKota; + public final double Latitude; + public final double Longitude; + /** + * Initialize DataKota with empty value + */ + public DataKota() { + this.NamaKota=""; + this.Latitude=0; + this.Longitude=0; + } + + @BA.Hide + /** + * Initialize DataKota with specific value + * @param namakota dalam string + * @param latitude dalam double + * @param longitude dalam double + */ + public DataKota(String namakota, double latitude, double longitude) { + this.NamaKota = namakota; + this.Latitude = latitude; + this.Longitude = longitude; + } + + /** + * Cek apakah ada data + * @return true kalau ada data + */ + public boolean HaveValue() { + return !NamaKota.isEmpty(); + } +} diff --git a/src/androgpio/gps/GpsGPGGA.java b/src/androgpio/gps/GpsGPGGA.java new file mode 100644 index 0000000..8e56048 --- /dev/null +++ b/src/androgpio/gps/GpsGPGGA.java @@ -0,0 +1,163 @@ +package androgpio.gps; + +import java.util.ArrayList; +import java.util.List; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Common; + +@BA.ShortName("GpsGPGGA") +public class GpsGPGGA { + + public String invalidmsg = ""; + public byte UTC_Hour = -1; + public byte UTC_Minute = -1; + public byte UTC_Second = -1; + public double Latitude = 0; + public double Longitude = 0; + public double HDOP = -1; + public boolean PositionFixed = false; + public int Satellites = -1; + public double Altitude = -1; + public double GeoID_Height = -1; + + public GpsGPGGA(String msg) { + String[] partB = msg.split(","); // pisahkan konten berdasar koma + if (partB.length<13) { + invalidmsg = "GPGGA Split is less than 13"; + return; + } + + List err = new ArrayList(); + + // waktu hhmmss.sss + byte[] utc = new byte[] {0,0,0}; + if (mycodes.Gps_UTCTime(partB[1],utc)) { + UTC_Hour = utc[0]; + UTC_Minute = utc[1]; + UTC_Second = utc[2]; + } else { + err.add("GPGGA dont have Time"); + } + + // Latitude + double[] result; + result = new double[] {0}; + if (mycodes.Gps_Latitude_formatchange(partB[2], result)) { + Latitude = result[0]; + } else { + err.add("GPGGA Invalid Latitude : "+partB[2]); + } + + // Latitude polarity, N or S + switch(mycodes.Gps_Char(partB[3])) { + case 'N' : + Latitude = mycodes.SetPositive(Latitude); // hasil harus positif + break; + case 'S' : + Latitude = mycodes.SetNegative(Latitude); // hasil harus negatif + break; + default : + Latitude = 0; // tidak jelas + err.add("GPGGA Invalid Latitude Polarity : "+partB[3]); + break; + } + + // Longitude + result = new double[] {0}; + if (mycodes.Gps_Longitude_formatchange(partB[4], result)) { + Longitude = result[0]; + } else { + err.add("GPGGA Invalid Longitude : "+partB[4]); + } + + // Longitude polarity, W or S + + switch(mycodes.Gps_Char(partB[5])) { + case 'W' : + Longitude = mycodes.SetNegative(Longitude); // hasil harus negatif + break; + case 'E' : + Longitude = mycodes.SetPositive(Longitude); // hasil harus positif + break; + default : + Longitude = 0; // tidak jelas + err.add("GPGGA Invalid Longitude Polarity : "+partB[5]); + break; + } + + // Position Fix, 0 / 1 / 2 + try { + int posfix = Integer.parseInt(partB[6]); + if (posfix<1) PositionFixed = false; else PositionFixed = true; + } catch(NumberFormatException e) { + err.add("GPGGA Invalid PositionFix : "+partB[6]); + } + + // Number of satelites + try { + int satfix = Integer.parseInt(partB[7]); + Satellites = satfix; + } catch(NumberFormatException e) { + err.add("GPGGA Invalid Satelite : "+partB[7]); + + } + + // HDOP di index 8 + try { + double hd = Double.parseDouble(partB[8]); + HDOP = hd; + }catch(NumberFormatException e) { + err.add("GPGGA Invalid HDOP : "+partB[8]); + } + + // Altitude , nilai di 9, M di 10 + try { + double alt = Double.parseDouble(partB[9]); + Altitude = alt; + } catch(NumberFormatException e) { + err.add("GPGGA Invalid Altitude : "+partB[9]); + } + + if (mycodes.Gps_Char(partB[10])!='M') { + err.add("GPGGA Altitude M not found"); + } + + // GeoID_height, nilai di 11, M di 12 + try { + double geoid = Double.parseDouble(partB[11]); + GeoID_Height = geoid; + } catch(NumberFormatException e) { + err.add("GPGGA Invalid Altitude : "+partB[11]); + } + + if (mycodes.Gps_Char(partB[12])!='M') { + err.add("GPGGA GeoID M is not found"); + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } + + /** + * Get UTC Time String in format hh:mm:ss + * @return UTC Time String in format hh:mm:ss, or empty string if invalid data + */ + public String UTC_Time_hhmmss() { + if (isValid()) { + StringBuilder str = new StringBuilder(); + str.append(Common.NumberFormat(UTC_Hour, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Minute, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Second,2,0)); + return str.toString(); + } else return ""; + } + + +} diff --git a/src/androgpio/gps/GpsGPGLL.java b/src/androgpio/gps/GpsGPGLL.java new file mode 100644 index 0000000..a2fe206 --- /dev/null +++ b/src/androgpio/gps/GpsGPGLL.java @@ -0,0 +1,121 @@ +package androgpio.gps; + +import java.util.ArrayList; +import java.util.List; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Common; + +@BA.ShortName("GpsGPGLL") +public class GpsGPGLL { + public String invalidmsg = ""; + public double Latitude = -1; + public double Longitude = -1; + public byte UTC_Hour = -1; + public byte UTC_Minute = -1; + public byte UTC_Second = -1; + public boolean PositionFixed = false; + public GpsGPGLL(String msg) { + String[] partB = msg.split(","); + if (partB.length<7) { + invalidmsg = "GPGLL split less than 7"; + return; + } + + List err = new ArrayList(); + + double[] result; + // Latitude + result = new double[] {0}; + if (mycodes.Gps_Latitude_formatchange(partB[1], result)) { + Latitude = result[0]; + } else { + err.add("GPGLL Invalid Latitude : "+partB[1]); + } + + // Latitude polarity, N or S + switch(mycodes.Gps_Char(partB[2])) { + case 'N' : + Latitude = mycodes.SetPositive(Latitude); // hasil harus positif + break; + case 'S' : + Latitude = mycodes.SetNegative(Latitude); // hasil harus negatif + break; + default : + Latitude = 0; // gak jelas + err.add("GPGLL Invalid Latitude Polarity : "+partB[2]); + break; + } + + + // Longitude + result = new double[] {0}; + if (mycodes.Gps_Longitude_formatchange(partB[3], result)) { + Longitude = result[0]; + } else { + err.add("GPGLL Invalid Longitude : "+partB[3]); + } + + // Longitude polarity, W or S + switch(mycodes.Gps_Char(partB[4])) { + case 'W' : + Longitude = mycodes.SetNegative(Longitude); // hasil harus negatif + break; + case 'E' : + Longitude = mycodes.SetPositive(Longitude); // hasil harus positif + break; + default : + Longitude = 0; + err.add("GPGLL Invalid Longitude Polarity : "+partB[4]); + break; + } + + // UTC Time + byte[] utc = new byte[] {0,0,0}; + if (mycodes.Gps_UTCTime(partB[5], utc)) { + UTC_Hour = utc[0]; + UTC_Minute = utc[1]; + UTC_Second = utc[2]; + } else { + err.add("GPGLL dont have Time"); + + } + + // Fixed Status + switch(mycodes.Gps_Char(partB[6])) { + case 'A' : + PositionFixed = true; + break; + case 'V' : + PositionFixed = false; + break; + default : + err.add("GPGLL Invalid PositionFix : "+partB[6]); + break; + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } + + /** + * Get UTC Time String in format hh:mm:ss + * @return UTC Time String in format hh:mm:ss, or empty string if invalid data + */ + public String UTC_Time_hhmmss() { + if (isValid()) { + StringBuilder str = new StringBuilder(); + str.append(Common.NumberFormat(UTC_Hour, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Minute, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Second,2,0)); + return str.toString(); + } else return ""; + } +} diff --git a/src/androgpio/gps/GpsGPGSA.java b/src/androgpio/gps/GpsGPGSA.java new file mode 100644 index 0000000..89ca9fd --- /dev/null +++ b/src/androgpio/gps/GpsGPGSA.java @@ -0,0 +1,98 @@ +package androgpio.gps; + +import java.util.ArrayList; +import java.util.List; + +import anywheresoftware.b4a.BA; +import androgpio.mycodes; + +@BA.ShortName("GpsGPGSA") +public class GpsGPGSA { + public String invalidmsg = ""; + public String Mode = ""; + public String LockMode = ""; + public int[] SatelitteID = new int[] {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; + public double PDOP = -1; + public double HDOP = -1; + public double VDOP = -1; + + public GpsGPGSA(String msg) { + String[] partB = msg.split(","); + if (partB.length<18) { + invalidmsg = "GPGSA Split less than 18"; + return; + } + + List err = new ArrayList(); + switch(mycodes.Gps_Char(partB[1])) { + case 'M' : + Mode = "Manual"; + break; + case 'A' : + Mode = "Automatic"; + break; + default : + Mode = ""; + err.add("GPGSA Mode is invalid : "+partB[1]); + break; + } + + int[] intresult = new int[] {0}; + if (mycodes.Gps_Int(partB[2], intresult)) { + switch(intresult[0]) { + case 1 : + LockMode = "No Fix"; + break; + case 2 : + LockMode = "2D"; + break; + case 3 : + LockMode = "3D"; + break; + default : + LockMode = ""; + err.add("GPGSA LockMode is invalid : "+partB[2]); + break; + } + } else { + err.add("GPGSA Lockmode is invalid : "+partB[2]); + } + + // satelite , 12 unit + for (int jj=0;jj<12;jj++) { + if (mycodes.Gps_Int(partB[3+jj], intresult)) { + SatelitteID[jj] = intresult[0]; + } else SatelitteID[jj] = -1; + } + + double[] doubleresult = new double[] {0}; + + // PDOP = 15 + if (mycodes.Gps_Double(partB[15], doubleresult)) { + PDOP = doubleresult[0]; + } else { + err.add("GPGSA Invalid PDOP : "+partB[15]); + } + + // HDOP = 16 + if (mycodes.Gps_Double(partB[16], doubleresult)) { + HDOP = doubleresult[0]; + } else { + err.add("GPGSA Invalid HDOP : "+partB[16]); + } + + // VDOP = 17 + if (mycodes.Gps_Double(partB[17], doubleresult)) { + VDOP = doubleresult[0]; + } else { + err.add("GPGSA Invalid VDOP : "+partB[17]); + + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } +} diff --git a/src/androgpio/gps/GpsGPRMC.java b/src/androgpio/gps/GpsGPRMC.java new file mode 100644 index 0000000..9e7341a --- /dev/null +++ b/src/androgpio/gps/GpsGPRMC.java @@ -0,0 +1,194 @@ +package androgpio.gps; + +import java.util.ArrayList; +import java.util.List; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Common; +import androgpio.mycodes; + +@BA.ShortName("GpsGPRMC") +public class GpsGPRMC { + public String invalidmsg = ""; + public byte UTC_Hour = -1; + public byte UTC_Minute = -1; + public byte UTC_Second = -1; + public byte UTC_Day = -1; + public byte UTC_Month = -1; + public int UTC_Year = -1; + public boolean PositionFix = false; + public double Latitude = 0; + public double Longitude = 0; + + /** + * In Knot + */ + public double Ground_Speed = 0; + + /** + * In Degree + */ + public double Ground_Course = 0; + + /** + * In Degree + */ + public double Magnetic_Variation = 0; + + + public GpsGPRMC(String msg) { + + String[] partB = msg.split(","); + + if (partB.length<12) { + invalidmsg = "GPRMC Split less than 12"; + return; + } + + List err = new ArrayList(); + + byte[] utctime = new byte[] {0,0,0}; + if (mycodes.Gps_UTCTime(partB[1], utctime)) { + UTC_Hour = utctime[0]; + UTC_Minute = utctime[1]; + UTC_Second = utctime[2]; + } else { + err.add("GPRMC dont have Time") ; + } + + switch(mycodes.Gps_Char(partB[2])) { + case 'A' : + PositionFix = true; + break; + case 'V' : + PositionFix = false; + break; + default : + err.add("GPRMC Invalid Position Fix : "+partB[2]); + break; + } + + double[] result; + result = new double[] {0}; + if (mycodes.Gps_Latitude_formatchange(partB[3], result)) { + Latitude = result[0]; + } else { + err.add("GPRMC Invalid Latitude : "+partB[3]); + } + + switch(mycodes.Gps_Char(partB[4])) { + case 'N' : + Latitude = mycodes.SetPositive(Latitude); //hasil harus positif + break; + case 'S' : + Latitude = mycodes.SetNegative(Latitude); // hasil harus negatif; + break; + default : + Latitude = 0; + err.add("GPRMC Invalid Latitude Polarity : "+partB[4]); + break; + } + + result = new double[] {0}; + if (mycodes.Gps_Longitude_formatchange(partB[5], result)) { + Longitude = result[0]; + } else { + err.add("GPRMC Invalid Longitude : "+partB[5]); + } + + switch(mycodes.Gps_Char(partB[6])) { + case 'W' : + Longitude = mycodes.SetNegative(Longitude); // hasil harus negative + break; + case 'E' : + Longitude = mycodes.SetPositive(Longitude); // hasil positive + break; + default : + err.add("GPRMC Invalid Longitude Polarity : "+partB[6]); + break; + } + + try { + double knot = Double.parseDouble(partB[7]); + Ground_Speed = knot; + } catch( NumberFormatException e) { + err.add("GPRMC invalid Ground Speed "+partB[7]); + Ground_Speed = -1; + } + + try { + double course = Double.parseDouble(partB[8]); + Ground_Course = course; + } catch( NumberFormatException e) { + err.add("GPRMC invalid Ground Course "+partB[8]); + Ground_Course =-1; + } + + byte[] utcdate = new byte[] {0,0,0}; + if (mycodes.Gps_UTCDate(partB[9], utcdate)) { + UTC_Day = utcdate[0]; + UTC_Month = utcdate[1]; + UTC_Year = 2000 + utcdate[2]; + } else { + err.add("GPRMC dont have Date"); + } + + try { + double mag = Double.parseDouble(partB[10]); + Magnetic_Variation = mag; + }catch( NumberFormatException e) { + err.add("GPRMC invalid Magnetic Variation "+partB[10]); + } + + switch(mycodes.Gps_Char(partB[11])) { + case 'E' : + Magnetic_Variation = mycodes.SetPositive(Magnetic_Variation); + break; + case 'W' : + Magnetic_Variation = mycodes.SetNegative(Magnetic_Variation); + break; + default : + err.add("GPRMC invalid Magnetic Polarity : "+partB[11]); + break; + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } + + /** + * Get UTC Time String in format hh:mm:ss + * @return UTC Time String in format hh:mm:ss, or empty string if invalid data + */ + public String UTC_Time_hhmmss() { + if (PositionFix) { + StringBuilder str = new StringBuilder(); + str.append(Common.NumberFormat(UTC_Hour, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Minute, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Second,2,0)); + return str.toString(); + } else return ""; + } + + /** + * Get UTC Date String in format dd-MM-yyyy + * @return UTC Time String in format dd-MM-yyyy , or empty string if invalid data + */ + public String UTC_Date_ddMMyyyy() { + if (PositionFix) { + StringBuilder str = new StringBuilder(); + str.append(Common.NumberFormat(UTC_Day, 2, 0)); + str.append("-"); + str.append(Common.NumberFormat(UTC_Month, 2, 0)); + str.append("-"); + str.append(UTC_Year); + return str.toString(); + } else return ""; + } + +} diff --git a/src/androgpio/gps/GpsGPVTG.java b/src/androgpio/gps/GpsGPVTG.java new file mode 100644 index 0000000..f2ded91 --- /dev/null +++ b/src/androgpio/gps/GpsGPVTG.java @@ -0,0 +1,83 @@ +package androgpio.gps; + +import java.util.ArrayList; +import java.util.List; + +import anywheresoftware.b4a.BA; +import androgpio.mycodes; + +@BA.ShortName("GpsGPVTG") +public class GpsGPVTG { + public String invalidmsg = ""; + public double Course_Heading = 0; + public double Magnetic_Heading = 0; + public double Horizontal_Knot_Speed = 0; + public double Horizontal_KM_Speed = 0; + + public GpsGPVTG(String msg) { + String partB[] = msg.split(","); + if (partB.length<8) { + invalidmsg = "GPVTG Split less than 8"; + return; + } + + List err = new ArrayList(); + + double[] result = new double[] {0}; + if (mycodes.Gps_Double(partB[1], result)) { + Course_Heading = result[0]; + } else { + Course_Heading = 0; + err.add("GPVTG invalid Course Heading : "+partB[1]); + } + + if (mycodes.Gps_Char(partB[2])!='T') { + Course_Heading = 0; + err.add("GPVTG Course Heading code is not T"); + } + + + if (mycodes.Gps_Double(partB[3], result)) { + Magnetic_Heading = result[0]; + } else { + Magnetic_Heading = 0; + err.add("GPVTG invalid Magnetic Heading : "+partB[3]); + } + + if (mycodes.Gps_Char(partB[4])!='M') { + Magnetic_Heading = 0; + err.add("GPVTG Magnetic Heading code is not M"); + } + + + if (mycodes.Gps_Double(partB[5], result)) { + Horizontal_Knot_Speed = result[0]; + } else { + Horizontal_Knot_Speed = 0; + err.add("GPVTG Invalid Horizontal Knot Speed : "+partB[5]); + } + + if (mycodes.Gps_Char(partB[6])!='N') { + Horizontal_Knot_Speed = 0; + err.add("GPVTG Horizontal Knot Speed code is not N"); + } + + if (mycodes.Gps_Double(partB[7], result)) { + Horizontal_KM_Speed = result[0]; + } else { + Horizontal_KM_Speed = 0; + err.add("GPVTG Invalid Horizontal KM Speed : "+partB[7]); + } + + if (mycodes.Gps_Char(partB[8])!='K') { + Horizontal_KM_Speed = 0; + err.add("GPVTG Horizontal KM Speed code is not K"); + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } +} diff --git a/src/androgpio/gps/GpsReceiver.java b/src/androgpio/gps/GpsReceiver.java new file mode 100644 index 0000000..3a83124 --- /dev/null +++ b/src/androgpio/gps/GpsReceiver.java @@ -0,0 +1,489 @@ +package androgpio.gps; + +import java.util.ArrayList; +import java.util.List; + +import com.fazecast.jSerialComm.SerialPort; +import com.fazecast.jSerialComm.SerialPortEvent; +import com.fazecast.jSerialComm.SerialPortInvalidPortException; +import com.fazecast.jSerialComm.SerialPortMessageListener; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("GpsReceiver") +@BA.Events(values= { + "log(msg as string)", + "newgpstime(value as string)", + "newgpsdate(value as string)", + "newgpslatitude(value as double)", + "newgpslongitude(value as double)", + + "rawdata(gpsmsg as string)", + "ggadata(data as GpsGPGGA)", + "glldata(data as GpsGPGLL)", +// "gsadata(data as GpsGPGSA)", +// "vtgdata(data as GpsGPVTG)", + "rmcdata(data as GpsGPRMC)" +}) + +@BA.DependsOn(values = { "jSerialComm-2.9.2" }) + +public class GpsReceiver { + + private BA bax; + private String event = ""; + private Object caller; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_rawdata_event = false; + private boolean need_ggadata_event = false; + private boolean need_newgpstime_event = false; + private boolean need_newgpsdate_event = false; + private boolean need_newgpslatitude_event = false; + private boolean need_newgpslongitude_event = false; + + private boolean need_glldata_event = false; + //private boolean need_gsadata_event = false; // tidak terlalu penting + private boolean need_rmcdata_event = false; + //private boolean need_vtgdata_event = false; // tidak terlalu penting + + private SerialPort myport; + private boolean initialized = false; + private long rxcount = 0; + + private String last_gps_time = ""; + private String last_gps_date = ""; + private double last_gps_latitude = 0; + private double last_gps_longitude = 0; + private boolean gga_fixed = false; + private boolean gll_fixed = false; + private boolean rmc_fixed = false; + private int last_gps_day = 0; + private int last_gps_month = 0; + private int last_gps_year = 0; + private int last_gps_hour = 0; + private int last_gps_minute = 0; + private int last_gps_second = 0; + + + /** + * Initialize GpsReceiver + * @param eventname : eventname string + */ + public void Initialize(BA ba, Object caller, String eventname ) { + bax = ba; + event = eventname; + this.caller = caller; + + if (bax!=null) { + if (this.caller!=null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_rawdata_event = bax.subExists(event+"_rawdata"); + need_ggadata_event = bax.subExists(event+"_ggadata"); + + need_glldata_event = bax.subExists(event+"_glldata"); + //need_gsadata_event = bax.subExists(event+"_gsadata"); + need_rmcdata_event = bax.subExists(event+"_rmcdata"); + //need_vtgdata_event = bax.subExists(event+"_vtgdata"); + + need_newgpstime_event = bax.subExists(event+"_newgpstime"); + need_newgpsdate_event = bax.subExists(event+"_newgpsdate"); + need_newgpslatitude_event = bax.subExists(event+"_newgpslatitude"); + need_newgpslongitude_event = bax.subExists(event+"_newgpslongitude"); + } + } + } + + + if (SerialPort.getVersion()!="") initialized = true; else initialized = false; + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + ClosePort(); + } + }); + } + + + + /** + * Check if GpsReceiver is initialized + * @return true if initialized + */ + public boolean IsInitialized() { + return initialized; + } + + /** + * Get all available port names in this system + * @return List of Serial Port Names + */ + public List GetPortNames(){ + List result = new ArrayList(); + if (initialized) { + for(SerialPort xx:SerialPort.getCommPorts()) { + if (xx != null) { + String portname = xx.getSystemPortName(); + if (portname!="") { + result.add(portname); + } + } + } + } else { + raise_log("GetPortNames failed, SerialPort Library not ready"); + } + return result; + } + + /** + * Close GPS Receiving Port + */ + public void ClosePort() { + if (myport instanceof SerialPort) { + myport.removeDataListener(); + + myport.closePort(); + myport = null; + } + + } + + /** + * How many GPS data received + * @return how many gps data received + */ + public long getRXCounter() { + return rxcount; + } + + public double GetLatitude_rounded(int fractiondigits) { + if (fractiondigits<1) fractiondigits = 1; + if (fractiondigits>4) fractiondigits = 4; + switch(fractiondigits) { + case 1 : + return Math.round(last_gps_latitude * 10.0) / 10.0; + case 2 : + return Math.round(last_gps_latitude * 100.0) / 100.0; + case 3 : + return Math.round(last_gps_latitude * 1000.0) / 1000.0; + default : + return Math.round(last_gps_latitude * 10000.0) / 10000.0; + } + } + + public double GetLongitude_rounded(int fractiondigits) { + if (fractiondigits<1) fractiondigits = 1; + if (fractiondigits>4) fractiondigits = 4; + switch(fractiondigits) { + case 1 : + return Math.round(last_gps_longitude * 10.0) / 10.0; + case 2 : + return Math.round(last_gps_longitude * 100.0) / 100.0; + case 3 : + return Math.round(last_gps_longitude * 1000.0) / 1000.0; + default : + return Math.round(last_gps_longitude * 10000.0) / 10000.0; + } + } + + /** + * Get last GPS Latitude + * @return Latitude value in double + */ + public double getLatitude() { + return last_gps_latitude; + } + + /** + * Get Last GPS Longitude + * @return Longitude value in double + */ + public double getLongitude() { + return last_gps_longitude; + } + + /** + * Get Last GPS Time + * @return time in format hh:mm:ss + */ + public String getTime_hhmmss() { + return last_gps_time; + } + + /** + * Get Last GPS Date + * @return date in format dd-MM-yyyy + */ + public String getDate_ddMMyyyy() { + return last_gps_date; + } + + /** + * Get GPS Fixed status + * @return true if GPS is fixed + */ + public boolean getGPS_Fixed() { + return (gga_fixed || gll_fixed || rmc_fixed); + } + + public int getUTC_Day() { + return last_gps_day; + } + + public int getUTC_Month() { + return last_gps_month; + } + + public int getUTC_Year() { + return last_gps_year; + } + + public int getUTC_Hour() { + return last_gps_hour; + } + + public int getUTC_Minute() { + return last_gps_minute; + } + + public int getUTC_Second() { + return last_gps_second; + } + + private void setUTC_Time(int hour, int minute, int second) { + last_gps_hour = hour; + last_gps_minute = minute; + last_gps_second = second; + } + + private void setUTC_Date(int day, int month, int year) { + last_gps_day = day; + last_gps_month = month; + last_gps_year = year; + } + + /** + * Open Serial Port for Gps Communication + * @param serialname : serialport name + * @param baudrate : default is 9600 + * @return true if success + */ + public boolean OpenPort(String serialname, int baudrate) { + rxcount = 0; + if (baudrate==0) baudrate = 9600; // default baudrate + if (initialized) { + try { + myport = SerialPort.getCommPort(serialname); + if (myport.openPort()) { + myport.setBaudRate(baudrate); + if (myport.addDataListener(new SerialPortMessageListener() { + + @Override + public int getListeningEvents() { + return SerialPort.LISTENING_EVENT_DATA_RECEIVED; + } + + @Override + public void serialEvent(SerialPortEvent arg0) { + if (arg0==null) return; + byte[] bb = arg0.getReceivedData(); + if (bb==null) return; + if (bb.length<1) return; + String msg = new String(bb); + + int indexmulai = msg.indexOf("$GP"); + if (indexmulai<0) return; + + String vv = msg.substring(indexmulai); + if (vv.startsWith("$GPGSV")) return; // ignore GPGSV + raise_rawdata(vv); + process_data(vv); + rxcount+=1; + + } + + @Override + public boolean delimiterIndicatesEndOfMessage() { + // delimiter ada di belakang + return true; + } + + @Override + public byte[] getMessageDelimiter() { + // delimiter adalah CR LF + return new byte[] {(byte) 13, (byte) 10}; + } + + })) + return true; + } + } catch(SerialPortInvalidPortException e) { + raise_exception("Failed to GetCommPort",e); + } + } + + return false; + } + + /** + * Check if SerialPort is opened and ready + * @return true if opened and ready + */ + public boolean PortIsReady() { + if (myport instanceof SerialPort) { + return myport.isOpen(); + } + return false; + } + + private void process_data(String vv) { + String[] gps_split = vv.split("\\*"); + if (gps_split.length!=2) return; + String gpsmsg = gps_split[0]; + + if (gpsmsg.startsWith("$GPGGA")) { + GpsGPGGA datagga = new GpsGPGGA(gpsmsg); + // supaya kalau udah fix, ada UTC Latitude, Longitude, UTC Time , dah bisa raise event + gga_fixed = datagga.PositionFixed; + if (gga_fixed) + { + raise_ggadata(datagga); + String time = datagga.UTC_Time_hhmmss(); + if (last_gps_time.compareTo(time)!=0) { + last_gps_time = time; + setUTC_Time(datagga.UTC_Hour, datagga.UTC_Minute, datagga.UTC_Second); + raise_newgpstime(last_gps_time); + } + if (last_gps_latitude != datagga.Latitude) { + last_gps_latitude = datagga.Latitude; + raise_newgpslatitude(last_gps_latitude); + } + if (last_gps_longitude != datagga.Longitude) { + last_gps_longitude = datagga.Longitude; + raise_newgpslongitude(last_gps_longitude); + } + } + return; + } + + // ini tidak perlu, gak penting + /* + * if (gpsmsg.startsWith("$GPGSA")) { GpsGPGSA datagsa = new GpsGPGSA(gpsmsg); + * if (datagsa.isValid()) { raise_gsadata(datagsa); } else { + * BA.Log(datagsa.invalidmsg); } return; } + */ + + if (gpsmsg.startsWith("$GPGLL")) { + GpsGPGLL datagll = new GpsGPGLL(gpsmsg); + // supaya kalau udah fix, ada UTC Latitude, Longitude, UTC Time, dah bisa raise event + gll_fixed = datagll.PositionFixed; + if (gll_fixed) + { + raise_glldata(datagll); + String time = datagll.UTC_Time_hhmmss(); + if (last_gps_time.compareTo(time)!=0) { + last_gps_time = time; + setUTC_Time(datagll.UTC_Hour, datagll.UTC_Minute, datagll.UTC_Second); + raise_newgpstime(last_gps_time); + } + if (last_gps_latitude != datagll.Latitude) { + last_gps_latitude = datagll.Latitude; + raise_newgpslatitude(last_gps_latitude); + } + if (last_gps_longitude != datagll.Longitude) { + last_gps_longitude = datagll.Longitude; + raise_newgpslongitude(last_gps_longitude); + } + } + return; + } + + if (gpsmsg.startsWith("$GPRMC")) { + GpsGPRMC datarmc = new GpsGPRMC(gpsmsg); + // GPRMC Groud Course, Magnetic Variation, dan Magnetic Polarity sering loss + // jadi tidak pakai isValid, tapi pakai PositionFix aja + rmc_fixed = datarmc.PositionFix; + if (rmc_fixed) + { + raise_rmcdata(datarmc); + String time = datarmc.UTC_Time_hhmmss(); + if (last_gps_time.compareTo(time)!=0) { + last_gps_time = time; + setUTC_Time(datarmc.UTC_Hour, datarmc.UTC_Minute, datarmc.UTC_Second); + raise_newgpstime(last_gps_time); + } + + String date = datarmc.UTC_Date_ddMMyyyy(); + if (last_gps_date.compareTo(date)!=0) { + last_gps_date = date; + setUTC_Date(datarmc.UTC_Day, datarmc.UTC_Month, datarmc.UTC_Year); + raise_newgpsdate(last_gps_date); + } + if (last_gps_latitude != datarmc.Latitude) { + last_gps_latitude = datarmc.Latitude; + raise_newgpslatitude(last_gps_latitude); + } + if (last_gps_longitude != datarmc.Longitude) { + last_gps_longitude = datarmc.Longitude; + raise_newgpslongitude(last_gps_longitude); + } + } + return; + } + + // ini tidak perlu, gak penting + /* + * if (gpsmsg.startsWith("$GPVTG")) { GpsGPVTG datavtg = new GpsGPVTG(gpsmsg); + * if (datavtg.isValid()) { raise_vtgdata(datavtg); } else { + * BA.Log(datavtg.invalidmsg); } return; } + */ + } + + private void raise_exception(String prefix, Exception e) { + raise_log(prefix+", Msg : "+e.getMessage()+", Caused : "+e.getCause()); + } + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_rawdata(String vv) { + if (need_rawdata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_rawdata", false, new Object[] {vv}); + } + + private void raise_ggadata(GpsGPGGA vv) { + if (need_ggadata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_ggadata", false, new Object[] {vv}); + } + + + + private void raise_glldata(GpsGPGLL vv) { + if (need_glldata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_glldata", false, new Object[] {vv}); + } + + + + private void raise_rmcdata(GpsGPRMC vv) { + if (need_rmcdata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_rmcdata", false, new Object[] {vv}); + } + + + + private void raise_newgpstime(String value) { + if (need_newgpstime_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newgpstime", false, new Object[] {value}); + } + + private void raise_newgpsdate(String value) { + if (need_newgpsdate_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newgpsdate", false, new Object[] {value}); + } + + private void raise_newgpslatitude(double value) { + if (need_newgpslatitude_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newgpslatitude", false, new Object[] {value}); + } + + private void raise_newgpslongitude(double value) { + if (need_newgpslongitude_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newgpslongitude", false, new Object[] {value}); + } +} diff --git a/src/androgpio/gps/KotaIndonesia.java b/src/androgpio/gps/KotaIndonesia.java new file mode 100644 index 0000000..cd4ed26 --- /dev/null +++ b/src/androgpio/gps/KotaIndonesia.java @@ -0,0 +1,103 @@ +package androgpio.gps; + +import java.io.BufferedReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import androgpio.customsocket.JsonArray; +import androgpio.customsocket.JsonObject; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; + +@BA.ShortName("KotaIndonesia") +/** + * Daftar Kota dan Kabupaten di Indonesia + * Source : https://github.com/benangmerah/wilayah/blob/master/datasources/daftar-nama-daerah.csv + * Tanggal copy : 5 Agustus 2022 + * @author rdkartono + * + */ +public class KotaIndonesia { + private JsonArray ja; + public KotaIndonesia() { + ja = new JsonArray(); + ja.InitializeEmpty(); + try(InputStream is = KotaIndonesia.class.getResourceAsStream("kotaindonesiaminified.json")) { + BufferedReader bf = new BufferedReader(new InputStreamReader(is)); + String ss; + int ii=0; + do { + ss = bf.readLine(); + if (ss != null) { + // difilter minimal 3 huruf. Gak mungkin JSON string kurang dari 3 huruf + if (ss.length()>3) { + + JsonObject jo = new JsonObject(); + if (jo.InitializeFromString(ss)) { + ja.Put(jo); + ii+=1; + //BA.Log("Reading "+jo.ToString()); + } + } + } + + } while (ss != null); + is.close(); + BA.Log("Finish reading kotaindonesiaminified.json, count = "+ii); + } + catch(IOException|NullPointerException e) { + BA.Log("Exception on constructor KotaIndonesia, Msg : "+e.getMessage()+", Cause : "+e.getCause()); + } + + } + + /** + * Get JsonArray object + * @return JsonArray value + */ + public JsonArray getJsonArray() { + return ja; + } + + /** + * Search city by name + * @param searchtext nama kota, atau sebagian nama kota. Kasih string kosong untuk ambil semua + * @return List berisi object DataKota + */ + public List Search(String searchtext) { + List result = new List(); + result.Initialize(); + + if (ja!=null) { + if (ja.GetSize()>0) { + for(int ii=0;ii timeNames; + private String InvalidTime; // The string used for invalid times + // --------------------- Technical Settings -------------------- + private int numIterations; // number of iterations needed to compute times + // ------------------- Calc Method Parameters -------------------- + private HashMap methodParams; + + /* + * this.methodParams[methodNum] = new Array(fa, ms, mv, is, iv); + * + * fa : fajr angle ms : maghrib selector (0 = angle; 1 = minutes after + * sunset) mv : maghrib parameter value (in angle or minutes) is : isha + * selector (0 = angle; 1 = minutes after maghrib) iv : isha parameter value + * (in angle or minutes) + */ + @SuppressWarnings("unused") + private double[] prayerTimesCurrent; + private int[] offsets; + + public PrayTime() { + // Initialize vars + + this.setCalcMethod(0); + this.setAsrJuristic(0); + this.setDhuhrMinutes(0); + this.setAdjustHighLats(1); + this.setTimeFormat(0); + + // Calculation Methods + this.setJafari(0); // Ithna Ashari + this.setKarachi(1); // University of Islamic Sciences, Karachi + this.setISNA(2); // Islamic Society of North America (ISNA) + this.setMWL(3); // Muslim World League (MWL) + this.setMakkah(4); // Umm al-Qura, Makkah + this.setEgypt(5); // Egyptian General Authority of Survey + this.setTehran(6); // Institute of Geophysics, University of Tehran + this.setCustom(7); // Custom Setting + + // Juristic Methods + this.setShafii(0); // Shafii (standard) + this.setHanafi(1); // Hanafi + + // Adjusting Methods for Higher Latitudes + this.setNone(0); // No adjustment + this.setMidNight(1); // middle of night + this.setOneSeventh(2); // 1/7th of night + this.setAngleBased(3); // angle/60th of night + + // Time Formats + this.setTime24(0); // 24-hour format + this.setTime12(1); // 12-hour format + this.setTime12NS(2); // 12-hour format with no suffix + this.setFloating(3); // floating point number + + // Time Names + timeNames = new ArrayList(); + timeNames.add("Fajr"); + timeNames.add("Sunrise"); + timeNames.add("Dhuhr"); + timeNames.add("Asr"); + timeNames.add("Sunset"); + timeNames.add("Maghrib"); + timeNames.add("Isha"); + + InvalidTime = "-----"; // The string used for invalid times + + // --------------------- Technical Settings -------------------- + + this.setNumIterations(1); // number of iterations needed to compute + // times + + // ------------------- Calc Method Parameters -------------------- + + // Tuning offsets {fajr, sunrise, dhuhr, asr, sunset, maghrib, isha} + offsets = new int[7]; + offsets[0] = 0; + offsets[1] = 0; + offsets[2] = 0; + offsets[3] = 0; + offsets[4] = 0; + offsets[5] = 0; + offsets[6] = 0; + + /* + * + * fa : fajr angle ms : maghrib selector (0 = angle; 1 = minutes after + * sunset) mv : maghrib parameter value (in angle or minutes) is : isha + * selector (0 = angle; 1 = minutes after maghrib) iv : isha parameter + * value (in angle or minutes) + */ + methodParams = new HashMap(); + + // Jafari + double[] Jvalues = {16,0,4,0,14}; + methodParams.put(Integer.valueOf(this.getJafari()), Jvalues); + + // Karachi + double[] Kvalues = {18,1,0,0,18}; + methodParams.put(Integer.valueOf(this.getKarachi()), Kvalues); + + // ISNA + double[] Ivalues = {15,1,0,0,15}; + methodParams.put(Integer.valueOf(this.getISNA()), Ivalues); + + // MWL + double[] MWvalues = {18,1,0,0,17}; + methodParams.put(Integer.valueOf(this.getMWL()), MWvalues); + + // Makkah + double[] MKvalues = {18.5,1,0,1,90}; + methodParams.put(Integer.valueOf(this.getMakkah()), MKvalues); + + // Egypt + double[] Evalues = {19.5,1,0,0,17.5}; + methodParams.put(Integer.valueOf(this.getEgypt()), Evalues); + + // Tehran + double[] Tvalues = {17.7,0,4.5,0,14}; + methodParams.put(Integer.valueOf(this.getTehran()), Tvalues); + + // Custom + double[] Cvalues = {20,0,0,0,18}; // fajar = 20 degree, magrib selector = angle, magrib parameter = 0, isha selector = angle, isha parameter = 18 degree + methodParams.put(Integer.valueOf(this.getCustom()), Cvalues); + + } + + // ---------------------- Trigonometric Functions ----------------------- + // range reduce angle in degrees. + private double fixangle(double a) { + + a = a - (360 * (Math.floor(a / 360.0))); + + a = a < 0 ? (a + 360) : a; + + return a; + } + + // range reduce hours to 0..23 + private double fixhour(double a) { + a = a - 24.0 * Math.floor(a / 24.0); + a = a < 0 ? (a + 24) : a; + return a; + } + + // radian to degree + private double radiansToDegrees(double alpha) { + return ((alpha * 180.0) / Math.PI); + } + + // deree to radian + private double DegreesToRadians(double alpha) { + return ((alpha * Math.PI) / 180.0); + } + + // degree sin + private double dsin(double d) { + return (Math.sin(DegreesToRadians(d))); + } + + // degree cos + private double dcos(double d) { + return (Math.cos(DegreesToRadians(d))); + } + + // degree tan + private double dtan(double d) { + return (Math.tan(DegreesToRadians(d))); + } + + // degree arcsin + private double darcsin(double x) { + double val = Math.asin(x); + return radiansToDegrees(val); + } + + // degree arccos + private double darccos(double x) { + double val = Math.acos(x); + return radiansToDegrees(val); + } + + // degree arctan + @SuppressWarnings("unused") + private double darctan(double x) { + double val = Math.atan(x); + return radiansToDegrees(val); + } + + // degree arctan2 + private double darctan2(double y, double x) { + double val = Math.atan2(y, x); + return radiansToDegrees(val); + } + + // degree arccot + private double darccot(double x) { + double val = Math.atan2(1.0, x); + return radiansToDegrees(val); + } + + // ---------------------- Time-Zone Functions ----------------------- + // compute local time-zone for a specific date + @SuppressWarnings("unused") + private double getTimeZone1() { + TimeZone timez = TimeZone.getDefault(); + double hoursDiff = (timez.getRawOffset() / 1000.0) / 3600; + return hoursDiff; + } + + // compute base time-zone of the system + @SuppressWarnings("unused") + private double getBaseTimeZone() { + TimeZone timez = TimeZone.getDefault(); + double hoursDiff = (timez.getRawOffset() / 1000.0) / 3600; + return hoursDiff; + + } + + // detect daylight saving in a given date + @SuppressWarnings("unused") + private double detectDaylightSaving() { + TimeZone timez = TimeZone.getDefault(); + double hoursDiff = timez.getDSTSavings(); + return hoursDiff; + } + + // ---------------------- Julian Date Functions ----------------------- + // calculate julian date from a calendar date + private double julianDate(int year, int month, int day) { + + if (month <= 2) { + year -= 1; + month += 12; + } + double A = Math.floor(year / 100.0); + + double B = 2 - A + Math.floor(A / 4.0); + + double JD = Math.floor(365.25 * (year + 4716)) + + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5; + + return JD; + } + + // convert a calendar date to julian date (second method) + @SuppressWarnings("unused") + private double calcJD(int year, int month, int day) { + double J1970 = 2440588.0; + @SuppressWarnings("deprecation") + Date date = new Date(year, month-1, day); + + double ms = date.getTime(); // # of milliseconds since midnight Jan 1, + // 1970 + double days = Math.floor(ms / (1000.0 * 60.0 * 60.0 * 24.0)); + return J1970 + days - 0.5; + + } + + // ---------------------- Calculation Functions ----------------------- + // References: + // http://www.ummah.net/astronomy/saltime + // http://aa.usno.navy.mil/faq/docs/SunApprox.html + // compute declination angle of sun and equation of time + private double[] sunPosition(double jd) { + + double D = jd - 2451545; + double g = fixangle(357.529 + 0.98560028 * D); + double q = fixangle(280.459 + 0.98564736 * D); + double L = fixangle(q + (1.915 * dsin(g)) + (0.020 * dsin(2 * g))); + + // double R = 1.00014 - 0.01671 * [self dcos:g] - 0.00014 * [self dcos: + // (2*g)]; + double e = 23.439 - (0.00000036 * D); + double d = darcsin(dsin(e) * dsin(L)); + double RA = (darctan2((dcos(e) * dsin(L)), (dcos(L))))/ 15.0; + RA = fixhour(RA); + double EqT = q/15.0 - RA; + double[] sPosition = new double[2]; + sPosition[0] = d; + sPosition[1] = EqT; + + return sPosition; + } + + // compute equation of time + private double equationOfTime(double jd) { + double eq = sunPosition(jd)[1]; + return eq; + } + + // compute declination angle of sun + private double sunDeclination(double jd) { + double d = sunPosition(jd)[0]; + return d; + } + + // compute mid-day (Dhuhr, Zawal) time + private double computeMidDay(double t) { + double T = equationOfTime(this.getJDate() + t); + double Z = fixhour(12 - T); + return Z; + } + + // compute time for a given angle G + private double computeTime(double G, double t) { + + double D = sunDeclination(this.getJDate() + t); + double Z = computeMidDay(t); + double Beg = -dsin(G) - dsin(D) * dsin(this.getLat()); + double Mid = dcos(D) * dcos(this.getLat()); + double V = darccos(Beg/Mid)/15.0; + + return Z + (G > 90 ? -V : V); + } + + // compute the time of Asr + // Shafii: step=1, Hanafi: step=2 + private double computeAsr(double step, double t) { + double D = sunDeclination(this.getJDate() + t); + double G = -darccot(step + dtan(Math.abs(this.getLat() - D))); + return computeTime(G, t); + } + + // ---------------------- Misc Functions ----------------------- + // compute the difference between two times + private double timeDiff(double time1, double time2) { + return fixhour(time2 - time1); + } + + // -------------------- Interface Functions -------------------- + // return prayer times for a given date + public ArrayList getDatePrayerTimes(int year, int month, int day, + double latitude, double longitude, double tZone) { + this.setLat(latitude); + this.setLng(longitude); + this.setTimeZone(tZone); + this.setJDate(julianDate(year, month, day)); + double lonDiff = longitude / (15.0 * 24.0); + this.setJDate(this.getJDate() - lonDiff); + return computeDayTimes(); + } + + // return prayer times for a given date + public ArrayList getPrayerTimes(Calendar date, double latitude, + double longitude, double tZone) { + + int year = date.get(Calendar.YEAR); + int month = date.get(Calendar.MONTH); + int day = date.get(Calendar.DATE); + + return getDatePrayerTimes(year, month+1, day, latitude, longitude, tZone); + } + + // set custom values for calculation parameters + private void setCustomParams(double[] params) { + + for (int i = 0; i < 5; i++) { + if (params[i] == -1) { + params[i] = methodParams.get(this.getCalcMethod())[i]; + methodParams.put(this.getCustom(), params); + } else { + methodParams.get(this.getCustom())[i] = params[i]; + } + } + this.setCalcMethod(this.getCustom()); + } + + // set the angle for calculating Fajr + public void setFajrAngle(double angle) { + double[] params = {angle, -1, -1, -1, -1}; + setCustomParams(params); + } + + // set the angle for calculating Maghrib + public void setMaghribAngle(double angle) { + double[] params = {-1, 0, angle, -1, -1}; + setCustomParams(params); + + } + + // set the angle for calculating Isha + public void setIshaAngle(double angle) { + double[] params = {-1, -1, -1, 0, angle}; + setCustomParams(params); + + } + + // set the minutes after Sunset for calculating Maghrib + public void setMaghribMinutes(double minutes) { + double[] params = {-1, 1, minutes, -1, -1}; + setCustomParams(params); + + } + + // set the minutes after Maghrib for calculating Isha + public void setIshaMinutes(double minutes) { + double[] params = {-1, -1, -1, 1, minutes}; + setCustomParams(params); + + } + + // convert double hours to 24h format + public String floatToTime24(double time) { + + String result; + + if (Double.isNaN(time)) { + return InvalidTime; + } + + time = fixhour(time + 0.5 / 60.0); // add 0.5 minutes to round + int hours = (int)Math.floor(time); + double minutes = Math.floor((time - hours) * 60.0); + + if ((hours >= 0 && hours <= 9) && (minutes >= 0 && minutes <= 9)) { + result = "0" + hours + ":0" + Math.round(minutes); + } else if ((hours >= 0 && hours <= 9)) { + result = "0" + hours + ":" + Math.round(minutes); + } else if ((minutes >= 0 && minutes <= 9)) { + result = hours + ":0" + Math.round(minutes); + } else { + result = hours + ":" + Math.round(minutes); + } + return result; + } + + // convert double hours to 12h format + public String floatToTime12(double time, boolean noSuffix) { + + if (Double.isNaN(time)) { + return InvalidTime; + } + + time = fixhour(time + 0.5 / 60); // add 0.5 minutes to round + int hours = (int)Math.floor(time); + double minutes = Math.floor((time - hours) * 60); + String suffix, result; + if (hours >= 12) { + suffix = "pm"; + } else { + suffix = "am"; + } + hours = ((((hours+ 12) -1) % (12))+ 1); + /*hours = (hours + 12) - 1; + int hrs = (int) hours % 12; + hrs += 1;*/ + if (noSuffix == false) { + if ((hours >= 0 && hours <= 9) && (minutes >= 0 && minutes <= 9)) { + result = "0" + hours + ":0" + Math.round(minutes) + " " + + suffix; + } else if ((hours >= 0 && hours <= 9)) { + result = "0" + hours + ":" + Math.round(minutes) + " " + suffix; + } else if ((minutes >= 0 && minutes <= 9)) { + result = hours + ":0" + Math.round(minutes) + " " + suffix; + } else { + result = hours + ":" + Math.round(minutes) + " " + suffix; + } + + } else { + if ((hours >= 0 && hours <= 9) && (minutes >= 0 && minutes <= 9)) { + result = "0" + hours + ":0" + Math.round(minutes); + } else if ((hours >= 0 && hours <= 9)) { + result = "0" + hours + ":" + Math.round(minutes); + } else if ((minutes >= 0 && minutes <= 9)) { + result = hours + ":0" + Math.round(minutes); + } else { + result = hours + ":" + Math.round(minutes); + } + } + return result; + + } + + // convert double hours to 12h format with no suffix + public String floatToTime12NS(double time) { + return floatToTime12(time, true); + } + + // ---------------------- Compute Prayer Times ----------------------- + // compute prayer times at given julian date + private double[] computeTimes(double[] times) { + + double[] t = dayPortion(times); + + double Fajr = this.computeTime( + 180 - methodParams.get(this.getCalcMethod())[0], t[0]); + + double Sunrise = this.computeTime(180 - 0.833, t[1]); + + double Dhuhr = this.computeMidDay(t[2]); + double Asr = this.computeAsr(1 + this.getAsrJuristic(), t[3]); + double Sunset = this.computeTime(0.833, t[4]); + + double Maghrib = this.computeTime( + methodParams.get(this.getCalcMethod())[2], t[5]); + double Isha = this.computeTime( + methodParams.get(this.getCalcMethod())[4], t[6]); + + double[] CTimes = {Fajr, Sunrise, Dhuhr, Asr, Sunset, Maghrib, Isha}; + + return CTimes; + + } + + // compute prayer times at given julian date + private ArrayList computeDayTimes() { + double[] times = {5, 6, 12, 13, 18, 18, 18}; // default times + + for (int i = 1; i <= this.getNumIterations(); i++) { + times = computeTimes(times); + } + + times = adjustTimes(times); + times = tuneTimes(times); + + return adjustTimesFormat(times); + } + + // adjust times in a prayer time array + private double[] adjustTimes(double[] times) { + for (int i = 0; i < times.length; i++) { + times[i] += this.getTimeZone() - this.getLng() / 15; + } + + times[2] += this.getDhuhrMinutes() / 60; // Dhuhr + if (methodParams.get(this.getCalcMethod())[1] == 1) // Maghrib + { + times[5] = times[4] + methodParams.get(this.getCalcMethod())[2]/ 60; + } + if (methodParams.get(this.getCalcMethod())[3] == 1) // Isha + { + times[6] = times[5] + methodParams.get(this.getCalcMethod())[4]/ 60; + } + + if (this.getAdjustHighLats() != this.getNone()) { + times = adjustHighLatTimes(times); + } + + return times; + } + + // convert times array to given time format + private ArrayList adjustTimesFormat(double[] times) { + + ArrayList result = new ArrayList(); + + if (this.getTimeFormat() == this.getFloating()) { + for (double time : times) { + result.add(String.valueOf(time)); + } + return result; + } + + for (int i = 0; i < 7; i++) { + if (this.getTimeFormat() == this.getTime12()) { + result.add(floatToTime12(times[i], false)); + } else if (this.getTimeFormat() == this.getTime12NS()) { + result.add(floatToTime12(times[i], true)); + } else { + result.add(floatToTime24(times[i])); + } + } + return result; + } + + // adjust Fajr, Isha and Maghrib for locations in higher latitudes + private double[] adjustHighLatTimes(double[] times) { + double nightTime = timeDiff(times[4], times[1]); // sunset to sunrise + + // Adjust Fajr + double FajrDiff = nightPortion(methodParams.get(this.getCalcMethod())[0]) * nightTime; + + if (Double.isNaN(times[0]) || timeDiff(times[0], times[1]) > FajrDiff) { + times[0] = times[1] - FajrDiff; + } + + // Adjust Isha + double IshaAngle = (methodParams.get(this.getCalcMethod())[3] == 0) ? methodParams.get(this.getCalcMethod())[4] : 18; + double IshaDiff = this.nightPortion(IshaAngle) * nightTime; + if (Double.isNaN(times[6]) || this.timeDiff(times[4], times[6]) > IshaDiff) { + times[6] = times[4] + IshaDiff; + } + + // Adjust Maghrib + double MaghribAngle = (methodParams.get(this.getCalcMethod())[1] == 0) ? methodParams.get(this.getCalcMethod())[2] : 4; + double MaghribDiff = nightPortion(MaghribAngle) * nightTime; + if (Double.isNaN(times[5]) || this.timeDiff(times[4], times[5]) > MaghribDiff) { + times[5] = times[4] + MaghribDiff; + } + + return times; + } + + // the night portion used for adjusting times in higher latitudes + private double nightPortion(double angle) { + double calc = 0; + + if (adjustHighLats == AngleBased) + calc = (angle)/60.0; + else if (adjustHighLats == MidNight) + calc = 0.5; + else if (adjustHighLats == OneSeventh) + calc = 0.14286; + + return calc; + } + + // convert hours to day portions + private double[] dayPortion(double[] times) { + for (int i = 0; i < 7; i++) { + times[i] /= 24; + } + return times; + } + + // Tune timings for adjustments + // Set time offsets + public void tune(int[] offsetTimes) { + + for (int i = 0; i < offsetTimes.length; i++) { // offsetTimes length + // should be 7 in order + // of Fajr, Sunrise, + // Dhuhr, Asr, Sunset, + // Maghrib, Isha + this.offsets[i] = offsetTimes[i]; + } + } + + private double[] tuneTimes(double[] times) { + for (int i = 0; i < times.length; i++) { + times[i] = times[i] + this.offsets[i] / 60.0; + } + + return times; + } + + /** + * @param args + */ + /* + * public static void main(String[] args) { double latitude = -37.823689; double + * longitude = 145.121597; double timezone = 10; // Test Prayer times here + * PrayTime prayers = new PrayTime(); + * + * prayers.setTimeFormat(prayers.Time12); prayers.setCalcMethod(prayers.Jafari); + * prayers.setAsrJuristic(prayers.Shafii); + * prayers.setAdjustHighLats(prayers.AngleBased); int[] offsets = {0, 0, 0, 0, + * 0, 0, 0}; // {Fajr,Sunrise,Dhuhr,Asr,Sunset,Maghrib,Isha} + * prayers.tune(offsets); + * + * Date now = new Date(); Calendar cal = Calendar.getInstance(); + * cal.setTime(now); + * + * ArrayList prayerTimes = prayers.getPrayerTimes(cal, latitude, + * longitude, timezone); ArrayList prayerNames = prayers.getTimeNames(); + * + * for (int i = 0; i < prayerTimes.size(); i++) { + * System.out.println(prayerNames.get(i) + " - " + prayerTimes.get(i)); } + * + * } + */ + + public int getCalcMethod() { + return calcMethod; + } + + public void setCalcMethod(int calcMethod) { + this.calcMethod = calcMethod; + } + + public int getAsrJuristic() { + return asrJuristic; + } + + public void setAsrJuristic(int asrJuristic) { + this.asrJuristic = asrJuristic; + } + + public int getDhuhrMinutes() { + return dhuhrMinutes; + } + + public void setDhuhrMinutes(int dhuhrMinutes) { + this.dhuhrMinutes = dhuhrMinutes; + } + + public int getAdjustHighLats() { + return adjustHighLats; + } + + public void setAdjustHighLats(int adjustHighLats) { + this.adjustHighLats = adjustHighLats; + } + + public int getTimeFormat() { + return timeFormat; + } + + public void setTimeFormat(int timeFormat) { + this.timeFormat = timeFormat; + } + + public double getLat() { + return lat; + } + + public void setLat(double lat) { + this.lat = lat; + } + + public double getLng() { + return lng; + } + + public void setLng(double lng) { + this.lng = lng; + } + + public double getTimeZone() { + return timeZone; + } + + public void setTimeZone(double timeZone) { + this.timeZone = timeZone; + } + + public double getJDate() { + return JDate; + } + + public void setJDate(double jDate) { + JDate = jDate; + } + + public int getJafari() { + return Jafari; + } + + private void setJafari(int jafari) { + Jafari = jafari; + } + + public int getKarachi() { + return Karachi; + } + + private void setKarachi(int karachi) { + Karachi = karachi; + } + + public int getISNA() { + return ISNA; + } + + private void setISNA(int iSNA) { + ISNA = iSNA; + } + + public int getMWL() { + return MWL; + } + + private void setMWL(int mWL) { + MWL = mWL; + } + + public int getMakkah() { + return Makkah; + } + + private void setMakkah(int makkah) { + Makkah = makkah; + } + + public int getEgypt() { + return Egypt; + } + + private void setEgypt(int egypt) { + Egypt = egypt; + } + + public int getCustom() { + return Custom; + } + + private void setCustom(int custom) { + Custom = custom; + } + + private int getTehran() { + return Tehran; + } + + private void setTehran(int tehran) { + Tehran = tehran; + } + + public int getShafii() { + return Shafii; + } + + private void setShafii(int shafii) { + Shafii = shafii; + } + + @SuppressWarnings("unused") + private int getHanafi() { + return Hanafi; + } + + private void setHanafi(int hanafi) { + Hanafi = hanafi; + } + + private int getNone() { + return None; + } + + private void setNone(int none) { + None = none; + } + + @SuppressWarnings("unused") + private int getMidNight() { + return MidNight; + } + + private void setMidNight(int midNight) { + MidNight = midNight; + } + + @SuppressWarnings("unused") + private int getOneSeventh() { + return OneSeventh; + } + + private void setOneSeventh(int oneSeventh) { + OneSeventh = oneSeventh; + } + + public int getAngleBased() { + return AngleBased; + } + + private void setAngleBased(int angleBased) { + AngleBased = angleBased; + } + + + public int getTime24() { + return Time24; + } + + private void setTime24(int time24) { + Time24 = time24; + } + + int getTime12() { + return Time12; + } + + private void setTime12(int time12) { + Time12 = time12; + } + + private int getTime12NS() { + return Time12NS; + } + + private void setTime12NS(int time12ns) { + Time12NS = time12ns; + } + + private int getFloating() { + return Floating; + } + + private void setFloating(int floating) { + Floating = floating; + } + + private int getNumIterations() { + return numIterations; + } + + private void setNumIterations(int numIterations) { + this.numIterations = numIterations; + } + + public ArrayList getTimeNames() { + return timeNames; + } +} \ No newline at end of file diff --git a/src/androgpio/gps/kotaindonesiaminified.json b/src/androgpio/gps/kotaindonesiaminified.json new file mode 100644 index 0000000..6d0e26c --- /dev/null +++ b/src/androgpio/gps/kotaindonesiaminified.json @@ -0,0 +1,545 @@ +[ +{"nid":1,"parent_nid":0,"name":"Provinsi Aceh","serial":11,"type":1,"latitude":4.695135,"longitude":96.7493993,"status":1}, +{"nid":2,"parent_nid":0,"name":"Provinsi Sumatera Utara","serial":12,"type":1,"latitude":2.1153547,"longitude":99.5450974,"status":1}, +{"nid":3,"parent_nid":0,"name":"Provinsi Sumatera Barat","serial":13,"type":1,"latitude":-0.7399397,"longitude":100.8000051,"status":1}, +{"nid":4,"parent_nid":0,"name":"Provinsi Riau","serial":14,"type":1,"latitude":0.2933469,"longitude":101.7068294,"status":1}, +{"nid":5,"parent_nid":0,"name":"Provinsi Jambi","serial":15,"type":1,"latitude":-1.4851831,"longitude":102.4380581,"status":1}, +{"nid":6,"parent_nid":0,"name":"Provinsi Sumatera Selatan","serial":16,"type":1,"latitude":-3.3194374,"longitude":103.914399,"status":1}, +{"nid":7,"parent_nid":0,"name":"Provinsi Bengkulu","serial":17,"type":1,"latitude":-3.5778471,"longitude":102.3463875,"status":1}, +{"nid":8,"parent_nid":0,"name":"Provinsi Lampung","serial":18,"type":1,"latitude":-4.5585849,"longitude":105.4068079,"status":1}, +{"nid":9,"parent_nid":0,"name":"Provinsi Kepulauan Bangka Belitung","serial":19,"type":1,"latitude":-2.7410513,"longitude":106.4405872,"status":1}, +{"nid":10,"parent_nid":0,"name":"Provinsi Kepulauan Riau","serial":21,"type":1,"latitude":3.9456514,"longitude":108.1428669,"status":1}, +{"nid":11,"parent_nid":0,"name":"Provinsi DKI Jakarta","serial":31,"type":1,"latitude":-6.211544,"longitude":106.845172,"status":1}, +{"nid":12,"parent_nid":0,"name":"Provinsi Jawa Barat","serial":32,"type":1,"latitude":-7.090911,"longitude":107.668887,"status":1}, +{"nid":13,"parent_nid":0,"name":"Provinsi Jawa Tengah","serial":33,"type":1,"latitude":-7.150975,"longitude":110.1402594,"status":1}, +{"nid":14,"parent_nid":0,"name":"Provinsi DI Yogyakarta","serial":34,"type":1,"latitude":-7.8753849,"longitude":110.4262088,"status":1}, +{"nid":15,"parent_nid":0,"name":"Provinsi Jawa Timur","serial":35,"type":1,"latitude":-7.5360639,"longitude":112.2384017,"status":1}, +{"nid":16,"parent_nid":0,"name":"Provinsi Banten","serial":36,"type":1,"latitude":-6.4058172,"longitude":106.0640179,"status":1}, +{"nid":17,"parent_nid":0,"name":"Provinsi Bali","serial":51,"type":1,"latitude":-8.4095178,"longitude":115.188916,"status":1}, +{"nid":18,"parent_nid":0,"name":"Provinsi Nusa Tenggara Barat","serial":52,"type":1,"latitude":-8.6529334,"longitude":117.3616476,"status":1}, +{"nid":19,"parent_nid":0,"name":"Provinsi Nusa Tenggara Timur","serial":53,"type":1,"latitude":-8.6573819,"longitude":121.0793705,"status":1}, +{"nid":20,"parent_nid":0,"name":"Provinsi Kalimantan Barat","serial":61,"type":1,"latitude":-0.2787808,"longitude":111.4752851,"status":1}, +{"nid":21,"parent_nid":0,"name":"Provinsi Kalimantan Tengah","serial":62,"type":1,"latitude":-1.6814878,"longitude":113.3823545,"status":1}, +{"nid":22,"parent_nid":0,"name":"Provinsi Kalimantan Selatan","serial":63,"type":1,"latitude":-3.0926415,"longitude":115.2837585,"status":1}, +{"nid":23,"parent_nid":0,"name":"Provinsi Kalimantan Timur","serial":64,"type":1,"latitude":1.6406296,"longitude":116.419389,"status":1}, +{"nid":24,"parent_nid":0,"name":"Provinsi Sulawesi Utara","serial":71,"type":1,"latitude":0.6246932,"longitude":123.9750018,"status":1}, +{"nid":25,"parent_nid":0,"name":"Provinsi Sulawesi Tengah","serial":72,"type":1,"latitude":-1.4300254,"longitude":121.4456179,"status":1}, +{"nid":26,"parent_nid":0,"name":"Provinsi Sulawesi Selatan","serial":73,"type":1,"latitude":-3.6687994,"longitude":119.9740534,"status":1}, +{"nid":27,"parent_nid":0,"name":"Provinsi Sulawesi Tenggara","serial":74,"type":1,"latitude":-4.14491,"longitude":122.174605,"status":1}, +{"nid":28,"parent_nid":0,"name":"Provinsi Gorontalo","serial":75,"type":1,"latitude":0.6999372,"longitude":122.4467238,"status":1}, +{"nid":29,"parent_nid":0,"name":"Provinsi Sulawesi Barat","serial":76,"type":1,"latitude":-2.8441371,"longitude":119.2320784,"status":1}, +{"nid":30,"parent_nid":0,"name":"Provinsi Maluku","serial":81,"type":1,"latitude":-3.2384616,"longitude":130.1452734,"status":1}, +{"nid":31,"parent_nid":0,"name":"Provinsi Maluku Utara","serial":82,"type":1,"latitude":1.5709993,"longitude":127.8087693,"status":1}, +{"nid":32,"parent_nid":0,"name":"Provinsi Papua Barat","serial":91,"type":1,"latitude":-1.3361154,"longitude":133.1747162,"status":1}, +{"nid":33,"parent_nid":0,"name":"Provinsi Papua","serial":94,"type":1,"latitude":-4.269928,"longitude":138.0803529,"status":1}, +{"nid":562,"parent_nid":74,"name":"Kota Tangerang Selatan","serial":0,"type":2,"latitude":-6.2888889,"longitude":106.7180556,"status":1}, +{"nid":821,"parent_nid":15,"name":"Kabupaten Banyuwangi","serial":1501,"type":2,"latitude":-8.2186111,"longitude":114.3669444,"status":1}, +{"nid":823,"parent_nid":15,"name":"Kabupaten Madiun","serial":1502,"type":2,"latitude":-7.627753,"longitude":111.505483,"status":1}, +{"nid":824,"parent_nid":15,"name":"Kabupaten Ponorogo","serial":1503,"type":2,"latitude":-7.867827,"longitude":111.466003,"status":1}, +{"nid":825,"parent_nid":15,"name":"Kabupaten Magetan","serial":1504,"type":2,"latitude":-7.6493413,"longitude":111.3381593,"status":1}, +{"nid":826,"parent_nid":15,"name":"Kabupaten Pacitan","serial":1505,"type":2,"latitude":-8.204614,"longitude":111.08769,"status":1}, +{"nid":827,"parent_nid":15,"name":"Kabupaten Ngawi","serial":1506,"type":2,"latitude":-7.38993,"longitude":111.46193,"status":1}, +{"nid":828,"parent_nid":15,"name":"Kabupaten Bangkalan","serial":1507,"type":2,"latitude":-7.0306912,"longitude":112.7450068,"status":1}, +{"nid":829,"parent_nid":15,"name":"Kabupaten Kediri","serial":1508,"type":2,"latitude":-7.809356,"longitude":112.032356,"status":1}, +{"nid":830,"parent_nid":15,"name":"Kabupaten Bondowoso","serial":1509,"type":2,"latitude":-7.917704,"longitude":113.813483,"status":1}, +{"nid":831,"parent_nid":15,"name":"Kabupaten Blitar","serial":1510,"type":2,"latitude":-8.1014419,"longitude":112.162762,"status":1}, +{"nid":832,"parent_nid":15,"name":"Kabupaten Trenggalek","serial":1511,"type":2,"latitude":-8.05,"longitude":111.7166667,"status":1}, +{"nid":833,"parent_nid":15,"name":"Kabupaten Tulungagung","serial":1512,"type":2,"latitude":-8.0666667,"longitude":111.9,"status":1}, +{"nid":834,"parent_nid":15,"name":"Kabupaten Nganjuk","serial":1513,"type":2,"latitude":-7.602932,"longitude":111.901808,"status":1}, +{"nid":835,"parent_nid":15,"name":"Kabupaten Situbondo","serial":1514,"type":2,"latitude":-7.702534,"longitude":113.955605,"status":1}, +{"nid":836,"parent_nid":15,"name":"Kabupaten Malang","serial":1515,"type":2,"latitude":-8.0495643,"longitude":112.6884549,"status":1}, +{"nid":837,"parent_nid":15,"name":"Kabupaten Jember","serial":1516,"type":2,"latitude":-8.172357,"longitude":113.700302,"status":1}, +{"nid":838,"parent_nid":15,"name":"Kabupaten Sumenep","serial":1517,"type":2,"latitude":-6.9253999,"longitude":113.9060624,"status":1}, +{"nid":839,"parent_nid":15,"name":"Kabupaten Pasuruan","serial":1518,"type":2,"latitude":-6.8623098,"longitude":108.8001936,"status":1}, +{"nid":840,"parent_nid":15,"name":"Kabupaten Pamekasan","serial":1519,"type":2,"latitude":-7.1666667,"longitude":113.4666667,"status":1}, +{"nid":841,"parent_nid":15,"name":"Kabupaten Probolinggo","serial":1520,"type":2,"latitude":-7.753965,"longitude":113.210675,"status":1}, +{"nid":842,"parent_nid":15,"name":"Kabupaten Lumajang","serial":1521,"type":2,"latitude":-8.137022,"longitude":113.226601,"status":1}, +{"nid":843,"parent_nid":15,"name":"Kabupaten Bojonegoro","serial":1522,"type":2,"latitude":0.882681,"longitude":124.4669566,"status":1}, +{"nid":844,"parent_nid":15,"name":"Kabupaten Tuban","serial":1523,"type":2,"latitude":-8.7493146,"longitude":115.1711298,"status":1}, +{"nid":845,"parent_nid":15,"name":"Kabupaten Lamongan","serial":1524,"type":2,"latitude":-7.406153,"longitude":109.3946794,"status":1}, +{"nid":846,"parent_nid":15,"name":"Kabupaten Sidoarjo","serial":1525,"type":2,"latitude":-7.4530278,"longitude":112.7173389,"status":1}, +{"nid":847,"parent_nid":15,"name":"Kabupaten Sampang","serial":1526,"type":2,"latitude":-7.5782556,"longitude":109.2058436,"status":1}, +{"nid":848,"parent_nid":15,"name":"Kabupaten Mojokerto","serial":1527,"type":2,"latitude":-7.488075,"longitude":112.427027,"status":1}, +{"nid":849,"parent_nid":15,"name":"Kabupaten Gresik","serial":1528,"type":2,"latitude":-7.15665,"longitude":112.6555,"status":1}, +{"nid":850,"parent_nid":15,"name":"Kabupaten Jombang","serial":1529,"type":2,"latitude":-7.5468395,"longitude":112.2264794,"status":1}, +{"nid":851,"parent_nid":15,"name":"Kota Mojokerto","serial":1530,"type":2,"latitude":-7.4722222,"longitude":112.4336111,"status":1}, +{"nid":852,"parent_nid":15,"name":"Kota Surabaya","serial":1531,"type":2,"latitude":-7.289166,"longitude":112.734398,"status":1}, +{"nid":853,"parent_nid":15,"name":"Kota Madiun","serial":1532,"type":2,"latitude":-7.629714,"longitude":111.513702,"status":1}, +{"nid":854,"parent_nid":15,"name":"Kota Blitar","serial":1533,"type":2,"latitude":-8.1,"longitude":112.15,"status":1}, +{"nid":855,"parent_nid":15,"name":"Kota Malang","serial":1534,"type":2,"latitude":-7.981894,"longitude":112.626503,"status":1}, +{"nid":856,"parent_nid":15,"name":"Kota Batu","serial":1535,"type":2,"latitude":-7.8671,"longitude":112.5239,"status":1}, +{"nid":857,"parent_nid":15,"name":"Kota Pasuruan","serial":1536,"type":2,"latitude":-7.644872,"longitude":112.903297,"status":1}, +{"nid":858,"parent_nid":15,"name":"Kota Kediri","serial":1537,"type":2,"latitude":-7.816895,"longitude":112.011398,"status":1}, +{"nid":859,"parent_nid":15,"name":"Kota Probolinggo","serial":1538,"type":2,"latitude":-7.756928,"longitude":113.211502,"status":1}, +{"nid":925,"parent_nid":5,"name":"Kabupaten Batanghari","serial":501,"type":2,"latitude":-1.7083922,"longitude":103.0817903,"status":1}, +{"nid":926,"parent_nid":5,"name":"Kabupaten Bungo","serial":502,"type":2,"latitude":-1.6401338,"longitude":101.8891721,"status":1}, +{"nid":927,"parent_nid":5,"name":"Kabupaten Kerinci","serial":503,"type":2,"latitude":-1.8720467,"longitude":101.4339148,"status":1}, +{"nid":928,"parent_nid":5,"name":"Kabupaten Merangin","serial":504,"type":2,"latitude":-2.1752789,"longitude":101.9804613,"status":1}, +{"nid":929,"parent_nid":5,"name":"Kabupaten Muaro Jambi","serial":505,"type":2,"latitude":-1.596672,"longitude":103.615799,"status":1}, +{"nid":930,"parent_nid":5,"name":"Kabupaten Sarolangun","serial":506,"type":2,"latitude":-2.2654937,"longitude":102.6905326,"status":1}, +{"nid":931,"parent_nid":5,"name":"Kabupaten Tanjung Jabung Barat","serial":507,"type":2,"latitude":-1.2332122,"longitude":103.7984428,"status":1}, +{"nid":932,"parent_nid":5,"name":"Kabupaten Tanjung Jabung Timur","serial":508,"type":2,"latitude":-1.3291599,"longitude":103.89973,"status":1}, +{"nid":933,"parent_nid":5,"name":"Kabupaten Tebo","serial":509,"type":2,"latitude":-1.2592999,"longitude":102.3463875,"status":1}, +{"nid":934,"parent_nid":5,"name":"Kota Jambi","serial":510,"type":2,"latitude":-1.596672,"longitude":103.615799,"status":1}, +{"nid":935,"parent_nid":5,"name":"Kota Sungai Penuh","serial":511,"type":2,"latitude":-2.06314,"longitude":101.387199,"status":1}, +{"nid":1326,"parent_nid":1,"name":"Kabupaten Simeulue","serial":1109,"type":2,"latitude":2.583333,"longitude":96.083333,"status":1}, +{"nid":1327,"parent_nid":1,"name":"Kabupaten Aceh Singkil","serial":1102,"type":2,"latitude":2.3589459,"longitude":97.87216,"status":1}, +{"nid":1328,"parent_nid":1,"name":"Kabupaten Aceh Selatan","serial":1101,"type":2,"latitude":3.3115056,"longitude":97.3516558,"status":1}, +{"nid":1329,"parent_nid":1,"name":"Kabupaten Aceh Tenggara","serial":1102,"type":2,"latitude":3.3088666,"longitude":97.6982272,"status":1}, +{"nid":1330,"parent_nid":1,"name":"Kabupaten Aceh Timur","serial":1103,"type":2,"latitude":5.255443,"longitude":95.9885456,"status":1}, +{"nid":1331,"parent_nid":1,"name":"Kabupaten Aceh Tengah","serial":1104,"type":2,"latitude":4.4482641,"longitude":96.8350999,"status":1}, +{"nid":1332,"parent_nid":1,"name":"Kabupaten Aceh Barat","serial":1105,"type":2,"latitude":4.4542745,"longitude":96.1526985,"status":1}, +{"nid":1333,"parent_nid":1,"name":"Kabupaten Aceh Besar","serial":1106,"type":2,"latitude":5.4529168,"longitude":95.4777811,"status":1}, +{"nid":1334,"parent_nid":1,"name":"Kabupaten Pidie","serial":1107,"type":2,"latitude":5.0742659,"longitude":95.940971,"status":1}, +{"nid":1335,"parent_nid":1,"name":"Kabupaten Bireuen","serial":1111,"type":2,"latitude":5.18254,"longitude":96.89005,"status":1}, +{"nid":1336,"parent_nid":1,"name":"Kabupaten Aceh Utara","serial":1108,"type":2,"latitude":4.9786331,"longitude":97.2221421,"status":1}, +{"nid":1337,"parent_nid":1,"name":"Kabupaten Aceh Barat Daya","serial":1112,"type":2,"latitude":3.0512643,"longitude":97.3368031,"status":1}, +{"nid":1338,"parent_nid":1,"name":"Kabupaten Gayo Lues","serial":1113,"type":2,"latitude":3.955165,"longitude":97.3516558,"status":1}, +{"nid":1339,"parent_nid":1,"name":"Kabupaten Aceh Tamiang","serial":1116,"type":2,"latitude":4.2328871,"longitude":98.0028892,"status":1}, +{"nid":1340,"parent_nid":1,"name":"Kabupaten Nagan Raya","serial":1115,"type":2,"latitude":4.1248406,"longitude":96.4929797,"status":1}, +{"nid":1341,"parent_nid":1,"name":"Kabupaten Aceh Jaya","serial":1114,"type":2,"latitude":4.7873684,"longitude":95.6457951,"status":1}, +{"nid":1342,"parent_nid":1,"name":"Kabupaten Bener Meriah","serial":1117,"type":2,"latitude":4.7748348,"longitude":97.0068393,"status":1}, +{"nid":1343,"parent_nid":1,"name":"Kabupaten Pidie Jaya","serial":1118,"type":2,"latitude":5.1548063,"longitude":96.195132,"status":1}, +{"nid":1344,"parent_nid":1,"name":"Kota Banda Aceh","serial":1171,"type":2,"latitude":5.55,"longitude":95.3166667,"status":1}, +{"nid":1345,"parent_nid":1,"name":"Kota Sabang","serial":1172,"type":2,"latitude":5.8946929,"longitude":95.3192982,"status":1}, +{"nid":1346,"parent_nid":1,"name":"Kota Langsa","serial":1174,"type":2,"latitude":4.48,"longitude":97.9633333,"status":1}, +{"nid":1347,"parent_nid":1,"name":"Kota Lhokseumawe","serial":1173,"type":2,"latitude":5.1880556,"longitude":97.1402778,"status":1}, +{"nid":1348,"parent_nid":1,"name":"Kota Subulussalam","serial":1175,"type":2,"latitude":2.6449927,"longitude":98.0165205,"status":1}, +{"nid":1349,"parent_nid":2,"name":"Kabupaten Nias","serial":1204,"type":2,"latitude":-8.1712591,"longitude":113.7111274,"status":1}, +{"nid":1350,"parent_nid":2,"name":"Kabupaten Mandailing Natal","serial":1213,"type":2,"latitude":0.7432372,"longitude":99.3673084,"status":1}, +{"nid":1351,"parent_nid":2,"name":"Kabupaten Tapanuli Selatan","serial":1203,"type":2,"latitude":1.5774933,"longitude":99.2785583,"status":1}, +{"nid":1352,"parent_nid":2,"name":"Kabupaten Tapanuli Tengah","serial":1201,"type":2,"latitude":1.8493299,"longitude":98.704075,"status":1}, +{"nid":1353,"parent_nid":2,"name":"Kabupaten Tapanuli Utara","serial":1202,"type":2,"latitude":2.0405246,"longitude":99.1013498,"status":1}, +{"nid":1354,"parent_nid":2,"name":"Kabupaten Toba Samosir","serial":1212,"type":2,"latitude":2.3502398,"longitude":99.2785583,"status":1}, +{"nid":1355,"parent_nid":2,"name":"Kabupaten Labuhanbatu","serial":1210,"type":2,"latitude":2.3439863,"longitude":100.1703257,"status":1}, +{"nid":1356,"parent_nid":2,"name":"Kabupaten Asahan","serial":1209,"type":2,"latitude":2.8174722,"longitude":99.634135,"status":1}, +{"nid":1357,"parent_nid":2,"name":"Kabupaten Simalungun","serial":1208,"type":2,"latitude":2.9781612,"longitude":99.2785583,"status":1}, +{"nid":1358,"parent_nid":2,"name":"Kabupaten Dairi","serial":1211,"type":2,"latitude":2.8675801,"longitude":98.265058,"status":1}, +{"nid":1359,"parent_nid":2,"name":"Kabupaten Karo","serial":1206,"type":2,"latitude":3.1052909,"longitude":98.265058,"status":1}, +{"nid":1360,"parent_nid":2,"name":"Kabupaten Deli Serdang","serial":1207,"type":2,"latitude":3.4201802,"longitude":98.704075,"status":1}, +{"nid":1361,"parent_nid":2,"name":"Kabupaten Langkat","serial":1205,"type":2,"latitude":3.8653916,"longitude":98.3088441,"status":1}, +{"nid":1362,"parent_nid":2,"name":"Kabupaten Nias Selatan","serial":1214,"type":2,"latitude":0.7086091,"longitude":97.8286368,"status":1}, +{"nid":1363,"parent_nid":2,"name":"Kabupaten Humbang Hasundutan","serial":1216,"type":2,"latitude":2.1988508,"longitude":98.5721016,"status":1}, +{"nid":1364,"parent_nid":2,"name":"Kabupaten Pakpak Bharat","serial":1215,"type":2,"latitude":2.545786,"longitude":98.299838,"status":1}, +{"nid":1365,"parent_nid":2,"name":"Kabupaten Samosir","serial":1217,"type":2,"latitude":2.5833333,"longitude":98.8166667,"status":1}, +{"nid":1366,"parent_nid":2,"name":"Kabupaten Serdang Bedagai","serial":1218,"type":2,"latitude":3.3371694,"longitude":99.0571089,"status":1}, +{"nid":1367,"parent_nid":2,"name":"Kabupaten Batu Bara","serial":1219,"type":2,"latitude":3.1740979,"longitude":99.5006143,"status":1}, +{"nid":1368,"parent_nid":2,"name":"Kabupaten Padang Lawas Utara","serial":1220,"type":2,"latitude":1.5758644,"longitude":99.634135,"status":1}, +{"nid":1369,"parent_nid":2,"name":"Kabupaten Padang Lawas","serial":1221,"type":2,"latitude":1.1186977,"longitude":99.8124935,"status":1}, +{"nid":1370,"parent_nid":2,"name":"Kota Sibolga","serial":1273,"type":2,"latitude":1.7403745,"longitude":98.7827988,"status":1}, +{"nid":1371,"parent_nid":2,"name":"Kota Tanjung Balai","serial":1274,"type":2,"latitude":2.965122,"longitude":99.800331,"status":1}, +{"nid":1372,"parent_nid":2,"name":"Kota Pematang Siantar","serial":1272,"type":2,"latitude":2.96,"longitude":99.06,"status":1}, +{"nid":1373,"parent_nid":2,"name":"Kota Tebing Tinggi","serial":1276,"type":2,"latitude":3.3856205,"longitude":99.2009815,"status":1}, +{"nid":1374,"parent_nid":2,"name":"Kota Medan","serial":1271,"type":2,"latitude":3.585242,"longitude":98.6755979,"status":1}, +{"nid":1375,"parent_nid":2,"name":"Kota Binjai","serial":1275,"type":2,"latitude":3.594462,"longitude":98.482246,"status":1}, +{"nid":1376,"parent_nid":2,"name":"Kota Padangsidimpuan","serial":1277,"type":2,"latitude":1.380424,"longitude":99.273972,"status":1}, +{"nid":1377,"parent_nid":3,"name":"Kabupaten Kepulauan Mentawai","serial":1309,"type":2,"latitude":-1.426001,"longitude":98.9245343,"status":1}, +{"nid":1378,"parent_nid":3,"name":"Kabupaten Pesisir Selatan","serial":1301,"type":2,"latitude":-1.7223147,"longitude":100.8903099,"status":1}, +{"nid":1379,"parent_nid":3,"name":"Kabupaten Solok","serial":1302,"type":2,"latitude":-0.803027,"longitude":100.644402,"status":1}, +{"nid":1380,"parent_nid":3,"name":"Kabupaten Sijunjung","serial":1303,"type":2,"latitude":-0.6881586,"longitude":100.997658,"status":1}, +{"nid":1381,"parent_nid":3,"name":"Kabupaten Tanah Datar","serial":1304,"type":2,"latitude":-0.4797043,"longitude":100.5746224,"status":1}, +{"nid":1382,"parent_nid":3,"name":"Kabupaten Padang Pariaman","serial":1305,"type":2,"latitude":-0.5546757,"longitude":100.2151578,"status":1}, +{"nid":1383,"parent_nid":3,"name":"Kabupaten Agam","serial":1306,"type":2,"latitude":-0.2209392,"longitude":100.1703257,"status":1}, +{"nid":1384,"parent_nid":3,"name":"Kabupaten Lima Puluh Kota","serial":1307,"type":2,"latitude":3.168216,"longitude":99.4187929,"status":1}, +{"nid":1385,"parent_nid":3,"name":"Kabupaten Pasaman","serial":1308,"type":2,"latitude":0.1288752,"longitude":99.7901781,"status":1}, +{"nid":1386,"parent_nid":3,"name":"Kabupaten Solok Selatan","serial":1311,"type":2,"latitude":-1.4157329,"longitude":101.2523792,"status":1}, +{"nid":1387,"parent_nid":3,"name":"Kabupaten Dharmas Raya","serial":1310,"type":2,"latitude":-1.1120568,"longitude":101.6157773,"status":1}, +{"nid":1388,"parent_nid":3,"name":"Kabupaten Pasaman Barat","serial":1312,"type":2,"latitude":0.2213005,"longitude":99.634135,"status":1}, +{"nid":1389,"parent_nid":3,"name":"Kota Padang","serial":1371,"type":2,"latitude":-0.95,"longitude":100.3530556,"status":1}, +{"nid":1390,"parent_nid":3,"name":"Kota Solok","serial":1372,"type":2,"latitude":-0.803027,"longitude":100.644402,"status":1}, +{"nid":1391,"parent_nid":3,"name":"Kota Sawah Lunto","serial":1373,"type":2,"latitude":-0.6810286,"longitude":100.7763604,"status":1}, +{"nid":1392,"parent_nid":3,"name":"Kota Padang Panjang","serial":1374,"type":2,"latitude":-0.470679,"longitude":100.4059456,"status":1}, +{"nid":1393,"parent_nid":3,"name":"Kota Bukittinggi","serial":1375,"type":2,"latitude":-0.3055556,"longitude":100.3691667,"status":1}, +{"nid":1394,"parent_nid":3,"name":"Kota Payakumbuh","serial":1376,"type":2,"latitude":-0.22887,"longitude":100.632301,"status":1}, +{"nid":1395,"parent_nid":3,"name":"Kota Pariaman","serial":1377,"type":2,"latitude":-0.6264389,"longitude":100.1179574,"status":1}, +{"nid":1396,"parent_nid":4,"name":"Kabupaten Kuantan Singingi","serial":1409,"type":2,"latitude":-0.4411596,"longitude":101.5248055,"status":1}, +{"nid":1397,"parent_nid":4,"name":"Kabupaten Indragiri Hulu","serial":1402,"type":2,"latitude":-0.7361181,"longitude":102.2547919,"status":1}, +{"nid":1398,"parent_nid":4,"name":"Kabupaten Indragiri Hilir","serial":1404,"type":2,"latitude":-0.1456733,"longitude":102.989615,"status":1}, +{"nid":1399,"parent_nid":4,"name":"Kabupaten Pelalawan","serial":1405,"type":2,"latitude":0.441415,"longitude":102.088699,"status":1}, +{"nid":1400,"parent_nid":4,"name":"Kabupaten S I A K","serial":1408,"type":2,"latitude":-0.789275,"longitude":113.921327,"status":1}, +{"nid":1401,"parent_nid":4,"name":"Kabupaten Kampar","serial":1401,"type":2,"latitude":0.146671,"longitude":101.1617356,"status":1}, +{"nid":1402,"parent_nid":4,"name":"Kabupaten Rokan Hulu","serial":1406,"type":2,"latitude":1.0410934,"longitude":100.439656,"status":1}, +{"nid":1403,"parent_nid":4,"name":"Kabupaten Bengkalis","serial":1403,"type":2,"latitude":1.4897222,"longitude":102.0797222,"status":1}, +{"nid":1404,"parent_nid":4,"name":"Kabupaten Rokan Hilir","serial":1407,"type":2,"latitude":1.6463978,"longitude":100.8000051,"status":1}, +{"nid":1405,"parent_nid":4,"name":"Kota Pekanbaru","serial":1471,"type":2,"latitude":0.5333333,"longitude":101.45,"status":1}, +{"nid":1406,"parent_nid":4,"name":"Kota Dumai","serial":1472,"type":2,"latitude":1.665742,"longitude":101.447601,"status":1}, +{"nid":1407,"parent_nid":5,"name":"Kabupaten Kerinci","serial":1501,"type":2,"latitude":-1.697,"longitude":101.264,"status":1}, +{"nid":1408,"parent_nid":5,"name":"Kabupaten Merangin","serial":1502,"type":2,"latitude":-2.1752789,"longitude":101.9804613,"status":1}, +{"nid":1409,"parent_nid":5,"name":"Kabupaten Sarolangun","serial":1503,"type":2,"latitude":-2.2654937,"longitude":102.6905326,"status":1}, +{"nid":1410,"parent_nid":5,"name":"Kabupaten Batang Hari","serial":1504,"type":2,"latitude":-1.7083922,"longitude":103.0817903,"status":1}, +{"nid":1411,"parent_nid":5,"name":"Kabupaten Muaro Jambi","serial":1505,"type":2,"latitude":-1.596672,"longitude":103.615799,"status":1}, +{"nid":1412,"parent_nid":5,"name":"Kabupaten Tanjung Jabung Timur","serial":1507,"type":2,"latitude":-1.3291599,"longitude":103.89973,"status":1}, +{"nid":1413,"parent_nid":5,"name":"Kabupaten Tanjung Jabung Barat","serial":1506,"type":2,"latitude":-1.2332122,"longitude":103.7984428,"status":1}, +{"nid":1414,"parent_nid":5,"name":"Kabupaten Tebo","serial":1509,"type":2,"latitude":-1.2592999,"longitude":102.3463875,"status":1}, +{"nid":1415,"parent_nid":5,"name":"Kabupaten Bungo","serial":1508,"type":2,"latitude":-1.6401338,"longitude":101.8891721,"status":1}, +{"nid":1416,"parent_nid":5,"name":"Kota Jambi","serial":1571,"type":2,"latitude":-1.596672,"longitude":103.615799,"status":1}, +{"nid":1417,"parent_nid":6,"name":"Kabupaten Ogan Komering Ulu","serial":1601,"type":2,"latitude":-4.0283486,"longitude":104.0072348,"status":1}, +{"nid":1418,"parent_nid":6,"name":"Kabupaten Ogan Komering Ilir","serial":1602,"type":2,"latitude":-3.4559744,"longitude":105.2194808,"status":1}, +{"nid":1419,"parent_nid":6,"name":"Kabupaten Muara Enim","serial":1603,"type":2,"latitude":-3.651581,"longitude":103.770798,"status":1}, +{"nid":1420,"parent_nid":6,"name":"Kabupaten Lahat","serial":1604,"type":2,"latitude":-3.7863889,"longitude":103.5427778,"status":1}, +{"nid":1421,"parent_nid":6,"name":"Kabupaten Musi Rawas","serial":1605,"type":2,"latitude":-2.8625305,"longitude":102.989615,"status":1}, +{"nid":1422,"parent_nid":6,"name":"Kabupaten Musi Banyuasin","serial":1606,"type":2,"latitude":-2.5442029,"longitude":103.7289167,"status":1}, +{"nid":1423,"parent_nid":6,"name":"Kabupaten Banyu Asin","serial":1607,"type":2,"latitude":-2.6095639,"longitude":104.7520939,"status":1}, +{"nid":1424,"parent_nid":6,"name":"Kabupaten Ogan Komering Ulu Selatan","serial":1609,"type":2,"latitude":-4.6681951,"longitude":104.0072348,"status":1}, +{"nid":1425,"parent_nid":6,"name":"Kabupaten Ogan Komering Ulu Timur","serial":1608,"type":2,"latitude":-3.8567934,"longitude":104.7520939,"status":1}, +{"nid":1426,"parent_nid":6,"name":"Kabupaten Ogan Ilir","serial":1610,"type":2,"latitude":-3.426544,"longitude":104.6121475,"status":1}, +{"nid":1427,"parent_nid":6,"name":"Kabupaten Empat Lawang","serial":1611,"type":2,"latitude":-3.7286029,"longitude":102.8975098,"status":1}, +{"nid":1428,"parent_nid":6,"name":"Kota Palembang","serial":1671,"type":2,"latitude":-2.9911083,"longitude":104.7567333,"status":1}, +{"nid":1429,"parent_nid":6,"name":"Kota Prabumulih","serial":1674,"type":2,"latitude":-3.440956,"longitude":104.235397,"status":1}, +{"nid":1430,"parent_nid":6,"name":"Kota Pagar Alam","serial":1672,"type":2,"latitude":-4.03767,"longitude":103.265297,"status":1}, +{"nid":1431,"parent_nid":6,"name":"Kota Lubuklinggau","serial":1673,"type":2,"latitude":-3.2966667,"longitude":102.8616667,"status":1}, +{"nid":1432,"parent_nid":7,"name":"Kabupaten Bengkulu Selatan","serial":1701,"type":2,"latitude":-4.3248409,"longitude":103.035694,"status":1}, +{"nid":1433,"parent_nid":7,"name":"Kabupaten Rejang Lebong","serial":1702,"type":2,"latitude":-3.4548154,"longitude":102.6675575,"status":1}, +{"nid":1434,"parent_nid":7,"name":"Kabupaten Bengkulu Utara","serial":1703,"type":2,"latitude":-3.4219555,"longitude":102.1632718,"status":1}, +{"nid":1435,"parent_nid":7,"name":"Kabupaten Kaur","serial":1704,"type":2,"latitude":-4.6792278,"longitude":103.4511768,"status":1}, +{"nid":1436,"parent_nid":7,"name":"Kabupaten Seluma","serial":1705,"type":2,"latitude":-4.0622929,"longitude":102.5642261,"status":1}, +{"nid":1437,"parent_nid":7,"name":"Kabupaten Mukomuko","serial":1706,"type":2,"latitude":-2.5760003,"longitude":101.1169805,"status":1}, +{"nid":1438,"parent_nid":7,"name":"Kabupaten Lebong","serial":1707,"type":2,"latitude":-2.992617,"longitude":104.382202,"status":1}, +{"nid":1439,"parent_nid":7,"name":"Kabupaten Kepahiang","serial":1708,"type":2,"latitude":-3.651431,"longitude":102.578201,"status":1}, +{"nid":1440,"parent_nid":7,"name":"Kota Bengkulu","serial":1771,"type":2,"latitude":-3.7955556,"longitude":102.2591667,"status":1}, +{"nid":1441,"parent_nid":8,"name":"Kabupaten Lampung Barat","serial":1804,"type":2,"latitude":-5.1490396,"longitude":104.1930918,"status":1}, +{"nid":1442,"parent_nid":8,"name":"Kabupaten Tanggamus","serial":1802,"type":2,"latitude":-5.3027489,"longitude":104.5655273,"status":1}, +{"nid":1443,"parent_nid":8,"name":"Kabupaten Lampung Selatan","serial":1801,"type":2,"latitude":-5.5622614,"longitude":105.5474373,"status":1}, +{"nid":1444,"parent_nid":8,"name":"Kabupaten Lampung Timur","serial":1807,"type":2,"latitude":-5.1134995,"longitude":105.6881788,"status":1}, +{"nid":1445,"parent_nid":8,"name":"Kabupaten Lampung Tengah","serial":1802,"type":2,"latitude":-4.8008086,"longitude":105.3131185,"status":1}, +{"nid":1446,"parent_nid":8,"name":"Kabupaten Lampung Utara","serial":1803,"type":2,"latitude":-4.8133905,"longitude":104.7520939,"status":1}, +{"nid":1447,"parent_nid":8,"name":"Kabupaten Way Kanan","serial":1808,"type":2,"latitude":-4.4963689,"longitude":104.5655273,"status":1}, +{"nid":1448,"parent_nid":8,"name":"Kabupaten Tulangbawang","serial":1812,"type":2,"latitude":-4.3176576,"longitude":105.5005483,"status":1}, +{"nid":1449,"parent_nid":8,"name":"Kabupaten Pesawaran","serial":1809,"type":2,"latitude":-5.493245,"longitude":105.0791228,"status":1}, +{"nid":1450,"parent_nid":8,"name":"Kota Bandar Lampung","serial":1871,"type":2,"latitude":-5.45,"longitude":105.2666667,"status":1}, +{"nid":1451,"parent_nid":8,"name":"Kota Metro","serial":1872,"type":2,"latitude":-5.1166667,"longitude":105.3,"status":1}, +{"nid":1452,"parent_nid":9,"name":"Kabupaten Bangka","serial":1901,"type":2,"latitude":-2.2884782,"longitude":106.0640179,"status":1}, +{"nid":1453,"parent_nid":9,"name":"Kabupaten Belitung","serial":1902,"type":2,"latitude":-2.8708938,"longitude":107.9531836,"status":1}, +{"nid":1454,"parent_nid":9,"name":"Kabupaten Bangka Barat","serial":1905,"type":2,"latitude":-2.2884782,"longitude":106.0640179,"status":1}, +{"nid":1455,"parent_nid":9,"name":"Kabupaten Bangka Tengah","serial":1904,"type":2,"latitude":-2.2884782,"longitude":106.0640179,"status":1}, +{"nid":1456,"parent_nid":9,"name":"Kabupaten Bangka Selatan","serial":1903,"type":2,"latitude":-2.2884782,"longitude":106.0640179,"status":1}, +{"nid":1457,"parent_nid":9,"name":"Kabupaten Belitung Timur","serial":1906,"type":2,"latitude":-2.8708938,"longitude":107.9531836,"status":1}, +{"nid":1458,"parent_nid":9,"name":"Kota Pangkal Pinang","serial":1971,"type":2,"latitude":-2.129323,"longitude":106.109596,"status":1}, +{"nid":1459,"parent_nid":10,"name":"Kabupaten Karimun","serial":2102,"type":2,"latitude":1.05,"longitude":103.3666667,"status":1}, +{"nid":1460,"parent_nid":10,"name":"Kabupaten Bintan","serial":2101,"type":2,"latitude":1.0619173,"longitude":104.5189214,"status":1}, +{"nid":1461,"parent_nid":10,"name":"Kabupaten Natuna","serial":2103,"type":2,"latitude":3.9329945,"longitude":108.1812242,"status":1}, +{"nid":1462,"parent_nid":10,"name":"Kabupaten Lingga","serial":2104,"type":2,"latitude":-0.1627686,"longitude":104.6354631,"status":1}, +{"nid":1463,"parent_nid":10,"name":"Kota Batam","serial":2171,"type":2,"latitude":1.0456264,"longitude":104.0304535,"status":1}, +{"nid":1464,"parent_nid":10,"name":"Kota Tanjung Pinang","serial":2172,"type":2,"latitude":0.9179205,"longitude":104.446464,"status":1}, +{"nid":1465,"parent_nid":11,"name":"Kabupaten Kepulauan Seribu","serial":3101,"type":2,"latitude":-5.7985266,"longitude":106.5071982,"status":1}, +{"nid":1466,"parent_nid":11,"name":"Kota Jakarta Selatan","serial":3174,"type":2,"latitude":-6.332973,"longitude":106.807915,"status":1}, +{"nid":1467,"parent_nid":11,"name":"Kota Jakarta Timur","serial":3175,"type":2,"latitude":-6.211544,"longitude":106.845172,"status":1}, +{"nid":1468,"parent_nid":11,"name":"Kota Jakarta Pusat","serial":3171,"type":2,"latitude":-6.211544,"longitude":106.845172,"status":1}, +{"nid":1469,"parent_nid":11,"name":"Kota Jakarta Barat","serial":3173,"type":2,"latitude":-6.211544,"longitude":106.845172,"status":1}, +{"nid":1470,"parent_nid":11,"name":"Kota Jakarta Utara","serial":3172,"type":2,"latitude":-6.211544,"longitude":106.845172,"status":1}, +{"nid":1471,"parent_nid":12,"name":"Kabupaten Bogor","serial":3201,"type":2,"latitude":-6.6,"longitude":106.8,"status":1}, +{"nid":1472,"parent_nid":12,"name":"Kabupaten Sukabumi","serial":3202,"type":2,"latitude":-6.92405,"longitude":106.922203,"status":1}, +{"nid":1473,"parent_nid":12,"name":"Kabupaten Cianjur","serial":3203,"type":2,"latitude":-6.8172531,"longitude":107.1307289,"status":1}, +{"nid":1474,"parent_nid":12,"name":"Kabupaten Bandung","serial":3204,"type":2,"latitude":-6.9147444,"longitude":107.6098111,"status":1}, +{"nid":1475,"parent_nid":12,"name":"Kabupaten Garut","serial":3205,"type":2,"latitude":-7.227906,"longitude":107.908699,"status":1}, +{"nid":1476,"parent_nid":12,"name":"Kabupaten Tasikmalaya","serial":3206,"type":2,"latitude":-7.327954,"longitude":108.214104,"status":1}, +{"nid":1477,"parent_nid":12,"name":"Kabupaten Ciamis","serial":3207,"type":2,"latitude":-7.3333333,"longitude":108.35,"status":1}, +{"nid":1478,"parent_nid":12,"name":"Kabupaten Kuningan","serial":3208,"type":2,"latitude":-6.9833333,"longitude":108.4833333,"status":1}, +{"nid":1479,"parent_nid":12,"name":"Kabupaten Cirebon","serial":3209,"type":2,"latitude":-6.715534,"longitude":108.564003,"status":1}, +{"nid":1480,"parent_nid":12,"name":"Kabupaten Majalengka","serial":3210,"type":2,"latitude":-6.8531026,"longitude":108.2258897,"status":1}, +{"nid":1481,"parent_nid":12,"name":"Kabupaten Sumedang","serial":3211,"type":2,"latitude":0.6095949,"longitude":110.0330554,"status":1}, +{"nid":1482,"parent_nid":12,"name":"Kabupaten Indramayu","serial":3212,"type":2,"latitude":-6.336315,"longitude":108.325104,"status":1}, +{"nid":1483,"parent_nid":12,"name":"Kabupaten Subang","serial":3213,"type":2,"latitude":-6.569361,"longitude":107.752403,"status":1}, +{"nid":1484,"parent_nid":12,"name":"Kabupaten Purwakarta","serial":3214,"type":2,"latitude":-6.5386806,"longitude":107.4499404,"status":1}, +{"nid":1485,"parent_nid":12,"name":"Kabupaten Karawang","serial":3215,"type":2,"latitude":-6.3227303,"longitude":107.3375791,"status":1}, +{"nid":1486,"parent_nid":12,"name":"Kabupaten Bekasi","serial":3216,"type":2,"latitude":-6.2333333,"longitude":107,"status":1}, +{"nid":1487,"parent_nid":12,"name":"Kabupaten Bandung Barat","serial":3217,"type":2,"latitude":-6.8937121,"longitude":107.4321959,"status":1}, +{"nid":1488,"parent_nid":12,"name":"Kota Bogor","serial":3271,"type":2,"latitude":-6.6,"longitude":106.8,"status":1}, +{"nid":1489,"parent_nid":12,"name":"Kota Sukabumi","serial":3272,"type":2,"latitude":-6.92405,"longitude":106.922203,"status":1}, +{"nid":1490,"parent_nid":12,"name":"Kota Bandung","serial":3273,"type":2,"latitude":-6.9147444,"longitude":107.6098111,"status":1}, +{"nid":1491,"parent_nid":12,"name":"Kota Cirebon","serial":3274,"type":2,"latitude":-6.715534,"longitude":108.564003,"status":1}, +{"nid":1492,"parent_nid":12,"name":"Kota Bekasi","serial":3275,"type":2,"latitude":-6.2333333,"longitude":107,"status":1}, +{"nid":1493,"parent_nid":12,"name":"Kota Depok","serial":3276,"type":2,"latitude":-6.39,"longitude":106.83,"status":1}, +{"nid":1494,"parent_nid":12,"name":"Kota Cimahi","serial":3277,"type":2,"latitude":-6.880239,"longitude":107.5355,"status":1}, +{"nid":1495,"parent_nid":12,"name":"Kota Tasikmalaya","serial":3278,"type":2,"latitude":-7.327954,"longitude":108.214104,"status":1}, +{"nid":1496,"parent_nid":12,"name":"Kota Banjar","serial":3279,"type":2,"latitude":-7.3666667,"longitude":108.5333333,"status":1}, +{"nid":1497,"parent_nid":13,"name":"Kabupaten Cilacap","serial":3301,"type":2,"latitude":-7.733333,"longitude":109,"status":1}, +{"nid":1498,"parent_nid":13,"name":"Kabupaten Banyumas","serial":3302,"type":2,"latitude":-7.4832133,"longitude":109.140438,"status":1}, +{"nid":1499,"parent_nid":13,"name":"Kabupaten Purbalingga","serial":3303,"type":2,"latitude":-7.390747,"longitude":109.3638,"status":1}, +{"nid":1500,"parent_nid":13,"name":"Kabupaten Banjarnegara","serial":3304,"type":2,"latitude":-7.402706,"longitude":109.681396,"status":1}, +{"nid":1501,"parent_nid":13,"name":"Kabupaten Kebumen","serial":3305,"type":2,"latitude":-7.678682,"longitude":109.656502,"status":1}, +{"nid":1502,"parent_nid":13,"name":"Kabupaten Purworejo","serial":3306,"type":2,"latitude":-7.709731,"longitude":110.008003,"status":1}, +{"nid":1503,"parent_nid":13,"name":"Kabupaten Wonosobo","serial":3307,"type":2,"latitude":-7.362109,"longitude":109.899399,"status":1}, +{"nid":1504,"parent_nid":13,"name":"Kabupaten Magelang","serial":3308,"type":2,"latitude":-7.481253,"longitude":110.213799,"status":1}, +{"nid":1505,"parent_nid":13,"name":"Kabupaten Boyolali","serial":3309,"type":2,"latitude":-7.523819,"longitude":110.595901,"status":1}, +{"nid":1506,"parent_nid":13,"name":"Kabupaten Klaten","serial":3310,"type":2,"latitude":-7.711687,"longitude":110.595497,"status":1}, +{"nid":1507,"parent_nid":13,"name":"Kabupaten Sukoharjo","serial":3311,"type":2,"latitude":-7.6808818,"longitude":110.8195292,"status":1}, +{"nid":1508,"parent_nid":13,"name":"Kabupaten Wonogiri","serial":3312,"type":2,"latitude":-7.817782,"longitude":110.920601,"status":1}, +{"nid":1509,"parent_nid":13,"name":"Kabupaten Karanganyar","serial":3313,"type":2,"latitude":-7.5961111,"longitude":110.9508333,"status":1}, +{"nid":1510,"parent_nid":13,"name":"Kabupaten Sragen","serial":3314,"type":2,"latitude":-7.430229,"longitude":111.021301,"status":1}, +{"nid":1511,"parent_nid":13,"name":"Kabupaten Grobogan","serial":3315,"type":2,"latitude":-7.0217194,"longitude":110.9625854,"status":1}, +{"nid":1512,"parent_nid":13,"name":"Kabupaten Blora","serial":3316,"type":2,"latitude":-6.95,"longitude":111.4166667,"status":1}, +{"nid":1513,"parent_nid":13,"name":"Kabupaten Rembang","serial":3317,"type":2,"latitude":-6.71124,"longitude":111.345299,"status":1}, +{"nid":1514,"parent_nid":13,"name":"Kabupaten Pati","serial":3318,"type":2,"latitude":-6.751338,"longitude":111.038002,"status":1}, +{"nid":1515,"parent_nid":13,"name":"Kabupaten Kudus","serial":3319,"type":2,"latitude":-6.804087,"longitude":110.838203,"status":1}, +{"nid":1516,"parent_nid":13,"name":"Kabupaten Jepara","serial":3320,"type":2,"latitude":-6.5596059,"longitude":110.6717,"status":1}, +{"nid":1517,"parent_nid":13,"name":"Kabupaten Demak","serial":3321,"type":2,"latitude":-6.888115,"longitude":110.639297,"status":1}, +{"nid":1518,"parent_nid":13,"name":"Kabupaten Semarang","serial":3322,"type":2,"latitude":-6.9666667,"longitude":110.4166667,"status":1}, +{"nid":1519,"parent_nid":13,"name":"Kabupaten Temanggung","serial":3323,"type":2,"latitude":-7.316669,"longitude":110.174797,"status":1}, +{"nid":1520,"parent_nid":13,"name":"Kabupaten Kendal","serial":3324,"type":2,"latitude":-6.919686,"longitude":110.205597,"status":1}, +{"nid":1521,"parent_nid":13,"name":"Kabupaten Batang","serial":3325,"type":2,"latitude":-6.8941111,"longitude":109.7234519,"status":1}, +{"nid":1522,"parent_nid":13,"name":"Kabupaten Pekalongan","serial":3326,"type":2,"latitude":-6.882887,"longitude":109.669998,"status":1}, +{"nid":1523,"parent_nid":13,"name":"Kabupaten Pemalang","serial":3327,"type":2,"latitude":-6.884234,"longitude":109.377998,"status":1}, +{"nid":1524,"parent_nid":13,"name":"Kabupaten Tegal","serial":3328,"type":2,"latitude":-6.8666667,"longitude":109.1333333,"status":1}, +{"nid":1525,"parent_nid":13,"name":"Kabupaten Brebes","serial":3329,"type":2,"latitude":-6.8833333,"longitude":109.05,"status":1}, +{"nid":1526,"parent_nid":13,"name":"Kota Magelang","serial":3371,"type":2,"latitude":-7.481253,"longitude":110.213799,"status":1}, +{"nid":1527,"parent_nid":13,"name":"Kota Surakarta","serial":3372,"type":2,"latitude":-7.5666667,"longitude":110.8166667,"status":1}, +{"nid":1528,"parent_nid":13,"name":"Kota Salatiga","serial":3373,"type":2,"latitude":-7.302328,"longitude":110.4729,"status":1}, +{"nid":1529,"parent_nid":13,"name":"Kota Semarang","serial":3374,"type":2,"latitude":-6.9666667,"longitude":110.4166667,"status":1}, +{"nid":1530,"parent_nid":13,"name":"Kota Pekalongan","serial":3375,"type":2,"latitude":-6.882887,"longitude":109.669998,"status":1}, +{"nid":1531,"parent_nid":13,"name":"Kota Tegal","serial":3376,"type":2,"latitude":-6.8666667,"longitude":109.1333333,"status":1}, +{"nid":1532,"parent_nid":14,"name":"Kabupaten Kulon Progo","serial":3401,"type":2,"latitude":-7.8266798,"longitude":110.1640846,"status":1}, +{"nid":1533,"parent_nid":14,"name":"Kabupaten Bantul","serial":3402,"type":2,"latitude":-7.8846111,"longitude":110.3341111,"status":1}, +{"nid":1534,"parent_nid":14,"name":"Kabupaten Gunung Kidul","serial":3403,"type":2,"latitude":-8.0305091,"longitude":110.6168921,"status":1}, +{"nid":1536,"parent_nid":14,"name":"Kota Yogyakarta","serial":3471,"type":2,"latitude":-7.797224,"longitude":110.368797,"status":1}, +{"nid":1537,"parent_nid":16,"name":"Kabupaten Pandeglang","serial":3601,"type":2,"latitude":-6.314835,"longitude":106.103897,"status":1}, +{"nid":1538,"parent_nid":16,"name":"Kabupaten Lebak","serial":3602,"type":2,"latitude":-6.5643956,"longitude":106.2522143,"status":1}, +{"nid":1539,"parent_nid":16,"name":"Kabupaten Tangerang","serial":3603,"type":2,"latitude":-6.1783056,"longitude":106.6318889,"status":1}, +{"nid":1540,"parent_nid":16,"name":"Kabupaten Serang","serial":3604,"type":2,"latitude":-6.12009,"longitude":106.150299,"status":1}, +{"nid":1541,"parent_nid":16,"name":"Kota Tangerang","serial":3671,"type":2,"latitude":-6.1783056,"longitude":106.6318889,"status":1}, +{"nid":1542,"parent_nid":16,"name":"Kota Cilegon","serial":3672,"type":2,"latitude":-6.0169825,"longitude":106.040506,"status":1}, +{"nid":1543,"parent_nid":16,"name":"Kota Serang","serial":3673,"type":2,"latitude":-6.12009,"longitude":106.150299,"status":1}, +{"nid":1544,"parent_nid":17,"name":"Kabupaten Jembrana","serial":5101,"type":2,"latitude":-8.361852,"longitude":114.6418,"status":1}, +{"nid":1545,"parent_nid":17,"name":"Kabupaten Tabanan","serial":5102,"type":2,"latitude":-8.544516,"longitude":115.119797,"status":1}, +{"nid":1546,"parent_nid":17,"name":"Kabupaten Badung","serial":5103,"type":2,"latitude":-8.5819296,"longitude":115.1770586,"status":1}, +{"nid":1547,"parent_nid":17,"name":"Kabupaten Gianyar","serial":5104,"type":2,"latitude":-8.544185,"longitude":115.3255,"status":1}, +{"nid":1548,"parent_nid":17,"name":"Kabupaten Klungkung","serial":5105,"type":2,"latitude":-8.5389222,"longitude":115.4045111,"status":1}, +{"nid":1549,"parent_nid":17,"name":"Kabupaten Bangli","serial":5106,"type":2,"latitude":-8.454303,"longitude":115.354897,"status":1}, +{"nid":1550,"parent_nid":17,"name":"Kabupaten Karang Asem","serial":5107,"type":2,"latitude":-6.3996057,"longitude":108.0503042,"status":1}, +{"nid":1551,"parent_nid":17,"name":"Kabupaten Buleleng","serial":5108,"type":2,"latitude":-8.113831,"longitude":115.126999,"status":1}, +{"nid":1552,"parent_nid":17,"name":"Kota Denpasar","serial":5171,"type":2,"latitude":-8.65629,"longitude":115.222099,"status":1}, +{"nid":1553,"parent_nid":18,"name":"Kabupaten Lombok Barat","serial":5201,"type":2,"latitude":-8.6464599,"longitude":116.1123078,"status":1}, +{"nid":1554,"parent_nid":18,"name":"Kabupaten Lombok Tengah","serial":5202,"type":2,"latitude":-8.694623,"longitude":116.2777073,"status":1}, +{"nid":1555,"parent_nid":18,"name":"Kabupaten Lombok Timur","serial":5203,"type":2,"latitude":-8.5134471,"longitude":116.5609857,"status":1}, +{"nid":1556,"parent_nid":18,"name":"Kabupaten Sumbawa","serial":5204,"type":2,"latitude":-8.6529334,"longitude":117.3616476,"status":1}, +{"nid":1557,"parent_nid":18,"name":"Kabupaten Dompu","serial":5205,"type":2,"latitude":-8.4966318,"longitude":118.4747173,"status":1}, +{"nid":1558,"parent_nid":18,"name":"Kabupaten Bima","serial":5206,"type":2,"latitude":-8.460566,"longitude":118.727402,"status":1}, +{"nid":1559,"parent_nid":18,"name":"Kabupaten Sumbawa Barat","serial":5207,"type":2,"latitude":-8.9292907,"longitude":116.8910342,"status":1}, +{"nid":1560,"parent_nid":18,"name":"Kota Mataram","serial":5271,"type":2,"latitude":-8.5833333,"longitude":116.1166667,"status":1}, +{"nid":1561,"parent_nid":18,"name":"Kota Bima","serial":5272,"type":2,"latitude":-8.460566,"longitude":118.727402,"status":1}, +{"nid":1562,"parent_nid":19,"name":"Kabupaten Sumba Barat","serial":5312,"type":2,"latitude":-9.6548326,"longitude":119.3947135,"status":1}, +{"nid":1563,"parent_nid":19,"name":"Kabupaten Sumba Timur","serial":5311,"type":2,"latitude":-9.9802103,"longitude":120.3435506,"status":1}, +{"nid":1564,"parent_nid":19,"name":"Kabupaten Kupang","serial":5301,"type":2,"latitude":-10.1833333,"longitude":123.5833333,"status":1}, +{"nid":1565,"parent_nid":19,"name":"Kabupaten Timor Tengah Selatan","serial":5302,"type":2,"latitude":-9.7762816,"longitude":124.4198243,"status":1}, +{"nid":1566,"parent_nid":19,"name":"Kabupaten Timor Tengah Utara","serial":5303,"type":2,"latitude":-9.4522647,"longitude":124.597132,"status":1}, +{"nid":1567,"parent_nid":19,"name":"Kabupaten Belu","serial":5304,"type":2,"latitude":-9.4125796,"longitude":124.9506625,"status":1}, +{"nid":1568,"parent_nid":19,"name":"Kabupaten Alor","serial":5305,"type":2,"latitude":-8.2754027,"longitude":124.7298765,"status":1}, +{"nid":1569,"parent_nid":19,"name":"Kabupaten Lembata","serial":5313,"type":2,"latitude":-8.4719075,"longitude":123.4831906,"status":1}, +{"nid":1570,"parent_nid":19,"name":"Kabupaten Flores Timur","serial":5306,"type":2,"latitude":-8.3130942,"longitude":122.9663018,"status":1}, +{"nid":1571,"parent_nid":19,"name":"Kabupaten Sikka","serial":5307,"type":2,"latitude":-8.6766175,"longitude":122.1291843,"status":1}, +{"nid":1572,"parent_nid":19,"name":"Kabupaten Ende","serial":5308,"type":2,"latitude":-8.854053,"longitude":121.654198,"status":1}, +{"nid":1573,"parent_nid":19,"name":"Kabupaten Ngada","serial":5309,"type":2,"latitude":-8.7430424,"longitude":120.9876321,"status":1}, +{"nid":1574,"parent_nid":19,"name":"Kabupaten Manggarai","serial":5310,"type":2,"latitude":-8.6796987,"longitude":120.3896651,"status":1}, +{"nid":1575,"parent_nid":19,"name":"Kabupaten Rote Ndao","serial":5314,"type":2,"latitude":-10.7386421,"longitude":123.1239049,"status":1}, +{"nid":1576,"parent_nid":19,"name":"Kabupaten Manggarai Barat","serial":5315,"type":2,"latitude":-8.6688149,"longitude":120.0665236,"status":1}, +{"nid":1577,"parent_nid":19,"name":"Kabupaten Sumba Tengah","serial":5317,"type":2,"latitude":-9.4879226,"longitude":119.6962677,"status":1}, +{"nid":1578,"parent_nid":19,"name":"Kabupaten Sumba Barat Daya","serial":5318,"type":2,"latitude":-9.539139,"longitude":119.1390642,"status":1}, +{"nid":1579,"parent_nid":19,"name":"Kabupaten Nagekeo","serial":5316,"type":2,"latitude":-8.6753545,"longitude":121.3084088,"status":1}, +{"nid":1580,"parent_nid":19,"name":"Kabupaten Manggarai Timur","serial":5319,"type":2,"latitude":-8.6206712,"longitude":120.6199895,"status":1}, +{"nid":1581,"parent_nid":19,"name":"Kota Kupang","serial":5371,"type":2,"latitude":-10.1833333,"longitude":123.5833333,"status":1}, +{"nid":1582,"parent_nid":20,"name":"Kabupaten Sambas","serial":6101,"type":2,"latitude":1.361328,"longitude":109.309998,"status":1}, +{"nid":1583,"parent_nid":20,"name":"Kabupaten Bengkayang","serial":6107,"type":2,"latitude":0.8209729,"longitude":109.477699,"status":1}, +{"nid":1584,"parent_nid":20,"name":"Kabupaten Landak","serial":6108,"type":2,"latitude":0.4237287,"longitude":109.7591675,"status":1}, +{"nid":1585,"parent_nid":20,"name":"Kabupaten Pontianak","serial":6102,"type":2,"latitude":-0.022523,"longitude":109.330307,"status":1}, +{"nid":1586,"parent_nid":20,"name":"Kabupaten Sanggau","serial":6103,"type":2,"latitude":0.119275,"longitude":110.597298,"status":1}, +{"nid":1587,"parent_nid":20,"name":"Kabupaten Ketapang","serial":6104,"type":2,"latitude":-1.859098,"longitude":109.971901,"status":1}, +{"nid":1588,"parent_nid":20,"name":"Kabupaten Sintang","serial":6105,"type":2,"latitude":0.080238,"longitude":111.495499,"status":1}, +{"nid":1589,"parent_nid":20,"name":"Kabupaten Kapuas Hulu","serial":6106,"type":2,"latitude":-0.7931004,"longitude":113.9060624,"status":1}, +{"nid":1590,"parent_nid":20,"name":"Kabupaten Sekadau","serial":6109,"type":2,"latitude":0.015637,"longitude":110.888603,"status":1}, +{"nid":1591,"parent_nid":20,"name":"Kabupaten Melawi","serial":6110,"type":2,"latitude":-0.7000681,"longitude":111.6660725,"status":1}, +{"nid":1592,"parent_nid":20,"name":"Kabupaten Kayong Utara","serial":6111,"type":2,"latitude":-0.9225877,"longitude":110.0449662,"status":1}, +{"nid":1593,"parent_nid":20,"name":"Kabupaten Kubu Raya","serial":6112,"type":2,"latitude":-0.3533938,"longitude":109.4735066,"status":1}, +{"nid":1594,"parent_nid":20,"name":"Kota Pontianak","serial":6171,"type":2,"latitude":-0.022523,"longitude":109.330307,"status":1}, +{"nid":1595,"parent_nid":20,"name":"Kota Singkawang","serial":6172,"type":2,"latitude":0.908795,"longitude":108.984596,"status":1}, +{"nid":1596,"parent_nid":21,"name":"Kabupaten Kotawaringin Barat","serial":6201,"type":2,"latitude":-6.1961131,"longitude":106.8630174,"status":1}, +{"nid":1597,"parent_nid":21,"name":"Kabupaten Kotawaringin Timur","serial":6202,"type":2,"latitude":-6.1952992,"longitude":106.8630737,"status":1}, +{"nid":1598,"parent_nid":21,"name":"Kabupaten Kapuas","serial":6203,"type":2,"latitude":-0.0459972,"longitude":110.1313251,"status":1}, +{"nid":1599,"parent_nid":21,"name":"Kabupaten Barito Selatan","serial":6204,"type":2,"latitude":-1.875943,"longitude":114.8092691,"status":1}, +{"nid":1600,"parent_nid":21,"name":"Kabupaten Barito Utara","serial":6205,"type":2,"latitude":-0.9587136,"longitude":115.094045,"status":1}, +{"nid":1601,"parent_nid":21,"name":"Kabupaten Sukamara","serial":6208,"type":2,"latitude":-2.6267517,"longitude":111.2368084,"status":1}, +{"nid":1602,"parent_nid":21,"name":"Kabupaten Lamandau","serial":6209,"type":2,"latitude":-1.9269166,"longitude":111.1891151,"status":1}, +{"nid":1603,"parent_nid":21,"name":"Kabupaten Seruyan","serial":6207,"type":2,"latitude":-3.0123467,"longitude":112.4291464,"status":1}, +{"nid":1604,"parent_nid":21,"name":"Kabupaten Katingan","serial":6206,"type":2,"latitude":-0.9758379,"longitude":112.8105512,"status":1}, +{"nid":1605,"parent_nid":21,"name":"Kabupaten Pulang Pisau","serial":6211,"type":2,"latitude":-2.6849607,"longitude":113.9536466,"status":1}, +{"nid":1606,"parent_nid":21,"name":"Kabupaten Gunung Mas","serial":6210,"type":2,"latitude":-6.7052778,"longitude":106.9913889,"status":1}, +{"nid":1607,"parent_nid":21,"name":"Kabupaten Barito Timur","serial":6213,"type":2,"latitude":-2.0123999,"longitude":115.188916,"status":1}, +{"nid":1608,"parent_nid":21,"name":"Kabupaten Murung Raya","serial":6212,"type":2,"latitude":-0.1362171,"longitude":114.3341432,"status":1}, +{"nid":1609,"parent_nid":21,"name":"Kota Palangka Raya","serial":6271,"type":2,"latitude":-2.21,"longitude":113.92,"status":1}, +{"nid":1610,"parent_nid":22,"name":"Kabupaten Tanah Laut","serial":6301,"type":2,"latitude":-3.7694047,"longitude":114.8092691,"status":1}, +{"nid":1611,"parent_nid":22,"name":"Kabupaten Kota Baru","serial":6302,"type":2,"latitude":-6.332973,"longitude":106.807915,"status":1}, +{"nid":1612,"parent_nid":22,"name":"Kabupaten Banjar","serial":6303,"type":2,"latitude":-7.3666667,"longitude":108.5333333,"status":1}, +{"nid":1613,"parent_nid":22,"name":"Kabupaten Barito Kuala","serial":6304,"type":2,"latitude":-3.0714738,"longitude":114.6667939,"status":1}, +{"nid":1614,"parent_nid":22,"name":"Kabupaten Tapin","serial":6305,"type":2,"latitude":-2.9160746,"longitude":115.0465991,"status":1}, +{"nid":1615,"parent_nid":22,"name":"Kabupaten Hulu Sungai Selatan","serial":6306,"type":2,"latitude":-2.7662681,"longitude":115.2363408,"status":1}, +{"nid":1616,"parent_nid":22,"name":"Kabupaten Hulu Sungai Tengah","serial":6307,"type":2,"latitude":-2.6153162,"longitude":115.5207358,"status":1}, +{"nid":1617,"parent_nid":22,"name":"Kabupaten Hulu Sungai Utara","serial":6308,"type":2,"latitude":-2.4421225,"longitude":115.188916,"status":1}, +{"nid":1618,"parent_nid":22,"name":"Kabupaten Tabalong","serial":6309,"type":2,"latitude":-1.864302,"longitude":115.5681084,"status":1}, +{"nid":1619,"parent_nid":22,"name":"Kabupaten Tanah Bumbu","serial":6310,"type":2,"latitude":-3.4512244,"longitude":115.5681084,"status":1}, +{"nid":1620,"parent_nid":22,"name":"Kabupaten Balangan","serial":6311,"type":2,"latitude":-2.3260425,"longitude":115.6154732,"status":1}, +{"nid":1621,"parent_nid":22,"name":"Kota Banjarmasin","serial":6371,"type":2,"latitude":-3.328499,"longitude":114.589203,"status":1}, +{"nid":1622,"parent_nid":22,"name":"Kota Banjar Baru","serial":6372,"type":2,"latitude":-3.4666667,"longitude":114.75,"status":1}, +{"nid":1623,"parent_nid":23,"name":"Kabupaten Paser","serial":6401,"type":2,"latitude":-1.7175266,"longitude":115.9467997,"status":1}, +{"nid":1624,"parent_nid":23,"name":"Kabupaten Kutai Barat","serial":6407,"type":2,"latitude":0.1353881,"longitude":115.094045,"status":1}, +{"nid":1625,"parent_nid":23,"name":"Kabupaten Kutai Kartanegara","serial":6402,"type":2,"latitude":-0.1336655,"longitude":116.6081653,"status":1}, +{"nid":1626,"parent_nid":23,"name":"Kabupaten Kutai Timur","serial":6408,"type":2,"latitude":0.9433774,"longitude":116.9852422,"status":1}, +{"nid":1627,"parent_nid":23,"name":"Kabupaten Berau","serial":6403,"type":2,"latitude":2.0450883,"longitude":117.3616476,"status":1}, +{"nid":1628,"parent_nid":23,"name":"Kabupaten Malinau","serial":6406,"type":2,"latitude":3.584221,"longitude":116.647797,"status":1}, +{"nid":1629,"parent_nid":23,"name":"Kabupaten Bulungan","serial":6404,"type":2,"latitude":2.9042476,"longitude":116.9852422,"status":1}, +{"nid":1630,"parent_nid":23,"name":"Kabupaten Nunukan","serial":6405,"type":2,"latitude":4.0609227,"longitude":117.666952,"status":1}, +{"nid":1631,"parent_nid":23,"name":"Kabupaten Penajam Paser Utara","serial":6409,"type":2,"latitude":-1.2917094,"longitude":116.5137964,"status":1}, +{"nid":1632,"parent_nid":23,"name":"Kabupaten Tana Tidung","serial":6410,"type":2,"latitude":3.551869,"longitude":117.0794082,"status":1}, +{"nid":1633,"parent_nid":23,"name":"Kota Balikpapan","serial":6471,"type":2,"latitude":-1.2635389,"longitude":116.8278833,"status":1}, +{"nid":1634,"parent_nid":23,"name":"Kota Samarinda","serial":6472,"type":2,"latitude":-0.502183,"longitude":117.153801,"status":1}, +{"nid":1635,"parent_nid":23,"name":"Kota Tarakan","serial":6473,"type":2,"latitude":3.3,"longitude":117.6333333,"status":1}, +{"nid":1636,"parent_nid":23,"name":"Kota Bontang","serial":6474,"type":2,"latitude":0.1333333,"longitude":117.5,"status":1}, +{"nid":1637,"parent_nid":24,"name":"Kabupaten Bolaang Mongondow","serial":7101,"type":2,"latitude":0.6870994,"longitude":124.0641419,"status":1}, +{"nid":1638,"parent_nid":24,"name":"Kabupaten Minahasa","serial":7102,"type":2,"latitude":1,"longitude":124.5833333,"status":1}, +{"nid":1639,"parent_nid":24,"name":"Kabupaten Kepulauan Sangihe","serial":7103,"type":2,"latitude":3.5303212,"longitude":125.5438967,"status":1}, +{"nid":1640,"parent_nid":24,"name":"Kabupaten Kepulauan Talaud","serial":7104,"type":2,"latitude":4.092,"longitude":126.768,"status":1}, +{"nid":1641,"parent_nid":24,"name":"Kabupaten Minahasa Selatan","serial":7105,"type":2,"latitude":1.0946773,"longitude":124.4641848,"status":1}, +{"nid":1642,"parent_nid":24,"name":"Kabupaten Minahasa Utara","serial":7106,"type":2,"latitude":1.5327973,"longitude":124.994751,"status":1}, +{"nid":1643,"parent_nid":24,"name":"Kabupaten Bolaang Mongondow Utara","serial":7108,"type":2,"latitude":0.818691,"longitude":123.5280072,"status":1}, +{"nid":1644,"parent_nid":24,"name":"Kabupaten Siau Tagulandang Biaro","serial":7109,"type":2,"latitude":2.345964,"longitude":125.4124355,"status":1}, +{"nid":1645,"parent_nid":24,"name":"Kabupaten Minahasa Tenggara","serial":7107,"type":2,"latitude":1.0278551,"longitude":124.7298765,"status":1}, +{"nid":1646,"parent_nid":24,"name":"Kota Manado","serial":7171,"type":2,"latitude":1.4917014,"longitude":124.842843,"status":1}, +{"nid":1647,"parent_nid":24,"name":"Kota Bitung","serial":7172,"type":2,"latitude":1.4553529,"longitude":125.204697,"status":1}, +{"nid":1648,"parent_nid":24,"name":"Kota Tomohon","serial":7173,"type":2,"latitude":1.3234131,"longitude":124.8384504,"status":1}, +{"nid":1649,"parent_nid":24,"name":"Kota Kotamobagu","serial":7174,"type":2,"latitude":0.7333333,"longitude":124.3166667,"status":1}, +{"nid":1650,"parent_nid":25,"name":"Kabupaten Banggai Kepulauan","serial":7207,"type":2,"latitude":-1.6408137,"longitude":123.5504076,"status":1}, +{"nid":1651,"parent_nid":25,"name":"Kabupaten Banggai","serial":7201,"type":2,"latitude":-1.6408137,"longitude":123.5504076,"status":1}, +{"nid":1652,"parent_nid":25,"name":"Kabupaten Morowali","serial":7206,"type":2,"latitude":-2.3003072,"longitude":121.5370003,"status":1}, +{"nid":1653,"parent_nid":25,"name":"Kabupaten Poso","serial":7202,"type":2,"latitude":-1.391922,"longitude":120.766998,"status":1}, +{"nid":1654,"parent_nid":25,"name":"Kabupaten Donggala","serial":7203,"type":2,"latitude":-0.4233155,"longitude":119.8352303,"status":1}, +{"nid":1655,"parent_nid":25,"name":"Kabupaten Toli-Toli","serial":7204,"type":2,"latitude":0.8768231,"longitude":120.7579834,"status":1}, +{"nid":1656,"parent_nid":25,"name":"Kabupaten Buol","serial":7205,"type":2,"latitude":0.9695452,"longitude":121.3541631,"status":1}, +{"nid":1657,"parent_nid":25,"name":"Kabupaten Parigi Moutong","serial":7208,"type":2,"latitude":0.5817607,"longitude":120.8039474,"status":1}, +{"nid":1658,"parent_nid":25,"name":"Kabupaten Tojo Una-Una","serial":7209,"type":2,"latitude":-1.098757,"longitude":121.5370003,"status":1}, +{"nid":1659,"parent_nid":25,"name":"Kota Palu","serial":7271,"type":2,"latitude":-0.898583,"longitude":119.850601,"status":1}, +{"nid":1660,"parent_nid":26,"name":"Kabupaten Selayar","serial":7301,"type":2,"latitude":-6,"longitude":120.5,"status":1}, +{"nid":1661,"parent_nid":26,"name":"Kabupaten Bulukumba","serial":7302,"type":2,"latitude":-5.4329368,"longitude":120.2051096,"status":1}, +{"nid":1662,"parent_nid":26,"name":"Kabupaten Bantaeng","serial":7303,"type":2,"latitude":-5.5169316,"longitude":120.0202964,"status":1}, +{"nid":1663,"parent_nid":26,"name":"Kabupaten Jeneponto","serial":7304,"type":2,"latitude":-5.554579,"longitude":119.6730939,"status":1}, +{"nid":1664,"parent_nid":26,"name":"Kabupaten Takalar","serial":7305,"type":2,"latitude":-5.4162493,"longitude":119.4875668,"status":1}, +{"nid":1665,"parent_nid":26,"name":"Kabupaten Gowa","serial":7306,"type":2,"latitude":-5.3102888,"longitude":119.742604,"status":1}, +{"nid":1666,"parent_nid":26,"name":"Kabupaten Sinjai","serial":7307,"type":2,"latitude":-5.2171961,"longitude":120.112735,"status":1}, +{"nid":1667,"parent_nid":26,"name":"Kabupaten Maros","serial":7309,"type":2,"latitude":-4.94695,"longitude":119.578903,"status":1}, +{"nid":1668,"parent_nid":26,"name":"Kabupaten Pangkajene Dan Kepulauan","serial":7310,"type":2,"latitude":-4.805035,"longitude":119.5571677,"status":1}, +{"nid":1669,"parent_nid":26,"name":"Kabupaten Barru","serial":7311,"type":2,"latitude":-4.4172651,"longitude":119.6730939,"status":1}, +{"nid":1670,"parent_nid":26,"name":"Kabupaten Bone","serial":7308,"type":2,"latitude":-2.083333,"longitude":120.216667,"status":1}, +{"nid":1671,"parent_nid":26,"name":"Kabupaten Soppeng","serial":7312,"type":2,"latitude":-4.3518541,"longitude":119.9277947,"status":1}, +{"nid":1672,"parent_nid":26,"name":"Kabupaten Wajo","serial":7313,"type":2,"latitude":-4.022229,"longitude":120.0665236,"status":1}, +{"nid":1673,"parent_nid":26,"name":"Kabupaten Sidenreng Rappang","serial":7314,"type":2,"latitude":-3.7738981,"longitude":120.0202964,"status":1}, +{"nid":1674,"parent_nid":26,"name":"Kabupaten Pinrang","serial":7315,"type":2,"latitude":-3.793071,"longitude":119.6408,"status":1}, +{"nid":1675,"parent_nid":26,"name":"Kabupaten Enrekang","serial":7316,"type":2,"latitude":-3.563128,"longitude":119.7612,"status":1}, +{"nid":1676,"parent_nid":26,"name":"Kabupaten Luwu","serial":7317,"type":2,"latitude":-3.3052214,"longitude":120.2512728,"status":1}, +{"nid":1677,"parent_nid":26,"name":"Kabupaten Tana Toraja","serial":7318,"type":2,"latitude":-3.0753003,"longitude":119.742604,"status":1}, +{"nid":1678,"parent_nid":26,"name":"Kabupaten Luwu Utara","serial":7322,"type":2,"latitude":-2.2690446,"longitude":119.9740534,"status":1}, +{"nid":1679,"parent_nid":26,"name":"Kabupaten Luwu Timur","serial":7324,"type":2,"latitude":-2.5825518,"longitude":121.1710389,"status":1}, +{"nid":1680,"parent_nid":26,"name":"Kota Makassar","serial":7371,"type":2,"latitude":-5.1333333,"longitude":119.4166667,"status":1}, +{"nid":1681,"parent_nid":26,"name":"Kota Pare-Pare","serial":7372,"type":2,"latitude":-4.0166667,"longitude":119.6236111,"status":1}, +{"nid":1682,"parent_nid":26,"name":"Kota Palopo","serial":7373,"type":2,"latitude":-3,"longitude":120.2,"status":1}, +{"nid":1683,"parent_nid":27,"name":"Kabupaten Buton","serial":7404,"type":2,"latitude":-5.3096355,"longitude":122.9888319,"status":1}, +{"nid":1684,"parent_nid":27,"name":"Kabupaten Muna","serial":7403,"type":2,"latitude":-4.901629,"longitude":122.6277455,"status":1}, +{"nid":1685,"parent_nid":27,"name":"Kabupaten Konawe","serial":7402,"type":2,"latitude":-3.9380432,"longitude":122.0837445,"status":1}, +{"nid":1686,"parent_nid":27,"name":"Kabupaten Kolaka","serial":7401,"type":2,"latitude":-4.049665,"longitude":121.593803,"status":1}, +{"nid":1687,"parent_nid":27,"name":"Kabupaten Konawe Selatan","serial":7405,"type":2,"latitude":-4.2027915,"longitude":122.4467238,"status":1}, +{"nid":1688,"parent_nid":27,"name":"Kabupaten Bombana","serial":7406,"type":2,"latitude":-4.6543462,"longitude":121.9017954,"status":1}, +{"nid":1689,"parent_nid":27,"name":"Kabupaten Wakatobi","serial":7407,"type":2,"latitude":-5.3264442,"longitude":123.5951925,"status":1}, +{"nid":1690,"parent_nid":27,"name":"Kabupaten Kolaka Utara","serial":7408,"type":2,"latitude":-3.1347227,"longitude":121.1710389,"status":1}, +{"nid":1691,"parent_nid":27,"name":"Kabupaten Buton Utara","serial":7410,"type":2,"latitude":-4.7023424,"longitude":123.0338767,"status":1}, +{"nid":1692,"parent_nid":27,"name":"Kabupaten Konawe Utara","serial":7409,"type":2,"latitude":-3.3803291,"longitude":122.0837445,"status":1}, +{"nid":1693,"parent_nid":27,"name":"Kota Kendari","serial":7471,"type":2,"latitude":-3.972201,"longitude":122.5149028,"status":1}, +{"nid":1694,"parent_nid":27,"name":"Kota Bau-Bau","serial":7472,"type":2,"latitude":-5.46667,"longitude":122.633,"status":1}, +{"nid":1695,"parent_nid":28,"name":"Kabupaten Boalemo","serial":7502,"type":2,"latitude":0.7013419,"longitude":122.2653887,"status":1}, +{"nid":1696,"parent_nid":28,"name":"Kabupaten Gorontalo","serial":7501,"type":2,"latitude":0.5333333,"longitude":123.0666667,"status":1}, +{"nid":1697,"parent_nid":28,"name":"Kabupaten Pohuwato","serial":7504,"type":2,"latitude":0.7055278,"longitude":121.7195459,"status":1}, +{"nid":1698,"parent_nid":28,"name":"Kabupaten Bone Bolango","serial":7503,"type":2,"latitude":0.5657885,"longitude":123.3486147,"status":1}, +{"nid":1699,"parent_nid":28,"name":"Kabupaten Gorontalo Utara","serial":7505,"type":2,"latitude":0.9252647,"longitude":122.4920088,"status":1}, +{"nid":1700,"parent_nid":28,"name":"Kota Gorontalo","serial":7571,"type":2,"latitude":0.5333333,"longitude":123.0666667,"status":1}, +{"nid":1701,"parent_nid":29,"name":"Kabupaten Majene","serial":7605,"type":2,"latitude":-3.0297251,"longitude":118.9062794,"status":1}, +{"nid":1702,"parent_nid":29,"name":"Kabupaten Polewali Mandar","serial":7604,"type":2,"latitude":-3.3419323,"longitude":119.1390642,"status":1}, +{"nid":1703,"parent_nid":29,"name":"Kabupaten Mamasa","serial":7603,"type":2,"latitude":-2.960135,"longitude":119.368202,"status":1}, +{"nid":1704,"parent_nid":29,"name":"Kabupaten Mamuju","serial":7602,"type":2,"latitude":-2.7293364,"longitude":118.9295737,"status":1}, +{"nid":1705,"parent_nid":29,"name":"Kabupaten Mamuju Utara","serial":7601,"type":2,"latitude":-1.5264542,"longitude":119.5107708,"status":1}, +{"nid":1706,"parent_nid":30,"name":"Kabupaten Maluku Tenggara Barat","serial":8103,"type":2,"latitude":-7.5322642,"longitude":131.3611121,"status":1}, +{"nid":1707,"parent_nid":30,"name":"Kabupaten Maluku Tenggara","serial":8102,"type":2,"latitude":-5.7512455,"longitude":132.7271587,"status":1}, +{"nid":1708,"parent_nid":30,"name":"Kabupaten Maluku Tengah","serial":8101,"type":2,"latitude":-3.0166501,"longitude":129.4864411,"status":1}, +{"nid":1709,"parent_nid":30,"name":"Kabupaten Buru Selatan","serial":8109,"type":2,"latitude":-3.3927754,"longitude":126.7819505,"status":1}, +{"nid":1710,"parent_nid":30,"name":"Kabupaten Kepulauan Aru","serial":8107,"type":2,"latitude":-6.1946502,"longitude":134.5501935,"status":1}, +{"nid":1711,"parent_nid":30,"name":"Kabupaten Seram Bagian Barat","serial":8106,"type":2,"latitude":-3.1271575,"longitude":128.4008357,"status":1}, +{"nid":1712,"parent_nid":30,"name":"Kabupaten Seram Bagian Timur","serial":8105,"type":2,"latitude":-3.4150761,"longitude":130.390488,"status":1}, +{"nid":1713,"parent_nid":30,"name":"Kota Ambon","serial":8171,"type":2,"latitude":-3.65607,"longitude":128.166419,"status":1}, +{"nid":1714,"parent_nid":30,"name":"KotaTual","serial":8172,"type":2,"latitude":-5.640851,"longitude":132.7475093,"status":1}, +{"nid":1715,"parent_nid":31,"name":"Kabupaten Halmahera Barat","serial":8201,"type":2,"latitude":1.3121235,"longitude":128.4849923,"status":1}, +{"nid":1716,"parent_nid":31,"name":"Kabupaten Halmahera Tengah","serial":8202,"type":2,"latitude":1.3121235,"longitude":128.4849923,"status":1}, +{"nid":1717,"parent_nid":31,"name":"Kabupaten Kepulauan Sula","serial":8205,"type":2,"latitude":-1.8666667,"longitude":125.3666667,"status":1}, +{"nid":1718,"parent_nid":31,"name":"Kabupaten Halmahera Selatan","serial":8204,"type":2,"latitude":1.3121235,"longitude":128.4849923,"status":1}, +{"nid":1719,"parent_nid":31,"name":"Kabupaten Halmahera Utara","serial":8203,"type":2,"latitude":1.3121235,"longitude":128.4849923,"status":1}, +{"nid":1720,"parent_nid":31,"name":"Kabupaten Halmahera Timur","serial":8206,"type":2,"latitude":1.3121235,"longitude":128.4849923,"status":1}, +{"nid":1721,"parent_nid":31,"name":"Kota Ternate","serial":8271,"type":2,"latitude":0.7833333,"longitude":127.3666667,"status":1}, +{"nid":1722,"parent_nid":31,"name":"Kota Tidore Kepulauan","serial":8272,"type":2,"latitude":0.6833333,"longitude":127.4,"status":1}, +{"nid":1723,"parent_nid":32,"name":"Kabupaten Fakfak","serial":9203,"type":2,"latitude":-2.885237,"longitude":132.2658282,"status":1}, +{"nid":1724,"parent_nid":32,"name":"Kabupaten Kaimana","serial":9208,"type":2,"latitude":-3.660925,"longitude":133.774506,"status":1}, +{"nid":1725,"parent_nid":32,"name":"Kabupaten Teluk Wondama","serial":9207,"type":2,"latitude":-2.8551699,"longitude":134.3236557,"status":1}, +{"nid":1726,"parent_nid":32,"name":"Kabupaten Teluk Bintuni","serial":9206,"type":2,"latitude":-1.9056848,"longitude":133.329466,"status":1}, +{"nid":1727,"parent_nid":32,"name":"Kabupaten Manokwari","serial":9202,"type":2,"latitude":-0.8614531,"longitude":134.0620421,"status":1}, +{"nid":1728,"parent_nid":32,"name":"Kabupaten Sorong Selatan","serial":9204,"type":2,"latitude":-0.8666667,"longitude":131.25,"status":1}, +{"nid":1729,"parent_nid":32,"name":"Kota Sorong","serial":9271,"type":2,"latitude":-0.8666667,"longitude":131.25,"status":1}, +{"nid":1730,"parent_nid":32,"name":"Kabupaten Raja Ampat","serial":9205,"type":2,"latitude":-1.0915151,"longitude":130.8778586,"status":1}, +{"nid":1731,"parent_nid":32,"name":"Kabupaten Sorong","serial":9201,"type":2,"latitude":-0.8666667,"longitude":131.25,"status":1}, +{"nid":1732,"parent_nid":33,"name":"Kabupaten Merauke","serial":9101,"type":2,"latitude":-8.4960406,"longitude":140.3945527,"status":1}, +{"nid":1733,"parent_nid":33,"name":"Kabupaten Jayawijaya","serial":9102,"type":2,"latitude":-4.0004481,"longitude":138.7995122,"status":1}, +{"nid":1734,"parent_nid":33,"name":"Kabupaten Jayapura","serial":9103,"type":2,"latitude":-2.533,"longitude":140.717,"status":1}, +{"nid":1735,"parent_nid":33,"name":"Kabupaten Nabire","serial":9104,"type":2,"latitude":-3.5095462,"longitude":135.7520985,"status":1}, +{"nid":1736,"parent_nid":33,"name":"Kabupaten Kepulauan Yapen","serial":9105,"type":2,"latitude":-1.7469359,"longitude":136.1709012,"status":1}, +{"nid":1737,"parent_nid":33,"name":"Kabupaten Biak Numfor","serial":9106,"type":2,"latitude":-1.0381022,"longitude":135.9800848,"status":1}, +{"nid":1738,"parent_nid":33,"name":"Kabupaten Paniai","serial":9108,"type":2,"latitude":-3.7876441,"longitude":136.3624686,"status":1}, +{"nid":1739,"parent_nid":33,"name":"Kabupaten Puncak Jaya","serial":9107,"type":2,"latitude":-4.0836111,"longitude":137.1847222,"status":1}, +{"nid":1740,"parent_nid":33,"name":"Kabupaten Mimika","serial":9109,"type":2,"latitude":-4.4553223,"longitude":137.1362125,"status":1}, +{"nid":1741,"parent_nid":33,"name":"Kabupaten Boven Digoel","serial":9116,"type":2,"latitude":-5.7400018,"longitude":140.3481835,"status":1}, +{"nid":1742,"parent_nid":33,"name":"Kabupaten Mappi","serial":9117,"type":2,"latitude":-7.102232,"longitude":139.396393,"status":1}, +{"nid":1743,"parent_nid":33,"name":"Kabupaten Asmat","serial":9118,"type":2,"latitude":-5.0573958,"longitude":138.3988186,"status":1}, +{"nid":1744,"parent_nid":33,"name":"Kabupaten Yahukimo","serial":9113,"type":2,"latitude":-4.4939717,"longitude":139.5279996,"status":1}, +{"nid":1745,"parent_nid":33,"name":"Kabupaten Pegunungan Bintang","serial":9112,"type":2,"latitude":-4.5589872,"longitude":140.5135589,"status":1}, +{"nid":1746,"parent_nid":33,"name":"Kabupaten Tolikara","serial":9114,"type":2,"latitude":-3.481132,"longitude":138.4787258,"status":1}, +{"nid":1747,"parent_nid":33,"name":"Kabupaten Sarmi","serial":9110,"type":2,"latitude":-1.868727,"longitude":138.743607,"status":1}, +{"nid":1748,"parent_nid":33,"name":"Kabupaten Keerom","serial":9111,"type":2,"latitude":-3.3449536,"longitude":140.7624493,"status":1}, +{"nid":1749,"parent_nid":33,"name":"Kabupaten Waropen","serial":9115,"type":2,"latitude":-2.8435717,"longitude":136.670534,"status":1}, +{"nid":1750,"parent_nid":33,"name":"Kabupaten Supiori","serial":9119,"type":2,"latitude":-0.7295099,"longitude":135.6385125,"status":1}, +{"nid":1751,"parent_nid":33,"name":"Kabupaten Mamberamo Raya","serial":9120,"type":2,"latitude":-2.5331255,"longitude":137.7637565,"status":1}, +{"nid":1752,"parent_nid":33,"name":"Kota Jayapura","serial":9171,"type":2,"latitude":-2.533,"longitude":140.717,"status":1}, +{"nid":1753,"parent_nid":2,"name":"Kabupaten Labuhanbatu Utara","serial":1223,"type":2,"latitude":2.3465638,"longitude":99.8124935,"status":1}, +{"nid":1754,"parent_nid":2,"name":"Kabupaten Labuhanbatu Selatan","serial":1222,"type":2,"latitude":1.8799353,"longitude":100.1703257,"status":1}, +{"nid":1756,"parent_nid":2,"name":"Kabupaten Nias Utara","serial":1224,"type":2,"latitude":1.1255279,"longitude":97.5247243,"status":1}, +{"nid":1757,"parent_nid":2,"name":"Kabupaten Nias Barat","serial":1225,"type":2,"latitude":1.1255279,"longitude":97.5247243,"status":1}, +{"nid":1758,"parent_nid":2,"name":"Kota Gunungsitoli","serial":1278,"type":2,"latitude":1.281964,"longitude":97.61594,"status":1}, +{"nid":1759,"parent_nid":4,"name":"Kabupaten Kepulauan Meranti","serial":1410,"type":2,"latitude":0.9208765,"longitude":102.6675575,"status":1}, +{"nid":1760,"parent_nid":5,"name":"Kota Sungai Penuh","serial":1572,"type":2,"latitude":-2.06314,"longitude":101.387199,"status":1}, +{"nid":1761,"parent_nid":7,"name":"Kabupaten Bengkulu Tengah","serial":1709,"type":2,"latitude":-3.7955556,"longitude":102.2591667,"status":1}, +{"nid":1762,"parent_nid":8,"name":"Kabupaten Tulangbawang Barat","serial":1806,"type":2,"latitude":-4.5256967,"longitude":105.0791228,"status":1}, +{"nid":1763,"parent_nid":8,"name":"Kabupaten Pringsewu","serial":1810,"type":2,"latitude":-5.3539884,"longitude":104.9622498,"status":1}, +{"nid":1764,"parent_nid":8,"name":"Kabupaten Mesuji","serial":1811,"type":2,"latitude":-4.0044783,"longitude":105.3131185,"status":1}, +{"nid":1765,"parent_nid":10,"name":"Kabupaten Lingga","serial":2104,"type":2,"latitude":-0.1627686,"longitude":104.6354631,"status":1}, +{"nid":1766,"parent_nid":10,"name":"Kabupaten Anambas","serial":2105,"type":2,"latitude":3.1055459,"longitude":105.6537231,"status":1}, +{"nid":1767,"parent_nid":14,"name":"Kabupaten Sleman","serial":3404,"type":2,"latitude":-7.716165,"longitude":110.335403,"status":1}, +{"nid":1768,"parent_nid":16,"name":"Kota Tangerang Selatan","serial":3674,"type":2,"latitude":-6.2888889,"longitude":106.7180556,"status":1}, +{"nid":1769,"parent_nid":18,"name":"Kabupaten Lombok Utara","serial":5208,"type":2,"latitude":-8.3739076,"longitude":116.2777073,"status":1}, +{"nid":1770,"parent_nid":19,"name":"Kabupaten Sabu Raijua","serial":5302,"type":2,"latitude":-10.5541116,"longitude":121.8334868,"status":1}, +{"nid":1771,"parent_nid":24,"name":"Kabupaten Bolang Mongondow Timur","serial":7110,"type":2,"latitude":0.7152651,"longitude":124.4641848,"status":1}, +{"nid":1772,"parent_nid":24,"name":"Kabupaten Bolang Mongondow Selatan","serial":7111,"type":2,"latitude":0.4053215,"longitude":123.8411288,"status":1}, +{"nid":1773,"parent_nid":25,"name":"Kabupaten Sigi","serial":7210,"type":2,"latitude":-1.3834127,"longitude":120.0665236,"status":1}, +{"nid":1774,"parent_nid":26,"name":"Kabupaten Toraja Utara","serial":7326,"type":2,"latitude":-2.8621942,"longitude":119.8352303,"status":1}, +{"nid":1775,"parent_nid":30,"name":"Kabupaten Maluku Barat Daya","serial":8108,"type":2,"latitude":-7.7851588,"longitude":126.3498097,"status":1}, +{"nid":1776,"parent_nid":30,"name":"Kabupaten Buru","serial":8104,"type":2,"latitude":-3.3927754,"longitude":126.7819505,"status":1}, +{"nid":1778,"parent_nid":31,"name":"Kabupaten Pulau Morota","serial":8207,"type":2,"latitude":2.3656672,"longitude":128.4008357,"status":1}, +{"nid":1789,"parent_nid":32,"name":"Kabupaten Tambrauw","serial":9209,"type":2,"latitude":-0.781856,"longitude":132.3938375,"status":1}, +{"nid":1790,"parent_nid":32,"name":"Kabupaten Maybat","serial":9210,"type":2,"latitude":3.1472,"longitude":101.6997,"status":1}, +{"nid":1791,"parent_nid":33,"name":"Kabupaten Memberamo Tengah","serial":9121,"type":2,"latitude":-2.3745692,"longitude":138.3190276,"status":1}, +{"nid":1792,"parent_nid":33,"name":"Kabupaten Yalimo","serial":9122,"type":2,"latitude":-3.7852847,"longitude":139.4466005,"status":1}, +{"nid":1793,"parent_nid":33,"name":"Kabupaten Lanny Jaya","serial":9123,"type":2,"latitude":-3.971033,"longitude":138.3190276,"status":1}, +{"nid":1794,"parent_nid":33,"name":"Kabupaten Nduga","serial":9124,"type":2,"latitude":-4.4069496,"longitude":138.2393528,"status":1}, +{"nid":1795,"parent_nid":33,"name":"Kabupaten Puncak","serial":9125,"type":2,"latitude":-6.7125476,"longitude":106.9542425,"status":1}, +{"nid":1796,"parent_nid":33,"name":"Kabupaten Dogiyai","serial":9126,"type":2,"latitude":-4.0193872,"longitude":135.9610446,"status":1}, +{"nid":1797,"parent_nid":33,"name":"Kabupaten Intan Jaya","serial":9127,"type":2,"latitude":-3.5076422,"longitude":136.7478493,"status":1}, +{"nid":1798,"parent_nid":33,"name":"Kabupaten Deiyai","serial":9128,"type":2,"latitude":-4.0974893,"longitude":136.4393054,"status":1} +] \ No newline at end of file diff --git a/src/androgpio/mycodes.java b/src/androgpio/mycodes.java new file mode 100644 index 0000000..96e9b38 --- /dev/null +++ b/src/androgpio/mycodes.java @@ -0,0 +1,1159 @@ +package androgpio; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import androgpio.customsocket.JsonArray; +import androgpio.customsocket.JsonObject; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.keywords.DateTime; + + +public class mycodes { + private static final String gpiofolder = "/sys/class/gpio"; + public static final String gpioexportfolder = gpiofolder + "/export"; + public static final String gpiounexportfolder = gpiofolder + "/unexport"; + + public static String gpio_direction_path(int pin) { + return gpio_path(pin)+"/direction"; + } + + public static String gpio_value_path(int pin) { + return gpio_path(pin)+"/value"; + } + + public static String gpio_path(int pin) { + return gpiofolder+"/gpio"+String.valueOf(pin); + } + + public static String path_combine(String...filename) { + if (filename != null && filename.length > 0) { + StringBuilder str = new StringBuilder(); + for (String xx : filename) { + if (str.length() > 0) + str.append("/"); + str.append(xx); + } + return str.toString(); + } + return ""; + } + + /** + * Convert Datetime Tick to Date String, on format DD-MM-YYYY + * @param tick : datetime tick in long + * @return empty string if tick less than 1 + */ + public static String Tick_To_DDMMYYYY(long tick) { + if (tick>0) { + StringBuilder str = new StringBuilder(); + int temp; + temp = DateTime.GetDayOfMonth(tick); + if (temp<10) str.append("0"); + str.append(temp); + str.append("-"); + + temp = DateTime.GetMonth(tick); + if (temp<10) str.append("0"); + str.append(temp); + str.append("-"); + + str.append(DateTime.GetYear(tick)); + return str.toString(); + } + return ""; + } + + /** + * Convert Datetime Tick to Time String, on format HH:MM:SS + * @param tick : datetime tick in long + * @return empty string if tick less than 1 + */ + public static String Tick_To_HHMMSS(long tick) { + if (tick>0) { + StringBuilder str = new StringBuilder(); + int temp; + temp = DateTime.GetHour(tick); + if (temp<10) str.append("0"); + str.append(temp); + str.append(":"); + + temp = DateTime.GetMinute(tick); + if (temp<10) str.append("0"); + str.append(temp); + str.append(":"); + + temp = DateTime.GetSecond(tick); + if (temp<10) str.append("0"); + str.append(temp); + return str.toString(); + } + return ""; + } + + /** + * Convert Datetime Tick to String , on format DD-MM-YYYY HH:MM:SS + * @param tick : datetime tick in long + * @return empty string if tick less than 1 + */ + public static String Tick_To_DDMMYYYY_HHMMSS(long tick) { + if (tick>0) { + return Tick_To_DDMMYYYY(tick) + " "+ Tick_To_HHMMSS(tick); + } else return ""; + + } + + /** + * Check if GPIO exist + * + * @param pinnya : pin number + * @return true if exist + */ + public static boolean GPIO_Exist(int pinnya) { + + File ff = new File(gpiofolder + "/" + GetGPIOName(pinnya)); + if (ff != null) { + if (ff.exists()) + return true; + } + return false; + } + + /** + * Check if file is exists, readable and writable + * @param dir Directory + * @param filename Filename + * @return true if file is exists, readable and writable + */ + public static boolean path_exists(String dir, String filename) { + File ff = new File(dir, filename); + if (ff != null) { + if (ff.isFile()) { + return true; + } + + } + return false; + } + + public static boolean path_readable(String dir, String filename) { + File ff = new File(dir, filename); + if (ff != null) { + if (ff.isFile()) { + if (ff.canRead()) { + return true; + } + } + } + return false; + } + + public static boolean path_writable(String dir, String filename) { + File ff = new File(dir, filename); + if (ff != null) { + if (ff.isFile()) { + if (ff.canWrite()) { + return true; + } + } + } + return false; + } + + /** + * Execute sudo di Android, tapi gak butuh result balik nya + * @param strings + */ + public static boolean sudo(String...strings) { + try{ + Process su = Runtime.getRuntime().exec("su"); + System.out.println("getting process for su"); + DataOutputStream outputStream = new DataOutputStream(su.getOutputStream()); + + for (String s : strings) { + outputStream.writeBytes(s+"\n"); + System.out.println("writing "+s); + outputStream.flush(); + } + + outputStream.writeBytes("exit\n"); + System.out.println("Writing exit"); + outputStream.flush(); + try { + su.waitFor(); + } catch (InterruptedException e) { + System.out.println("sudo error:"+e.getMessage()); + } + outputStream.close(); + return true; + }catch(IOException e){ + System.out.println("sudo error:"+e.getMessage()); + + } + return false; + } + + /** + * Execute command dengan sudo, dan menunggu hasil nya + * @param strings commands + * @return string hasil nya + */ + public static String sudoForResult(String...strings) { + String res = ""; + DataOutputStream outputStream = null; + InputStream response = null; + try{ + Process su = Runtime.getRuntime().exec("su"); + outputStream = new DataOutputStream(su.getOutputStream()); + response = su.getInputStream(); + + for (String s : strings) { + outputStream.writeBytes(s+"\n"); + outputStream.flush(); + } + + outputStream.writeBytes("exit\n"); + outputStream.flush(); + try { + su.waitFor(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + res = readFully(response); + } catch (IOException e){ + e.printStackTrace(); + } finally { + Closer.closeSilently(outputStream, response); + } + return res; + } + + public static String readFully(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length = 0; + while ((length = is.read(buffer)) != -1) { + baos.write(buffer, 0, length); + } + return baos.toString("UTF-8"); + } + + public static String FileRead(File f) { + try(FileInputStream fis = new FileInputStream(f)){ + byte[] buf = new byte[fis.available()]; + fis.read(buf); + fis.close(); + return new String(buf); + } catch (IOException ignored) { + + } + return null; + } + + /** + * Write value to file + * + * @param f : file to write + * @param value : value to write + * @return true if success + */ + public static boolean FileWrite(File f, String value) { + return FileWrite(f, value.getBytes()); + } + + + + /** + * Write value to file + * + * @param f : file to write + * @param value : value to write + * @return true if success + */ + public static boolean FileWrite(File f, byte[] value) { + try (FileOutputStream fos = new FileOutputStream(f)) { + fos.write(value); + fos.close(); + return true; + } catch (IOException ignored) { + + } + return false; + } + + /** + * Export GPIO pin + * + * @param pinnya : pin number + * @return true if success + * @throws IOException + * @throws SecurityException + */ + public static boolean GPIO_Export(int pinnya) throws SecurityException, IOException { + File ff = new File(gpioexportfolder); + if (ff != null) { + if (ff.exists()) { + if (ff.canWrite()) { + FileOutputStream fos = new FileOutputStream(ff); + String datatowrite = String.valueOf(pinnya); + fos.write(datatowrite.getBytes()); + fos.close(); + + File fd = new File(gpio_path(pinnya)); + if (fd.exists()) { + return true; + } else BA.Log("GPIO Export failed, "+fd.getAbsolutePath()+" is not exists after export"); + } else BA.Log("GPIO Export failed, "+ff.getAbsolutePath()+" is not writable"); + } else BA.Log("GPIO Export failed, "+ff.getAbsolutePath()+" not exists"); + } + return false; + } + + /** + * Un-Export GPIO pin + * + * @param pinnya : pin number + * @return true if success + * @throws IOException + * @throws SecurityException + */ + public static boolean GPIO_UnExport(int pinnya) throws IOException,SecurityException { + File ff = new File(gpiounexportfolder); + if (ff != null) { + if (ff.exists()) { + if (ff.canWrite()) { + FileOutputStream fos = new FileOutputStream(ff); + String datatowrite = String.valueOf(pinnya); + fos.write(datatowrite.getBytes()); + fos.close(); + return true; + } else BA.Log("GPIO Unexport failed, "+ff.getAbsolutePath()+" is not writable"); + } else BA.Log("GPIO Unexport failed, "+ff.getAbsolutePath()+" not exists"); + } + return false; + } + + /** + * Get GPIO direction + * + * @param pinnya : pin number + * @return empty string if failed, "in" = Input, "out" = Output + * @throws IOException + * @throws SecurityException + */ + public static String GPIO_GetDirection(int pinnya) throws IOException,SecurityException { + File ff = new File(gpio_direction_path(pinnya)); + if (ff != null) { + if (ff.exists()) { + if (ff.canRead()) { + + FileInputStream fis = new FileInputStream(ff); + + byte[] buf = new byte[5]; + int xx = -1; + int ii = 0; + while (true) { + xx = fis.read(); + if (xx == -1) + break; + if (xx == 13) + continue; // skip CR + if (xx == 10) + continue; // skip LF + buf[ii] = (byte) xx; + if (ii < buf.length - 1) + ii += 1; + + } + fis.close(); + return new String(buf); + } else BA.Log("GPIO_GetDirection failed, "+ff.getAbsolutePath()+" is not readable"); + } else BA.Log("GPIO_GetDirection failed, "+ff.getAbsolutePath()+" not exists"); + } + return ""; + } + + /** + * Set GPIO direction + * + * @param pinnya : pin number + * @param asOutput : true for Output pin, and false for Input pin + * @return true if success + * @throws IOException + * @throws SecurityException + */ + public static boolean GPIO_SetDirection(int pinnya, boolean asOutput) throws IOException,SecurityException { + File ff = new File(gpio_direction_path(pinnya)); + if (ff != null) { + if (ff.exists()) { + if (ff.canWrite()) { + FileOutputStream fos = new FileOutputStream(ff); + fos.write(asOutput ? "out".getBytes():"in".getBytes()); + fos.close(); + return true; + } else BA.Log("GPIO_SetDirection failed for "+ff.getAbsolutePath()+" because not writable"); + } else BA.Log("GPIO_SetDirection failed for "+ff.getAbsolutePath()+", because not exists"); + } + return false; + } + + /** + * Get Value from GPIO + * + * @param pinnya : pin number + * @return 0 = low, 1 = high, -1 = unknown / failed + * @throws IOException + * @throws SecurityException + */ + public static int GPIO_GetValue(int pinnya) throws IOException,SecurityException { + int result = -1; + File ff = new File(gpio_value_path(pinnya)); + if (ff != null) { + if (ff.exists()) { + if (ff.canRead()) { + FileInputStream fis = new FileInputStream(ff); + int vv = fis.read(); + fis.close(); + if (vv == '0') { + result = 0; + } else if (vv == '1') { + result = 1; + } + + } else BA.Log("GPIO_GetValue failed, "+ff.getAbsolutePath()+" is not readable"); + } else BA.Log("GPIO_GetValue failed, "+ff.getAbsolutePath()+" not exits"); + } + return result; + } + + /** + * Set Value to GPIO + * + * @param pinnya : pin number + * @param isON : true = high, false = low + * @return true if success + * @throws IOException + * @throws SecurityException + */ + public static boolean GPIO_SetValue(int pinnya, boolean isON) throws IOException,SecurityException { + File ff = new File(gpio_value_path(pinnya)); + if (ff != null) { + if (ff.exists()) { + if (ff.canWrite()) { + FileOutputStream fos = new FileOutputStream(ff); + fos.write(isON ? '1':'0'); + fos.close(); + return true; + } else BA.Log("GPIO_SetValue failed, "+ff.getAbsolutePath()+" is not writable"); + } else BA.Log("GPIO_SetValue failed, "+ff.getAbsolutePath()+" not exists"); + } + return false; + } + + + + /** + * Translate to correct gpio name + * + * @param pinnya : pin number + * @return + */ + private static String GetGPIOName(int pinnya) { + return "gpio" + pinnya; + } + + public static String printbytes(byte[] bb) { + if (bb == null) + return ""; + if (bb.length < 0) + return ""; + StringBuilder result = new StringBuilder(); + for (byte xx : bb) { + if (result.length() > 0) + result.append(" , "); + result.append(Bit.ToHexString(xx & 0xFF)); + } + return result.toString(); + } + + /** + * Change format of GPS Latitude String for ddmm.mmmm to dd.ddddd + * + * @param vv : string in format ddmm.mmmm + * @param result : array double , length 1 , untuk passing hasilnya + * @return false kalau gagal + */ + public static boolean Gps_Latitude_formatchange(String vv, double[] result) { + if (result == null) + return false; + if (result.length < 1) + return false; // gak ada tempatnya + if (vv.length() < 6) + return false; // minimal ddmm.m --> 6 karakter + if (vv.indexOf('.') != 4) + return false; // tidak ada titik nya + + try { + + double dd = Double.parseDouble(vv.substring(0, 2)); // dd + double mm = Double.parseDouble(vv.substring(2)); // mm.mmmm + + result[0] = Gps_Latitude_Round(dd + (mm / 60)); + return true; + } catch (IndexOutOfBoundsException | NumberFormatException | NullPointerException e) { + return false; + } + } + + /** + * Change format of GPS Longitude String from dddmm.mmmm to dd.dddddd + * + * @param vv : String in format dddmm.mmmmm + * @param result : array double, length 1, untuk passing hasil + * @return false kalau gagal + */ + public static boolean Gps_Longitude_formatchange(String vv, double[] result) { + if (result == null) + return false; + if (result.length < 1) + return false; // gak ada tempatnya + if (vv.length() < 7) + return false; // minimal dddmm.m --> 7 karakter + if (vv.indexOf('.') != 5) + return false; // tidak ada titiknya + + try { + double dd = Double.parseDouble(vv.substring(0, 3)); // ddd + double mm = Double.parseDouble(vv.substring(3)); // mm.mmmmm + result[0] = Gps_Longitude_Round(dd + (mm / 60)); + return true; + } catch (IndexOutOfBoundsException | NumberFormatException | NullPointerException e) { + return false; + } + } + + /** + * Convert GPS Time String to array of byte + * + * @param vv : GPS time String (hhmmss.ss or hhmmss) + * @param result : array of byte, length = 3, {hour, minute, second} + * @return true if conversion success + */ + public static boolean Gps_UTCTime(String vv, byte[] result) { + if (result == null) + return false; + if (result.length < 3) + return false; // gak ada tempatnya + if (vv.length() < 6) + return false; // minimal hhmmss + + int indextitik = vv.indexOf("."); // kalau ada desimal second + if (indextitik != -1) { + vv = vv.substring(0, indextitik); // potong aja + } + + try { + result[0] = Byte.parseByte(vv.substring(0, 2)); + result[1] = Byte.parseByte(vv.substring(2, 4)); + result[2] = Byte.parseByte(vv.substring(4)); + return true; + } catch (IndexOutOfBoundsException | NumberFormatException e) { + BA.Log("Error getting GPS_UTCTime, Msg : " + e.getMessage() + ", Caused : " + e.getCause()); + return false; + } + } + + /** + * Convert GPS Date String to array of byte + * + * @param vv : GPS Date String (ddmmyy) + * @param result : array of byte, length = 3, {day, month, year} + * @return true if conversion success + */ + public static boolean Gps_UTCDate(String vv, byte[] result) { + if (result == null) + return false; + if (result.length < 3) + return false; // gak ada tempatnya + if (vv.length() < 6) + return false; // harus ddmmyy + + try { + result[0] = Byte.parseByte(vv.substring(0, 2)); + result[1] = Byte.parseByte(vv.substring(2, 4)); + result[2] = Byte.parseByte(vv.substring(4)); + return true; + } catch (IndexOutOfBoundsException | NumberFormatException e) { + BA.Log("Error getting GPS_UTCDate, Msg : " + e.getMessage() + ", Caused : " + e.getCause()); + return false; + } + } + + /** + * Get Longitude Polarity + * + * @param vv : W = negative, E = positive, other = 0 + * @return 1 / -1 / 0 + */ + public static int Longitude_Polarity(String vv) { + if (vv != null) { + if (vv.length() > 0) { + byte[] xx = vv.getBytes(); + if (xx[0] == 87) // W + return -1; + else if (xx[0] == 69) // E + return 1; + else + return 0; // other + } + } + return 0; // invalid + } + + /** + * Convert GPS String to Double + * + * @param vv : string in format xx.xxxx + * @param result : array of double, length = 1 + * @return true if conversion success + */ + public static boolean Gps_Double(String vv, double[] result) { + if (result == null) + return false; + if (result.length < 1) + return false; + + try { + double xx = Double.parseDouble(vv); + result[0] = xx; + return true; + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Convert GPS String to Integer + * + * @param vv : String in integer + * @param result : array of integer, length = 1 + * @return true if conversion success + */ + public static boolean Gps_Int(String vv, int[] result) { + if (result == null) + return false; + if (result.length < 1) + return false; + try { + int xx = Integer.parseInt(vv); + result[0] = xx; + return true; + } catch (NumberFormatException e) { + return false; + } + + } + + /** + * Get first Char value from a GPS String + * + * @param vv : GPS String + * @return char value + */ + public static char Gps_Char(String vv) { + if (vv == null) + return (char) 0; + if (vv.length() < 1) + return (char) 0; + return vv.charAt(0); + } + + /** + * Force a value become Postive + * + * @param vv : value + * @return positive value from vv + */ + public static double SetPositive(double vv) { + if (vv < 0) + return -vv; + else + return vv; + } + + /** + * Force a value become Negative + * + * @param vv : value + * @return negative value from vv + */ + public static double SetNegative(double vv) { + if (vv > 0) + return -vv; + else + return vv; + } + + /** + * Convert GPS Error List to a String, separated by CRLF + * + * @param err : error list + * @return String + */ + public static String Gps_ErrList_to_String(List err) { + if (err == null) + return ""; + if (err.size() < 1) + return ""; + + StringBuilder str = new StringBuilder(); + + for (String xx : err) { + if (str.length() > 0) + str.append("\r\n"); + str.append(xx.trim()); + } + + return str.toString(); + } + + /** + * Make Latitude fractions to 4 digits + * + * @param vv : Latitude in original + * @return Latitude in dd.dddd + */ + public static double Gps_Latitude_Round(double vv) { + return Math.round(vv * 10000) / 10000.0; + } + + /** + * Make Longitude fractions to 4 digits + * + * @param vv : Longitude in original + * @return Longitude in ddd.dddd + */ + public static double Gps_Longitude_Round(double vv) { + return Math.round(vv * 10000) / 10000.0; + } + + /** + * Make 16bit value from 2 bytes + * @param Hbyte : high byte + * @param Lbyte : low byte + * @return value in 16bit + */ + public static int Make_Int16(byte Hbyte, byte Lbyte) { + int result = Bit.And(Hbyte, 0xFF); + result = result << 8; + result = result + Bit.And(Lbyte, 0xFF); + return result; + } + + /** + * Low byte of a 16bit value + * @param value16bit : value to get + * @return low byte of value16bit + */ + public static byte LowByte(int value16bit) { + return (byte) value16bit; + } + + /** + * low byte of a 16bit value, in unsigned value + * @param value16bit : value to get + * @return low byte of value16bit + */ + public static int LowByte_Unsigned(int value16bit) { + return Bit.And(value16bit, 0xFF); + } + + /** + * High byte of a 16bit value + * @param value16bit value to get + * @return high byte of value16bit + */ + public static byte HighByte(int value16bit) { + return (byte) (value16bit >> 8); + } + + /** + * High byte of a 16bit value, in unsigned value + * @param value16bit : value to get + * @return high byte of value16bit + */ + public static int HighByte_Unsigned(int value16bit) { + int result = Bit.And(value16bit, 0xFF00); + return Bit.ShiftRight(result, 8); + } + + public static boolean valid_string(String value) { + if (value!=null) { + return value.length()>0; + } + return false; + } + + public static List GetInputStreamString(Process pp) { + if (pp!=null) { + InputStream is = pp.getInputStream(); + if (is!=null) { + List result = new ArrayList<>(); + try(BufferedReader reader = new BufferedReader(new InputStreamReader(is))){ + while(true) { + String xx = reader.readLine(); + if (xx==null) break; + if (xx!=null) result.add(xx); + } + reader.close(); + return result; + } catch(Exception e) { + System.out.println("GetInputStreamString exception = "+e.getMessage()); + } + } + } + return new ArrayList(); + } + + public static String ConvertToByteString(String value) { + if (valid_string(value)) { + byte[] valuebyte = value.getBytes(StandardCharsets.UTF_8); + if (valuebyte!=null && valuebyte.length>0) { + StringBuilder str = new StringBuilder(); + str.append("{"); + for(byte xx : valuebyte) { + str.append(xx & 0xFF).append(" "); + } + str.append("}"); + return str.toString(); + } + } + return "{0}"; + } + + public static String GetValueAfterKey(String source, String key) { + if (valid_string(key) && valid_string(source)) { + int i1 = source.indexOf(key); + if (i1>0) { + int i2 = source.indexOf(" ",i1+key.length()); + String result = i2>0 ? source.substring(i1+key.length(), i2) : source.substring(i1+key.length()); + return result; + } + } + return null; + } + + public static int ToInt(String value, int defaultvalue) { + try { + Integer result = Integer.valueOf(value); + return result; + } catch(NumberFormatException e) { + return defaultvalue; + } + } + + public static long ToLong(String value, long defaultvalue) { + try { + Long result = Long.valueOf(value); + return result; + } catch(NumberFormatException e) { + return defaultvalue; + } + } + + public static String Combine(final String combiner, final String...strings) { + if (strings!=null && strings.length>0) { + StringBuilder str = new StringBuilder(); + for(String x : strings) { + if (valid_string(x)) { + if (str.length()>0) str.append(combiner); + str.append(x); + } + } + return str.toString(); + } + return ""; + } + + public static String Combine(String combiner, final Object... values) { + if (values != null && values.length > 0) { + StringBuilder str = new StringBuilder(); + for (Object xx : values) { + if (xx instanceof String && valid_string((String) xx)) { + if (str.length() > 0) + str.append(combiner); + str.append(String.valueOf(xx)); + } + } + return str.toString(); + } + return ""; + } + + + public static void Log(BA ba, String...strings) { + if (strings!=null && strings.length>0) { + StringBuilder str = new StringBuilder(); + for(String xx : strings) { + if (str.length()>0) str.append(" "); + str.append(xx); + } + if (ba!=null) { + BA.Log(str.toString()); + } else { + System.out.println(str.toString()); + } + } + } + + public static InetAddress ToInetAddress(String value) { + try { + return InetAddress.getByName(value); + } catch (UnknownHostException | SecurityException e) { + return null; + } + } + + public static boolean valid_stringarray(String[] value) { + return value != null && value.length > 0; + } + + public static boolean valid_bytearray(byte[] value) { + return value != null && value.length > 0; + } + + public static boolean valid_JsonObject(JsonObject value) { + return value != null && value.HasSomeValue(); + } + + public static boolean valid_JsonArray(JsonArray value) { + return value != null && value.HasSomeValue(); + } + + public static boolean String_Is_JSONObject(String value) { + if (valid_string(value)) { + try { + new org.json.JSONObject(value); + return true; + } catch (org.json.JSONException e) { + + } + } + return false; + } + + + + + + public static boolean String_Is_JSONArray(String value) { + if (valid_string(value)) { + try { + new org.json.JSONArray(value); + return true; + } catch (org.json.JSONException e) { + + } + } + return false; + } + + public static boolean valid_Map(anywheresoftware.b4a.objects.collections.Map value) { + return value != null && value.IsInitialized() && value.getSize() > 0; + } + + public static boolean valid_List(anywheresoftware.b4a.objects.collections.List value) { + return value != null && value.IsInitialized() && value.getSize() > 0; + } + + public static String MaptoJsonString(anywheresoftware.b4a.objects.collections.Map value) { + if (valid_Map(value)) { + JSONObject xx = new JSONObject(value.getObject()); + return xx.toString(); + } + return "{}"; + } + + public static String ListtoJsonString(anywheresoftware.b4a.objects.collections.List value) { + if (valid_List(value)) { + JSONArray xx = new JSONArray(value.getObject()); + return xx.toString(); + } + return "[]"; + } + + public static String ArrayObjecttoJsonString(Object[] value) { + if (value != null && value.length > 0) { + JSONArray xx; + try { + xx = new JSONArray(value); + return xx.toString(); + } catch (JSONException e) { + BA.Log("ArrayObjecttoJsonString error : "+e.getMessage()); + } + + } + return "[]"; + } + + public static String Byte_toHex(byte xx) { + return Bit.ToHexString(Bit.And(xx, 0xFF)); + } + + public static String Int8_toHex(int xx) { + return Bit.ToHexString(Bit.And(xx, 0xFF)); + } + + public static String Int16_ToHex(int xx) { + return Bit.ToHexString(Bit.And(xx, 0xFFFF)); + } + + public static String Int32_ToHex(int xx) { + return Bit.ToHexString(xx); + } + + public static boolean Array_contain_String(String[] array, String value) { + if (array != null && array.length > 0) { + for (String xx : array) { + if (xx.equals(value)) + return true; + } + } + return false; + } + + public static boolean ObjectisString(Object value) { + return value!=null ? String.class.isInstance(value) : false; + } + + public static boolean ObjectisBoolean(Object value) { + return value != null ? Boolean.class.isInstance(value) : false; + } + + public static boolean ObjectisByte(Object value) { + return value != null ? Byte.class.isInstance(value) : false; + } + + public static boolean ObjectisInteger(Object value) { + return value != null ? Integer.class.isInstance(value) : false; + } + + public static boolean ObjectisDouble(Object value) { + return value != null ? Double.class.isInstance(value) : false; + } + + public static boolean ObjectisFloat(Object value) { + return value != null ? Float.class.isInstance(value) : false; + } + + public static boolean ObjectisLong(Object value) { + return value != null ? Long.class.isInstance(value) : false; + } + + public static boolean ObjectisShort(Object value) { + return value != null ? Short.class.isInstance(value) : false; + } + + public static boolean ObjectisCharacter(Object value) { + return value != null ? Character.class.isInstance(value) : false; + } + + public static boolean ObjectisByteArray(Object value) { + return value != null ? byte[].class.isInstance(value) : false; + } + + public static boolean ObjectisIntArray(Object value) { + return value != null ? int[].class.isInstance(value) : false; + } + + public static boolean ObjectisDoubleArray(Object value) { + return value != null ? double[].class.isInstance(value) : false; + } + + public static boolean ObjectisFloatArray(Object value) { + return value != null ? float[].class.isInstance(value) : false; + } + + public static boolean ObjectisLongArray(Object value) { + return value != null ? long[].class.isInstance(value) : false; + } + + public static boolean ObjectisShortArray(Object value) { + return value != null ? short[].class.isInstance(value) : false; + } + + public static boolean ObjectisCharArray(Object value) { + return value != null ? char[].class.isInstance(value) : false; + } + + public static boolean ObjectisBooleanArray(Object value) { + return value != null ? boolean[].class.isInstance(value) : false; + } + + public static boolean ObjectisObjectArray(Object value) { + return value != null ? Object[].class.isInstance(value) : false; + } + + public static boolean ObjectisStringArray(Object value) { + return value != null ? String[].class.isInstance(value) : false; + } + + public static double pembulatan_1desimal(double value) { + int temp = (int) (value * 10); + return temp / 10.0; + } + + public static double pembulatan_2desimal(double value) { + int temp = (int) (value * 100); + return temp / 100.0; + } + + public static float pembulatan_1desimal(float value) { + int temp = (int) (value * 10); + return temp / 10.0f; + } + + public static float pembulatan_2desimal(float value) { + int temp = (int) (value * 100); + return temp / 100.0f; + } + + /** + * Repair Json String + * java will add escape characters, so we need to remove it + * @param value : json string + * @return repaired json string + */ + public static String repair_json(String value) { + if (mycodes.valid_string(value)) { + CharSequence cs1 = "\\/"; + CharSequence cs2 = "/"; + if (value.contains(cs1)) { + value = value.replace(cs1, cs2); + } + return value; + } + return ""; + } + + public static boolean valid_portnumber(int port) { + return (port > 0) && (port < 65536); + } +} diff --git a/src/androgpio/networkrelated/IfConfigResult.java b/src/androgpio/networkrelated/IfConfigResult.java new file mode 100644 index 0000000..f4131e7 --- /dev/null +++ b/src/androgpio/networkrelated/IfConfigResult.java @@ -0,0 +1,120 @@ +package androgpio.networkrelated; + +import java.util.List; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; + +@BA.ShortName("IfConfigResult") +public class IfConfigResult { + + private String HWaddr; + private String Driver; + + private String inet_addr; + private String Mask; + private String inet6_addr; + + private boolean isUP; + + private final String HWaddrkey = "HWaddr "; + private final String inetaddrkey = "inet addr:"; + private final String inet6addrkey = "inet6 addr: "; + private final String Driverkey = "Driver "; + private final String Maskkey = "Mask:"; + + + public IfConfigResult(List values) { + for(String xx : values) { + if (contain_Driver(xx)) Driver = mycodes.GetValueAfterKey(xx, Driverkey); + if (contain_HWaddr(xx)) HWaddr = mycodes.GetValueAfterKey(xx, HWaddrkey); + if (contain_inet_addr(xx)) inet_addr = mycodes.GetValueAfterKey(xx, inetaddrkey); + if (contain_inet6_addr(xx)) inet6_addr=mycodes.GetValueAfterKey(xx, inet6addrkey); + if (contain_SubnetMask(xx)) Mask = mycodes.GetValueAfterKey(xx, Maskkey); + if (xx.contains("UP")) isUP = true; + } + } + + public String getInet_addr() { + return inet_addr; + } + + public void setInet_addr(String xx) { + inet_addr = xx; + } + + public void setInet6_addr(String xx) { + inet6_addr = xx; + } + + public String getInet6_addr() { + return inet6_addr; + } + + public boolean isUP() { + return isUP; + } + + public void setUP(boolean xx) { + isUP = xx; + } + + public String getHWaddr() { + return HWaddr; + } + + public String getDriver() { + return Driver; + } + + public String getMask() { + return Mask; + } + + private boolean contain_HWaddr(String xx) { + if (mycodes.valid_string(xx)) { + return xx.contains(HWaddrkey); + } + return false; + } + + private boolean contain_inet_addr(String xx) { + if (mycodes.valid_string(xx)) { + return xx.contains(inetaddrkey); + } + return false; + } + + private boolean contain_inet6_addr(String xx) { + if (mycodes.valid_string(xx)) { + return xx.contains(inet6addrkey); + } + return false; + } + + private boolean contain_Driver(String xx) { + if (mycodes.valid_string(xx)) { + return xx.contains(Driverkey); + } + return false; + } + + private boolean contain_SubnetMask(String xx) { + if (mycodes.valid_string(xx)) { + return xx.contains(Maskkey); + } + return false; + } + + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("MAC = "+HWaddr).append("\r\n"); + str.append("Driver = "+Driver).append("\r\n"); + str.append("Up = "+isUP).append("\r\n"); + str.append("IPV4 = "+inet_addr).append("\r\n"); + str.append("Subnet = "+Mask).append("\r\n"); + str.append("IPV6 = "+inet6_addr).append("\r\n"); + return str.toString(); + } + +} diff --git a/src/androgpio/networkrelated/ethernetmanager.java b/src/androgpio/networkrelated/ethernetmanager.java new file mode 100644 index 0000000..e668761 --- /dev/null +++ b/src/androgpio/networkrelated/ethernetmanager.java @@ -0,0 +1,643 @@ +package androgpio.networkrelated; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.topjohnwu.superuser.Shell; +import com.topjohnwu.superuser.Shell.GetShellCallback; + +import androgpio.mycodes; +import android.Manifest; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.text.TextUtils; +import anywheresoftware.b4a.BA; + + +@BA.ShortName("ethernet_manager") +@BA.Events(values= { + "log(msg as string)", + "linkpropertiesupdated", + "connectionstatus(up as boolean)" +}) +@BA.Permissions(values = { + Manifest.permission.ACCESS_NETWORK_STATE, + }) +@BA.DependsOn(values= { + "core-5.2.1.aar" +}) + +public class ethernetmanager { + private BA ba; + private String event; + private Object Me; + private boolean need_log_event = false; + private boolean need_linkpropertiesupdated_event = false; + private boolean need_connectionstatus_event = false; + private IfConfigResult ifconfigresult; + private String _gateway; + private String devicename; + private String _dhcpserver; + private ConnectivityManager _conman ; + private NetworkRequest netreq; + + private LinkProperties mLinkProp; + private String[] _dns; + + /** + * Initialize Ethernet Manager + * @param eventname eventname + * @param devicename Device Name want to manage + */ + public void Initialize(BA ba, String eventname, String devicename) { + this.ba = ba; + this.event = eventname; + this.devicename = devicename; + + check_events(); + Shell.getShell(new GetShellCallback() { + + @Override + public void onShell(Shell shell) { + mycodes.Log(ba, "Is root : "+isRooted()); + mycodes.Log(ba, "isApproot :"+isAppGrantedRoot()); + mycodes.Log(ba, "Shell alive :"+isShellAlive()); + } + + }); + } + + /** + * Check if Device is Rooted + * @return true if rooted + */ + public boolean isRooted() { + return Shell.getShell().isRoot(); + + } + + /** + * Check if this App is granted Root access + * @return true if if granted + */ + public boolean isAppGrantedRoot() { + return Shell.isAppGrantedRoot(); + } + + /** + * Check if Shell is still alive + * @return true if alive + */ + public boolean isShellAlive() { + return Shell.getShell().isAlive(); + } + + /** + * Get detected DHCP server + * @return null if not available + */ + public String getDHCPServer() { + return _dhcpserver; + } + + /** + * Get devicename for this ethernet manager + * @return devicename + */ + public String getDeviceName() { + return devicename; + } + + /** + * Get IP V4 address + * @return null if not available + */ + public String getIPV4_Address() { + return ifconfigresult!=null ? ifconfigresult.getInet_addr() : null; + } + + /** + * Get Subnet for IP V4 + * @return null if not available + */ + public String getSubnet() { + return ifconfigresult!=null ? ifconfigresult.getMask() : null; + } + + /** + * Get MAC Address + * @return null if not available + */ + public String getMAC_Address() { + return ifconfigresult!=null ? ifconfigresult.getHWaddr() : null; + } + + /** + * Get Gateway for IP V4 + * @return null if not available + */ + public String getGateway() { + return _gateway; + } + + /** + * Get IP V6 address + * @return null if not available + */ + public String getIPV6_Address() { + return ifconfigresult!=null ? ifconfigresult.getInet6_addr() : null; + } + + /** + * Get Detected Driver for this Ethernet + * @return null if not available + */ + public String getDriver() { + return ifconfigresult!=null ? ifconfigresult.getDriver() : null; + } + + + + /** + * Get DNS in one line string + * @return null if not available + */ + public String getDNS() { + if (_dns!=null && _dns.length>0) { + return TextUtils.join(";", _dns); + } + return null; + } + + /** + * Check if Ethernet is UP (enabled) + * @return true if enabled + */ + public boolean getUp() { + return ifconfigresult!=null ? ifconfigresult.isUP() : false; + } + + /** + * Check B4A events + */ + private void check_events() { + if (ba!=null) { + if (event!=null) { + if (event.length()>0) { + need_log_event = ba.subExists(event+"_log"); + need_linkpropertiesupdated_event = ba.subExists(event+"_linkpropertiesupdated"); + need_connectionstatus_event = ba.subExists(event+"_connectionstatus"); + } + } + } + } + + /** + * Turn ON / OFF Ethernet + * @param value True = Up, False = Down + * @return true if success + */ + public boolean SetUp(boolean value) { + final String cmd = mycodes.Combine(" ", "ifconfig", devicename, (value?"up":"down")); + + if (isAppGrantedRoot()) { + mycodes.Log(ba, "SetUp about to execute command "+cmd); + Shell.Result result = Shell.cmd(cmd).exec(); + raise_log_shellresult("SetUp",result.getCode(),result.getOut(), result.getErr()); + return result.isSuccess(); + } else mycodes.Log(ba, "SetUp failed, App is not granted root"); + return false; + } + + /** + * Set DHCP + * Need to trigger SetUp false then SetUp true to refresh network state + * @return true if success + */ + public boolean SetDHCP() { + final String cmd = mycodes.Combine(" ", "ifconfig",devicename,"default"); + if (isAppGrantedRoot()) { + mycodes.Log(ba, "SetDHCP about to execute command "+cmd); + Shell.Result result = Shell.cmd(cmd).exec(); + raise_log_shellresult("SetDHCP",result.getCode(), result.getOut(), result.getErr()); + return result.isSuccess(); + } else mycodes.Log(ba, "SetDHCP failed, App is not granted root"); + return false; + } + + /** + * Set Static + * @param ipaddress IP Address value + * @param subnet Subnet mask + * @param gateway Gateway + * @param dns1 DNS 1 + * @param dns2 DNS 2 + * @return true if all correct + */ + public boolean SetStatic(String ipaddress, String subnet, String gateway, String dns1, String dns2) { + boolean result = true; + result &= SetUp(false); + result &= SetIPV4_Address(ipaddress, subnet); + result &= SetGateway(gateway); + result &= SetDNS(dns1, dns2); + return result; + } + + + + /** + * Set IP V4 address + * @param ipvalue IP address value + * @param netmaskvalue Subnetmask value + * @return true if success + */ + public boolean SetIPV4_Address(String ipvalue, String netmaskvalue) { + InetAddress inetip = mycodes.ToInetAddress(ipvalue); + InetAddress inetmask = mycodes.ToInetAddress(netmaskvalue); + if (isAppGrantedRoot()) { + if (inetip!=null) { + if (inetmask==null) inetmask = mycodes.ToInetAddress("255.255.255.0"); // default + final String cmd = mycodes.Combine(" ", "ifconfig", devicename, inetip.getHostAddress(), "netmask", inetmask.getHostAddress(), "up"); + mycodes.Log(ba, "SetIPV4_Address about to execute command "+cmd); + Shell.Result result = Shell.cmd(cmd).exec(); + raise_log_shellresult("SetIPV4_Address", result.getCode(), result.getOut(), result.getErr()); + return result.isSuccess(); + } else mycodes.Log(ba, "SetIPV4_Address failed, IP Value is invalid"); + } else mycodes.Log(ba, "SetIPV4_Address failed, App is not granted root"); + return false; + } + + + /** + * Set Gateway + * Belum bisa ! + * @param value + * @return true if success + */ + public boolean SetGateway(String value) { + InetAddress inetvalue = mycodes.ToInetAddress(value); + + if (inetvalue!=null) { + + final String cmd = mycodes.Combine(" ", "route add default gw",inetvalue.getHostAddress(),"dev",devicename); + mycodes.Log(ba, "SetGateway about to execute "+cmd); + Shell.Result result = Shell.cmd(cmd).exec(); + raise_log_shellresult("SetGateway", result.getCode(), result.getOut(), result.getErr()); + return result.isSuccess(); + } else mycodes.Log(ba, "SetGateway failed, value is not valid ip address"); + return false; + } + + + + /** + * Set DNS + * Belum bisa ! + * @param value + * @return true if success + */ + public boolean SetDNS(String dns1, String dns2) { + InetAddress inetvalue1 = mycodes.ToInetAddress(dns1); + InetAddress inetvalue2 = mycodes.ToInetAddress(dns2); + + if (inetvalue1!=null || inetvalue2!=null) { + if (mLinkProp!=null) { + List dns = new ArrayList<>(); + dns.add(inetvalue1); + dns.add(inetvalue2); + mLinkProp.setDnsServers(dns); + return true; + } else mycodes.Log(ba, "SetDNS failed, LinkProperties is invalid"); + } else mycodes.Log(ba, "SetDNS failed, dns1 or dns2 is invalid"); + return false; + } + + + /** + * Check / Refresh Ethernet Information + * Call it on Activity_Resume + * @return true if success + */ + public boolean check_device() { + if (mycodes.valid_string(devicename)) { + // dapat MAC, iP4+6, Subnet, status UP + RUNNING + ifconfigresult = ifconfig(); + // dapat gateway + _gateway = getGatewayUsingIP(); + + if (ba!=null) { + _conman = (ConnectivityManager) ba.context.getSystemService(Context.CONNECTIVITY_SERVICE); + netreq = new NetworkRequest.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) + .build(); + } + + CheckUsingConnectivityManager(); + + } + return false; + } + + /** + * Network Callback Object used by ConnectivityManager + */ + private NetworkCallback ncb = new NetworkCallback() { + @Override + public void onAvailable(Network network) { + mycodes.Log(ba, devicename+" is available"); + raise_connectionstatus(true); + if (ifconfigresult!=null) ifconfigresult.setUP(true); + + } + + @Override + public void onLost(Network network) { + if (ifconfigresult!=null) ifconfigresult.setUP(false); + mycodes.Log(ba, devicename+" is Lost"); + raise_connectionstatus(false); + mLinkProp = null; + } + + @Override + public void onBlockedStatusChanged(Network network, boolean blocked) { + LinkProperties linkprop = _conman.getLinkProperties(network); + if (linkprop!=null && linkprop.getInterfaceName().equals(devicename)) { + mycodes.Log(ba, devicename, blocked?" is blocked":" is allowed"); + } + } + + @Override + public void onUnavailable() { + mycodes.Log(ba, "Unable to registerNetworkCallback on Ethernet"); + } + + @Override + public void onLinkPropertiesChanged(Network network, LinkProperties linkprop) { + if (linkprop!=null && linkprop.getInterfaceName().equals(devicename)) { + mycodes.Log(ba, "Linkproperties changed for ",devicename); + + // DHCP server + _dhcpserver = linkprop.getDhcpServerAddress().getHostAddress(); + mycodes.Log(ba, "DHCP Server :",_dhcpserver); + + // IP Addresses + List ipv4list = new ArrayList<>(); + List ipv6list = new ArrayList<>(); + Categorize_IPAddress(linkprop, ipv4list, ipv6list); + if (ifconfigresult!=null) { + + String ipv4 = ListInetAddressToString(ipv4list); + String ipv6 = ListInetAddressToString(ipv6list); + ifconfigresult.setInet_addr(ipv4); + ifconfigresult.setInet6_addr(ipv6); + mycodes.Log(ba, "IPV4 :",ipv4); + mycodes.Log(ba, "IPV6 :",ipv6); + } + + // DNS + List listdns = linkprop.getDnsServers(); + _dns = null; + if (listdns!=null && listdns.size()>0) { + _dns = new String[listdns.size()]; + for(int ii=0;ii listgateway = new ArrayList<>(); + linkprop.getRoutes().forEach(routeinfo->{ + if (routeinfo.hasGateway()) listgateway.add(routeinfo.getGateway().getHostAddress()); + }); + if (listgateway.size()>0) { + _gateway = TextUtils.join(";", listgateway.toArray(new String[0])); + mycodes.Log(ba, "Gateway :",_gateway); + } else mycodes.Log(ba,"No Gateway available"); + + raise_linkpropertiesupdated(); + mLinkProp = linkprop; + + + } + } + }; + + /** + * Unregister event + * Call it on Activity_Pause + */ + public void unregister_events() { + if (_conman!=null) { + _conman.unregisterNetworkCallback(ncb); + } else mycodes.Log(ba,"ConnectivityManager is null"); + } + + /** + * Getting gateway using ip route + * @param devicename ethernet name + * @return null if not available + */ + private String getGatewayUsingIP() { + final String cmd = "ip route get 8.8.8.8"; + Shell.Result result = Shell.cmd(cmd).exec(); + this.raise_log_shellresult("getGatewayUsingIP", result.getCode(),result.getOut(), result.getErr()); + List r2 = result.getOut(); + if (r2!=null && r2.size()>0) { + Pattern pattern = Pattern.compile("8.8.8.8 via (\\S+) dev (\\S+)"); + + for(String rx : r2) { + Matcher matcher = pattern.matcher(rx); + if (matcher.find()) { + String g1 = matcher.group(1); + String g2 = matcher.group(2); + if (g2.equals(devicename)) { + return g1; + } + } + } + + } + return null; + } + +// @Deprecated +// private List exec_su(String command){ +// mycodes.Log(ba, "Executing su "+command); +// List result = new ArrayList<>(); +// List errors = new ArrayList<>(); +// try { +// Process pp = Runtime.getRuntime().exec("su"); +// DataOutputStream os = new DataOutputStream(pp.getOutputStream()); +// BufferedReader is = new BufferedReader(new InputStreamReader(pp.getInputStream())); +// BufferedReader es = new BufferedReader(new InputStreamReader(pp.getErrorStream())); +// os.writeBytes(command+"\n"); +// os.flush(); +// os.writeBytes("exit\n"); +// os.flush(); +// os.close(); +// +// +// while(true) { +// String xx = is.readLine(); +// if (xx==null) break; +// if (mycodes.valid_string(xx)) result.add(xx); +// } +// is.close(); +// +// while(true) { +// String xx = es.readLine(); +// if (xx==null) break; +// if (mycodes.valid_string(xx)) errors.add(xx); +// } +// es.close(); +// +// int exitcode = pp.waitFor(); +// mycodes.Log(ba, "exitcode = "+exitcode); +// +// if (result.size()>0) +// mycodes.Log(ba, "There is result"); +// else +// mycodes.Log(ba, "No result"); +// if (errors.size()>0) +// mycodes.Log(ba, "there is error"); +// else +// mycodes.Log(ba, "No error"); +// +// return result; +// } catch (IOException | InterruptedException | SecurityException |NullPointerException | IllegalArgumentException e) { +// mycodes.Log(ba, "exec_su exception = "+e.getMessage()); +// } +// return result; +// } + +// @Deprecated +// /** +// * Exec shell command +// * @param command command to execute +// * @return List of String of response +// */ +// private List exec(String command){ +// mycodes.Log(ba, "Executing "+command); +// List result = new ArrayList<>(); +// if (mycodes.valid_string(command)) { +// try { +// Process pp = Runtime.getRuntime().exec(command); +// List ppresult = mycodes.GetInputStreamString(pp); +// if (ppresult!=null && ppresult.size()>0) { +// result.addAll(ppresult); +// } +// } catch (IOException e) { +// raise_log("Exception running command ["+command+"], exception="+e.getMessage()); +// } +// +// } +// return result; +// } + +// @Deprecated +// private List exec(String...commands){ +// +// return exec(TextUtils.join(" ", commands)); +// } + + /** + * Call ifconfig on devicename + * @param devicename ethernet name + * @return null if failed + */ + private IfConfigResult ifconfig() { + if (mycodes.valid_string(devicename)) { + final String cmd = "ifconfig "+devicename; + Shell.Result result = Shell.cmd(cmd).exec(); + this.raise_log_shellresult("ifconfig", result.getCode(), result.getOut(), result.getErr()); + List ppresult = result.getOut(); + if (ppresult.size()>0) { + IfConfigResult ifresult = new IfConfigResult(ppresult); + return ifresult; + + } else raise_log("check_device process result length is zero"); + } else raise_log("check_device devicename is invalid"); + return null; + } + + /** + * Call network monitoring using Connectivity Manager + */ + private void CheckUsingConnectivityManager() { + if (_conman!=null) { + _conman.registerNetworkCallback(netreq, ncb); + } else mycodes.Log(ba,"ConnectivityManager is null"); + } + + private void Categorize_IPAddress(final LinkProperties linkprop, final List ipv4, final List ipv6) { + if (linkprop!=null) { + linkprop.getLinkAddresses().forEach(linkaddress->{ + InetAddress inetaddress = linkaddress.getAddress(); + if (inetaddress instanceof Inet6Address) + { + if (ipv6!=null) { + ipv6.add(inetaddress); + } + } else if (inetaddress instanceof Inet4Address) { + if (ipv4!=null) { + ipv4.add(inetaddress); + } + } + }); + } + } + + + + private String ListInetAddressToString(List inet) { + if (inet!=null && inet.size()>0) { + + StringBuilder str = new StringBuilder(); + for(InetAddress ii : inet) { + if (str.length()>0) str.append(";"); + str.append(ii.getHostAddress()); + } + return str.toString(); + + } + return null; + } + + private void raise_log_shellresult(String functionname, int code, List output, List error) { + mycodes.Log(ba, mycodes.Combine(" ", functionname, "code =",String.valueOf(code))); + if (error!=null && error.size()>0) { + mycodes.Log(ba, mycodes.Combine(" ", functionname, "Error :")); + error.forEach(xx-> mycodes.Log(ba, xx)); + } + if (output!=null && output.size()>0) { + mycodes.Log(ba, mycodes.Combine(" ", functionname,"Output :")); + output.forEach(xx -> mycodes.Log(ba, xx)); + } + } + + /** + * raise event log + * @param msg Message data for log + */ + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_linkpropertiesupdated() { + if (need_linkpropertiesupdated_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_linkpropertiesupdated", false, null); + } + + private void raise_connectionstatus(boolean up) { + if (need_connectionstatus_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_connectionstatus", false, new Object[] {up}); + } +} diff --git a/src/androgpio/openweather/OpenWeather.java b/src/androgpio/openweather/OpenWeather.java new file mode 100644 index 0000000..ed6a6e7 --- /dev/null +++ b/src/androgpio/openweather/OpenWeather.java @@ -0,0 +1,227 @@ +package androgpio.openweather; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +import androgpio.mycodes; +import anywheresoftware.b4a.BA; + +@BA.Events(values = { + "log(msg as String)", + "getresult(success as boolean, weather as OpenWeatherData)" +}) + +@BA.ShortName("OpenWeather") +public class OpenWeather { + + // B4X Objects + private BA ba; + private String eventName; + private boolean need_log_event = false; + private boolean need_getresult_event = false; + + private String cityname; + private double latitude; + private double longitude; + private String apiKey; + private URL url; + private Gson gs; + private OpenWeatherEvent javaEvent; + + @BA.Hide + /** + * Java Constructor for OpenWeather + * @param latitude latitude of the location + * @param longitude longitude of the location + * @param apiKey API key for OpenWeather + */ + public OpenWeather(double latitude, double longitude, String apiKey, OpenWeatherEvent event) { + this.latitude = latitude; + this.longitude = longitude; + this.apiKey = apiKey; + this.javaEvent = event; + CreateURL(); + gs = new Gson(); + } + + @BA.Hide + /** + * Java Constructor for OpenWeather + * citiname is used, based on ISO 3166 + * For Indonesia, can be check at https://en.wikipedia.org/wiki/ISO_3166-2:ID + * @param citiname Name of the city, based on ISO 3166 + * @param apiKey API key for OpenWeather + * @param event Event for the object + */ + public OpenWeather(String citiname, String apiKey, OpenWeatherEvent event) { + this.cityname = citiname; + this.apiKey = apiKey; + this.javaEvent = event; + CreateURL(); + gs = new Gson(); + } + + /** + * Initializes the OpenWeather object for B4X + * @param eventname Name of the event + * @param latitude Latitude of the location + * @param longitude Longitude of the location + * @param apiKey API key for OpenWeather + */ + public void Initialize(BA ba, String eventname, double latitude, double longitude, String apiKey) { + this.ba = ba; + this.eventName = eventname.toLowerCase(BA.cul); + this.latitude = latitude; + this.longitude = longitude; + this.apiKey = apiKey; + + if (this.ba!=null) { + if (mycodes.valid_string(eventName)) { + need_log_event = ba.subExists(eventName + "_log"); + need_getresult_event = ba.subExists(eventName + "_getresult"); + } + } + + CreateURL(); + gs = new Gson(); + } + + /** + * Initializes the OpenWeather object for B4X + * City name is used, based on ISO 3166 + * For Indonesia, can be check at https://en.wikipedia.org/wiki/ISO_3166-2:ID + * @param eventname Name of the event + * @param cityname Name of the city, based on ISO 3166 + * @param apiKey API key for OpenWeather + */ + public void Initialize2(BA ba, String eventname, String cityname, String apiKey) { + this.ba = ba; + this.eventName = eventname.toLowerCase(BA.cul); + this.cityname = cityname; + this.apiKey = apiKey; + + if (this.ba != null) { + if (mycodes.valid_string(eventName)) { + need_log_event = ba.subExists(eventName + "_log"); + need_getresult_event = ba.subExists(eventName + "_getresult"); + } + } + + CreateURL(); + gs = new Gson(); + } + + /** + * Create URL for the OpenWeather API + */ + private void CreateURL() { + String xx ; + if (mycodes.valid_string(cityname)) { + xx = "https://api.openweathermap.org/data/2.5/weather?q=" + cityname + "&appid=" + apiKey+"&units=metric"; + } else { + xx = "https://api.openweathermap.org/data/2.5/weather?lat=" + latitude + "&lon=" + longitude + "&appid=" + apiKey+"&units=metric"; + } + try { + url = new URL(xx); + } catch (MalformedURLException e) { + raise_log("Error creating URL: " + e.getMessage()); + } + } + + /** + * Get Latitude + * @return Latitude of the location + */ + public double getLatitude() { + return latitude; + } + + /** + * Get Longitude + * @return Longitude of the location + */ + public double getLongitude() { + return longitude; + } + + /** + * Get API Key + * @return API Key for OpenWeather + */ + public String getApiKey() { + return apiKey; + } + + /** + * Get URL + * @return URL for OpenWeather API + */ + public String getURL() { + if (url != null) { + return url.toString(); + } + return null; + } + + /** + * Get Weather + * if success, will raise the getresult event + */ + public void GetWeather() { + try { + HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); + con.setRequestMethod("GET"); + con.connect(); + + StringBuilder str = new StringBuilder(); + BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream())); + String line; + while ((line = br.readLine()) != null) { + str.append(line); + } + br.close(); + con.disconnect(); + + OpenWeatherData weather = gs.fromJson(str.toString(), OpenWeatherData.class); + //raise_log("Stringbuilder: " + str.toString()); + raise_log("Weather data: " + weather.toString()); + raise_getresult(true, weather); + return; + } catch (IOException e1) { + raise_log("Error opening connection: " + e1.getMessage()); + } catch(JsonSyntaxException e2) { + raise_log("Error parsing JSON: " + e2.getMessage()); + } + raise_getresult(false, null); + } + + + + + private void raise_log(String msg) { + if (need_log_event) { + ba.raiseEventFromDifferentThread(OpenWeather.this, null, 0, eventName+"_log", false, new Object[] {msg}); + } + if (javaEvent!=null) { + javaEvent.log(msg); + } + } + + private void raise_getresult(boolean success, OpenWeatherData result) { + if (need_getresult_event) { + ba.raiseEventFromDifferentThread(OpenWeather.this, null, 0, eventName+"_getresult", false, new Object[] {success, result}); + } + + if (javaEvent!=null) { + javaEvent.GetResult(success, result); + } + } +} diff --git a/src/androgpio/openweather/OpenWeatherData.java b/src/androgpio/openweather/OpenWeatherData.java new file mode 100644 index 0000000..abffdc1 --- /dev/null +++ b/src/androgpio/openweather/OpenWeatherData.java @@ -0,0 +1,39 @@ +package androgpio.openweather; + +import androgpio.openweather.jsondata.*; +import anywheresoftware.b4a.BA; +@BA.ShortName("OpenWeatherData") + +public class OpenWeatherData { + public coord coord; + public weather[] weather; + public String base; + public main main; + public int visibility; + public wind wind; + public clouds clouds; + public rain rain; + public snow snow; + public long dt; + public sys sys; + public int timezone; + public int id; + public String name; + public int cod; + + @Override + public String toString() { + return "OpenWeatherData [coord=" + coord + ", weather=" + getWeatherString() + ", base=" + base + ", main=" + main + + ", visibility=" + visibility + ", wind=" + wind + ", clouds=" + clouds + ", rain=" + rain + ", snow=" + + snow + ", dt=" + dt + ", sys=" + sys + ", timezone=" + timezone + ", id=" + id + ", name=" + name + + ", cod=" + cod + "]"; + } + + private String getWeatherString() { + String weatherString = ""; + for (weather w : weather) { + weatherString += w.toString() + ";"; + } + return weatherString; + } +} diff --git a/src/androgpio/openweather/OpenWeatherEvent.java b/src/androgpio/openweather/OpenWeatherEvent.java new file mode 100644 index 0000000..f9f5c5b --- /dev/null +++ b/src/androgpio/openweather/OpenWeatherEvent.java @@ -0,0 +1,6 @@ +package androgpio.openweather; + +public interface OpenWeatherEvent { + void log(String msg); + void GetResult(boolean success, OpenWeatherData data); +} diff --git a/src/androgpio/openweather/jsondata/clouds.java b/src/androgpio/openweather/jsondata/clouds.java new file mode 100644 index 0000000..2b4152a --- /dev/null +++ b/src/androgpio/openweather/jsondata/clouds.java @@ -0,0 +1,10 @@ +package androgpio.openweather.jsondata; + +public class clouds { + public int all; + + @Override + public String toString() { + return "clouds [all=" + all + "]"; + } +} diff --git a/src/androgpio/openweather/jsondata/coord.java b/src/androgpio/openweather/jsondata/coord.java new file mode 100644 index 0000000..2543a4e --- /dev/null +++ b/src/androgpio/openweather/jsondata/coord.java @@ -0,0 +1,11 @@ +package androgpio.openweather.jsondata; + +public class coord { + public double lat; + public double lon; + + @Override + public String toString() { + return "coord [lat=" + lat + ", lon=" + lon + "]"; + } +} diff --git a/src/androgpio/openweather/jsondata/main.java b/src/androgpio/openweather/jsondata/main.java new file mode 100644 index 0000000..dde75f0 --- /dev/null +++ b/src/androgpio/openweather/jsondata/main.java @@ -0,0 +1,19 @@ +package androgpio.openweather.jsondata; + +public class main { + public double temp; + public double feels_like; + public double temp_min; + public double temp_max; + public int pressure; + public int humidity; + public int sea_level; + public int grnd_level; + + @Override + public String toString() { + return "main [temp=" + temp + ", feels_like=" + feels_like + ", temp_min=" + temp_min + ", temp_max=" + temp_max + + ", pressure=" + pressure + ", humidity=" + humidity + ", sea_level=" + sea_level + ", grnd_level=" + + grnd_level + "]"; + } +} diff --git a/src/androgpio/openweather/jsondata/rain.java b/src/androgpio/openweather/jsondata/rain.java new file mode 100644 index 0000000..8f44181 --- /dev/null +++ b/src/androgpio/openweather/jsondata/rain.java @@ -0,0 +1,11 @@ +package androgpio.openweather.jsondata; + +public class rain { + public double _1h; + public double _3h; + + @Override + public String toString() { + return "rain [_1h=" + _1h + ", _3h=" + _3h + "]"; + } +} diff --git a/src/androgpio/openweather/jsondata/snow.java b/src/androgpio/openweather/jsondata/snow.java new file mode 100644 index 0000000..527a39a --- /dev/null +++ b/src/androgpio/openweather/jsondata/snow.java @@ -0,0 +1,11 @@ +package androgpio.openweather.jsondata; + +public class snow { + public double _1h; + public double _3h; + + @Override + public String toString() { + return "snow [_1h=" + _1h + ", _3h=" + _3h + "]"; + } +} diff --git a/src/androgpio/openweather/jsondata/sys.java b/src/androgpio/openweather/jsondata/sys.java new file mode 100644 index 0000000..aa3ed81 --- /dev/null +++ b/src/androgpio/openweather/jsondata/sys.java @@ -0,0 +1,16 @@ +package androgpio.openweather.jsondata; + +public class sys { + public int type; + public int id; + public String message; + public String country; + public long sunrise; + public long sunset; + + @Override + public String toString() { + return "sys [type=" + type + ", id=" + id + ", message=" + message + ", country=" + country + ", sunrise=" + + sunrise + ", sunset=" + sunset + "]"; + } +} diff --git a/src/androgpio/openweather/jsondata/weather.java b/src/androgpio/openweather/jsondata/weather.java new file mode 100644 index 0000000..9f8f061 --- /dev/null +++ b/src/androgpio/openweather/jsondata/weather.java @@ -0,0 +1,13 @@ +package androgpio.openweather.jsondata; + +public class weather { + public int id; + public String main; + public String description; + public String icon; + + @Override + public String toString() { + return "weather [id=" + id + ", main=" + main + ", description=" + description + ", icon=" + icon + "]"; + } +} diff --git a/src/androgpio/openweather/jsondata/wind.java b/src/androgpio/openweather/jsondata/wind.java new file mode 100644 index 0000000..9355249 --- /dev/null +++ b/src/androgpio/openweather/jsondata/wind.java @@ -0,0 +1,12 @@ +package androgpio.openweather.jsondata; + +public class wind { + public double speed; + public int deg; + public double gust; + + @Override + public String toString() { + return "wind [speed=" + speed + ", deg=" + deg + ", gust=" + gust + "]"; + } +} diff --git a/src/devices/Button.java b/src/devices/Button.java new file mode 100644 index 0000000..4f0d7eb --- /dev/null +++ b/src/devices/Button.java @@ -0,0 +1,219 @@ +package devices; + + + +import java.util.Timer; +import java.util.TimerTask; + +import com.sun.jna.Platform; + +import androgpio.DigitalInput.DigitalInput; +import androgpio.DigitalInput.DigitalInputEvent; +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.DateTime; + + + +@BA.ShortName("HardwareButton") +@BA.Events(values= { + "log(msg as string)", + "shortpressed(tick as long)", + "longpressed(tick as long)", + "currentstate(isON as boolean, tick as long)" +}) + +/** + * Button class + * use class DigitalInput + * Button is always Active Low, so Pin need to be Pull Up using Resistor to VCC + * @author rdkartono + * + */ +public class Button implements DigitalInputEvent { + private DigitalInput di; + private BA bax; + private Object myobject; + private String event; + private int mypinnumber; + + private Timer tt; // scan DigitalInput using timer + private final int timerms = 50; // timer trigger per 50 ms + private int statecounter = 0; // for detecting shortpressed or longpressed + private final int shortpresslimit = 4; // 4 x 50ms = 200ms + private final int longpresslimit = 40; // 40 x 50ms = 2000ms = 2 seconds + + + private boolean initialized = false; + + + private boolean need_log_event = false; + private boolean need_shortpressed_event = false; + private boolean need_longpressed_event = false; + private boolean need_currentstate_event = false; + + public Button() { + + } + + + public boolean Initialize(BA ba, Object caller, String buttonname, int pin_number) { + myobject = caller; + bax = ba; + + event = buttonname.trim(); + mypinnumber = pin_number; + initialized = false; + + if (bax!=null) { + if (caller!=null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_shortpressed_event = bax.subExists(event+"_shortpressed"); + need_longpressed_event = bax.subExists(event+"_longpressed"); + need_currentstate_event = bax.subExists(event+"_currentstate"); + } + } + } + + + if (Platform.isAndroid()==false) { + raise_log("Target device is not Android"); + return false; + } + + di = new DigitalInput(); + di.SetJavaEvent(this); // link javaevent to this class + + di.Initialize(bax, caller, event, pin_number); + + if (di.getIsInitialized()) { + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Release(); + } + }); + + TimerTask task = new TimerTask() { + + @Override + public void run() { + if (di == null) { + cancel(); // stop timer + raise_log("DigitalInput is null, Button Timer stopped"); + return; + } + if (di.getIsInitialized()==false) { + cancel(); // stop timer + raise_log("DigitalInput is not initialized, Button Timer stopped"); + return; + } + int value = di.ReadState(); + + if (value==-1) { + cancel(); // stop timer + raise_log("DigitalInput ReadState failed, Button Timer stopped"); + return; + } + + + if (value==1) { + // high + if (statecounter>=shortpresslimit) { + if (statecounter0) return true; + + } + } + } + } + + return false; + } + + /** + * Read from I2C. Result in int, but actually value will 0 - 255 (byte size) + * @return -1 if failed + */ + public int Read() { + if (i2cbus instanceof I2C_BUS) { + if (i2cbus.IsOpened()) { + if (i2cdev instanceof I2C_Device) { + if (i2cdev.IsOpened()) { + byte[] result = i2cdev.ReadBytes(1); + if (result!=null) { + return Bit.And(result[0], 0xFF); + } + + } + } + } + } + return -1; + } + + private void raise_log(String msg) { + if (need_log_event) { + bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg}); + } + } + + @Override + public void Log(String msg) { + raise_log(msg); + + } +} diff --git a/src/devices/PublicIPChecker.java b/src/devices/PublicIPChecker.java new file mode 100644 index 0000000..2b68ac3 --- /dev/null +++ b/src/devices/PublicIPChecker.java @@ -0,0 +1,115 @@ +package devices; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Regex; + +@BA.ShortName("PublicIPChecker") +@BA.Events(values= { + "log(msg as string)", + "publicip(success as boolean, testserver as string, result as string)" +}) + + +// https://stackoverflow.com/questions/2939218/getting-the-external-ip-address-in-java + +public class PublicIPChecker { + + private final String[] servers = { + "http://checkip.amazonaws.com/", + "https://ipv4.icanhazip.com/", + "http://myexternalip.com/raw", + "http://ipecho.net/plain", + "http://bot.whatismyipaddress.com" + }; + + private final String regex_validip = "^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$"; + + private BA bax; + private Object caller; + private String event=""; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_publicip_event = false; + + /** + * Initialize Public IP Checker + * @param callerobject : callback object + * @param eventname : eventname + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname; + if (bax instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_publicip_event = bax.subExists(event+"_publicip"); + } + } + } + } + + /** + * Get Public IP + * Will raise event publicip(success as boolean, testserver as string, result as string) + */ + public void GetPublicIP() { + Thread tx = new Thread(new Runnable() { + + @Override + public void run() { + boolean success = false; + String currentserver = ""; + String resultip = ""; + for(int ii=0;ii