x : Frame.frameDeEscaping(t)) {
79 | byte[] xx = new byte[x.size()];
80 | for (int i = 0; i < x.size(); i++)
81 | xx[i] = x.get(i);
82 | boolean rel = false;
83 | if (x.size()>1 && (x.get(1) & 0x20) == 0x20) {
84 | rel = true;
85 |
86 | byte seq = 0x00;
87 | if ((x.get(1) & 0x80) == 0x80)
88 | seq = (byte) 0x80;
89 |
90 | handler.sendImidiateAcknowledge(seq);
91 | } else {
92 | rel = false;
93 | }
94 | handleRX(xx, x.size(), rel,handler);
95 | }
96 | }
97 |
98 | public static enum Response{
99 | ID,
100 | SYNC,
101 | RELIABLE_DATA,
102 | UNRELIABLE_DATA
103 |
104 | }
105 | private static void handleRX(byte[] inBuf, int length, boolean reliableFlagged, PacketHandler handler) {
106 |
107 | ByteBuffer buffer = ByteBuffer.wrap(inBuf, 0, length);
108 | buffer.order(ByteOrder.LITTLE_ENDIAN);
109 |
110 | byte[] nonce, payload, umac, packetNoUmac;
111 |
112 | Byte command;
113 | buffer.get(); //ignore
114 | command = buffer.get();
115 |
116 | short payloadlength = buffer.getShort();
117 |
118 | buffer.get(); //ignore
119 |
120 | nonce = new byte[13];
121 | buffer.get(nonce, 0, nonce.length);
122 |
123 | payload = new byte[payloadlength];
124 | buffer.get(payload, 0, payload.length);
125 |
126 | umac = new byte[8];
127 | buffer.get(umac, 0, umac.length);
128 |
129 | packetNoUmac = new byte[buffer.capacity() - umac.length];
130 | buffer.rewind();
131 | for (int i = 0; i < packetNoUmac.length; i++)
132 | packetNoUmac[i] = buffer.get();
133 |
134 | buffer.rewind();
135 |
136 | byte c = (byte)(command & 0x1F);
137 | switch (c) {
138 | case 20:
139 | handler.log("got an id response");
140 | if (Utils.ccmVerify(packetNoUmac, handler.getToDeviceKey(), umac, nonce)) {
141 | handler.handleResponse(Response.ID,reliableFlagged,payload);
142 | }
143 | break;
144 | case 24:
145 | handler.log("got a sync response ");
146 | if (Utils.ccmVerify(packetNoUmac, handler.getToDeviceKey(), umac, nonce)) {
147 | handler.handleResponse(Response.SYNC,reliableFlagged,payload);
148 | }
149 | break;
150 |
151 | case 0x23:
152 | if (Utils.ccmVerify(packetNoUmac, handler.getToDeviceKey(), umac, nonce)) {
153 | handler.handleResponse(Response.RELIABLE_DATA,reliableFlagged,payload);
154 | }
155 | break;
156 | case 0x03:
157 | if (Utils.ccmVerify(packetNoUmac, handler.getToDeviceKey(), umac, nonce)) {
158 | handler.handleResponse(Response.UNRELIABLE_DATA,reliableFlagged,payload);
159 | }
160 | break;
161 | case 0x05:
162 | //ignore ack response
163 | break;
164 |
165 | case 0x06:
166 | if(Utils.ccmVerify(packetNoUmac, handler.getToDeviceKey(), umac, nonce))
167 | {
168 | byte error = 0;
169 | String err = "";
170 |
171 | if(payload.length > 0)
172 | error = payload[0];
173 |
174 | switch(error)
175 | {
176 | case 0x00:
177 | err = "Undefined";
178 | break;
179 | case 0x0F:
180 | err = "Wrong state";
181 | break;
182 | case 0x33:
183 | err = "Invalid service primitive";
184 | break;
185 | case 0x3C:
186 | err = "Invalid payload length";
187 | break;
188 | case 0x55:
189 | err = "Invalid source address";
190 | break;
191 | case 0x66:
192 | err = "Invalid destination address";
193 | break;
194 | }
195 |
196 | handler.log( "Error in Transport Layer! ("+err+")");
197 | handler.handleErrorResponse(error,err,reliableFlagged,payload);
198 |
199 | }
200 | break;
201 | default:
202 | handler.log("not yet implemented rx command: " + command + " ( " + String.format("%02X", command));
203 |
204 | }
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/app/src/main/java/org/monkey/d/ruffy/ruffy/driver/Twofish_Properties.java:
--------------------------------------------------------------------------------
1 | // $Id: $
2 | //
3 | // $Log: $
4 | // Revision 1.0 1998/03/24 raif
5 | // + start of history.
6 | //
7 | // $Endlog$
8 | /*
9 | * Copyright (c) 1997, 1998 Systemics Ltd on behalf of
10 | * the Cryptix Development Team. All rights reserved.
11 | */
12 | package org.monkey.d.ruffy.ruffy.driver;
13 |
14 | import java.io.InputStream;
15 | import java.io.PrintStream;
16 | import java.io.PrintWriter;
17 | import java.util.Enumeration;
18 | import java.util.Properties;
19 |
20 | /**
21 | * This class acts as a central repository for an algorithm specific
22 | * properties. It reads an (algorithm).properties file containing algorithm-
23 | * specific properties. When using the AES-Kit, this (algorithm).properties
24 | * file is located in the (algorithm).jar file produced by the "jarit" batch/
25 | * script command.
26 | *
27 | * Copyright © 1997, 1998
28 | * Systemics Ltd on behalf of the
29 | * Cryptix Development Team.
30 | *
All rights reserved.
31 | *
32 | * $Revision: $
33 | * @author David Hopwood
34 | * @author Jill Baker
35 | * @author Raif S. Naffah
36 | */
37 | public class Twofish_Properties // implicit no-argument constructor
38 | {
39 | // Constants and variables with relevant static code
40 | //...........................................................................
41 |
42 | static final boolean GLOBAL_DEBUG = false;
43 |
44 | static final String ALGORITHM = "Twofish";
45 | static final double VERSION = 0.2;
46 | static final String FULL_NAME = ALGORITHM + " ver. " + VERSION;
47 | static final String NAME = "Twofish_Properties";
48 |
49 | static final Properties properties = new Properties();
50 |
51 | /** Default properties in case .properties file was not found. */
52 | private static final String[][] DEFAULT_PROPERTIES = {
53 | {"Trace.Twofish_Algorithm", "true"},
54 | {"Debug.Level.*", "1"},
55 | {"Debug.Level.Twofish_Algorithm", "9"},
56 | };
57 |
58 | static {
59 | if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Looking for " + ALGORITHM + " properties");
60 | String it = ALGORITHM + ".properties";
61 | InputStream is = Twofish_Properties.class.getResourceAsStream(it);
62 | boolean ok = is != null;
63 | if (ok)
64 | try {
65 | properties.load(is);
66 | is.close();
67 | if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Properties file loaded OK...");
68 | } catch (Exception x) {
69 | ok = false;
70 | }
71 | if (!ok) {
72 | if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": WARNING: Unable to load \"" + it + "\" from CLASSPATH.");
73 | if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Will use default values instead...");
74 | int n = DEFAULT_PROPERTIES.length;
75 | for (int i = 0; i < n; i++)
76 | properties.put(
77 | DEFAULT_PROPERTIES[i][0], DEFAULT_PROPERTIES[i][1]);
78 | if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Default properties now set...");
79 | }
80 | }
81 |
82 |
83 | // Properties methods (excluding load and save, which are deliberately not
84 | // supported).
85 | //...........................................................................
86 |
87 | /** Get the value of a property for this algorithm. */
88 | public static String getProperty (String key) {
89 | return properties.getProperty(key);
90 | }
91 |
92 | /**
93 | * Get the value of a property for this algorithm, or return
94 | * value if the property was not set.
95 | */
96 | public static String getProperty (String key, String value) {
97 | return properties.getProperty(key, value);
98 | }
99 |
100 | /** List algorithm properties to the PrintStream out. */
101 | public static void list (PrintStream out) {
102 | list(new PrintWriter(out, true));
103 | }
104 |
105 | /** List algorithm properties to the PrintWriter out. */
106 | public static void list (PrintWriter out) {
107 | out.println("#");
108 | out.println("# ----- Begin "+ALGORITHM+" properties -----");
109 | out.println("#");
110 |
111 | String key, value;
112 | Enumeration enumer = properties.propertyNames();
113 | while (enumer.hasMoreElements()) {
114 | key = (String) enumer.nextElement();
115 | value = getProperty(key);
116 | out.println(key + " = " + value);
117 | }
118 |
119 | out.println("#");
120 | out.println("# ----- End "+ALGORITHM+" properties -----");
121 | }
122 |
123 | // public synchronized void load(InputStream in) throws IOException {}
124 |
125 | public static Enumeration propertyNames() {
126 | return properties.propertyNames();
127 | }
128 |
129 | // public void save (OutputStream os, String comment) {}
130 |
131 |
132 | // Developer support: Tracing and debugging enquiry methods (package-private)
133 | //...........................................................................
134 |
135 | /**
136 | * Return true if tracing is requested for a given class.
137 | *
138 | * User indicates this by setting the tracing boolean
139 | * property for label in the (algorithm).properties
140 | * file. The property's key is "Trace.label".
141 | *
142 | * @param label The name of a class.
143 | * @return True iff a boolean true value is set for a property with
144 | * the key Trace.label.
145 | */
146 | static boolean isTraceable (String label) {
147 | String s = getProperty("Trace." + label);
148 | if (s == null)
149 | return false;
150 | return new Boolean(s).booleanValue();
151 | }
152 |
153 | /**
154 | * Return the debug level for a given class.
155 | *
156 | * User indicates this by setting the numeric property with key
157 | * "Debug.Level.label".
158 | *
159 | * If this property is not set, "Debug.Level.*" is looked up
160 | * next. If neither property is set, or if the first property found is
161 | * not a valid decimal integer, then this method returns 0.
162 | *
163 | * @param label The name of a class.
164 | * @return The required debugging level for the designated class.
165 | */
166 | static int getLevel (String label) {
167 | String s = getProperty("Debug.Level." + label);
168 | if (s == null) {
169 | s = getProperty("Debug.Level.*");
170 | if (s == null)
171 | return 0;
172 | }
173 | try {
174 | return Integer.parseInt(s);
175 | } catch (NumberFormatException e) {
176 | return 0;
177 | }
178 | }
179 |
180 | /**
181 | * Return the PrintWriter to which tracing and debugging output is to
182 | * be sent.
183 | *
184 | * User indicates this by setting the property with key Output
185 | * to the literal out or err.
186 | *
187 | * By default or if the set value is not allowed, System.err
188 | * will be used.
189 | */
190 | static PrintWriter getOutput() {
191 | PrintWriter pw;
192 | String name = getProperty("Output");
193 | if (name != null && name.equals("out"))
194 | pw = new PrintWriter(System.out, true);
195 | else
196 | pw = new PrintWriter(System.err, true);
197 | return pw;
198 | }
199 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/monkey/d/ruffy/ruffy/driver/Utils.java:
--------------------------------------------------------------------------------
1 | package org.monkey.d.ruffy.ruffy.driver;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.nio.ByteOrder;
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * Created by fishermen21 on 16.05.17.
10 | */
11 |
12 | public class Utils {
13 |
14 | public static byte[] generateKey(String strKey)
15 | {
16 | byte[] pin = new byte[10];
17 |
18 | for(int i=0;i ccmAuthenticate(List buffer, Object key, byte[] nonceIn)
120 | {
121 | List output = new ArrayList();
122 | int origLength = buffer.size(); //Hang on to the original length
123 |
124 | byte[] packet = new byte[buffer.size()]; //Create primitive array
125 | for(int i=0;i
140 | output.add(packetBuffer.array()[i]);
141 |
142 | return output;
143 | }
144 |
145 | public static void addCRC(List out)
146 | {
147 | short crc = -1;
148 | Object[] objArr = new Object[1];
149 | objArr[0] = Short.valueOf((short)-1);
150 | for (int i = 0; i < out.size(); i += 1) {
151 | crc = Utils.updateCrc(crc, ((Byte) out.get(i)).byteValue());
152 | objArr = new Object[1];
153 | objArr[0] = Short.valueOf(crc);
154 | }
155 | out.add(Byte.valueOf((byte) (crc & 255)));
156 | out.add(Byte.valueOf((byte) (crc >> 8)));
157 | for (int i = 0; i < 8; i += 1) {
158 | out.add(Byte.valueOf((byte) 0));
159 | }
160 | }
161 | private static short updateCrc(short crc, byte input) {
162 | Object[] objArr = new Object[1];
163 | objArr[0] = Byte.valueOf(input);
164 | short crcTemp = (short) (((short) input) ^ crc);
165 | crc = (short) (((short) (crc >> 8)) & 255);
166 | objArr = new Object[1];
167 | objArr[0] = Short.valueOf(crc);
168 | objArr = new Object[1];
169 | objArr[0] = Short.valueOf(crcTemp);
170 | if ((crcTemp & 128) > 0) {
171 | crc = (short) (crc ^ -31736);
172 | }
173 | objArr = new Object[1];
174 | objArr[0] = Short.valueOf(crc);
175 | if ((crcTemp & 64) > 0) {
176 | crc = (short) (crc ^ 16900);
177 | }
178 | objArr = new Object[1];
179 | objArr[0] = Short.valueOf(crc);
180 | if ((crcTemp & 32) > 0) {
181 | crc = (short) (crc ^ 8450);
182 | }
183 | objArr = new Object[1];
184 | objArr[0] = Short.valueOf(crc);
185 | if ((crcTemp & 16) > 0) {
186 | crc = (short) (crc ^ 4225);
187 | }
188 | objArr = new Object[1];
189 | objArr[0] = Short.valueOf(crc);
190 | if ((crcTemp & 8) > 0) {
191 | crc = (short) (crc ^ -29624);
192 | }
193 | objArr = new Object[1];
194 | objArr[0] = Short.valueOf(crc);
195 | if ((crcTemp & 4) > 0) {
196 | crc = (short) (crc ^ 17956);
197 | }
198 | objArr = new Object[1];
199 | objArr[0] = Short.valueOf(crc);
200 | if ((crcTemp & 2) > 0) {
201 | crc = (short) (crc ^ 8978);
202 | }
203 | objArr = new Object[1];
204 | objArr[0] = Short.valueOf(crc);
205 | if ((crcTemp & 1) > 0) {
206 | crc = (short) (crc ^ 4489);
207 | }
208 | objArr = new Object[1];
209 | objArr[0] = Short.valueOf(crc);
210 | return crc;
211 | }
212 |
213 | public static int incrementArray(byte[] array) {
214 | int i = 0, carry = 0;
215 |
216 | array[i]++;
217 | if (array[i] == 0)
218 | carry = 1;
219 |
220 | for (i = 1; i < array.length; i++) {
221 | if (carry == 1) {
222 | array[i] += carry;
223 | if (array[i] > 0) {
224 | carry = 0;
225 | return carry;
226 | } else
227 | carry = 1;
228 | }
229 | }
230 |
231 | return carry;
232 | }
233 |
234 | public static String byteArrayToHexString(byte[] buffer, int bytes) {
235 | StringBuilder sb = new StringBuilder();
236 | for (int i = 0; i < bytes; i++) {
237 | sb.append(String.format("%02X ", buffer[i]));
238 | }
239 | return sb.toString();
240 | }
241 | public static byte[] hexStringToByteArray(String s) {
242 | s = s.replaceAll(" ","");
243 | int len = s.length();
244 | byte[] data = new byte[len / 2];
245 | for (int i = 0; i < len; i += 2) {
246 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
247 | + Character.digit(s.charAt(i+1), 16));
248 | }
249 | return data;
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/app/src/main/java/org/monkey/d/ruffy/ruffy/driver/BTConnection.java:
--------------------------------------------------------------------------------
1 | package org.monkey.d.ruffy.ruffy.driver;
2 |
3 | import android.app.Activity;
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.bluetooth.BluetoothDevice;
6 | import android.bluetooth.BluetoothServerSocket;
7 | import android.bluetooth.BluetoothSocket;
8 | import android.content.Intent;
9 | import android.content.IntentFilter;
10 | import android.util.Log;
11 |
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 | import java.io.OutputStream;
15 | import java.util.LinkedList;
16 | import java.util.List;
17 | import java.util.UUID;
18 | import java.util.concurrent.Executors;
19 | import java.util.concurrent.ScheduledExecutorService;
20 |
21 | /**
22 | * Created by SandraK82 on 15.05.17.
23 | */
24 |
25 | public class BTConnection {
26 | private final BTHandler handler;
27 | private BluetoothAdapter bluetoothAdapter;
28 | private ListenThread listen;
29 |
30 | private BluetoothSocket currentConnection;
31 |
32 | public int seqNo;
33 | private InputStream currentInput;
34 | private OutputStream currentOutput;
35 | private PairingRequest pairingReciever;
36 | private ConnectReceiver connectReceiver;
37 |
38 | private PumpData pumpData;
39 |
40 | public BTConnection(final BTHandler handler)
41 | {
42 | this.handler = handler;
43 | bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
44 | if (!bluetoothAdapter.isEnabled()) {
45 | handler.requestBlueTooth();
46 | }
47 | }
48 |
49 | public void makeDiscoverable(Activity activity) {
50 |
51 | this.pumpData = new PumpData(activity);
52 |
53 | IntentFilter filter = new IntentFilter("android.bluetooth.device.action.PAIRING_REQUEST");
54 | pairingReciever = new PairingRequest(activity, handler);
55 | activity.registerReceiver(pairingReciever, filter);
56 |
57 | Intent discoverableIntent = new Intent("android.bluetooth.adapter.action.REQUEST_DISCOVERABLE");
58 | discoverableIntent.putExtra("android.bluetooth.adapter.extra.DISCOVERABLE_DURATION", 60);
59 | discoverableIntent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
60 | activity.startActivity(discoverableIntent);
61 |
62 | BluetoothServerSocket srvSock = null;
63 | try {
64 | srvSock = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("SerialLink", UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"));
65 | } catch (IOException e) {
66 | handler.fail("socket listen() failed");
67 | return;
68 | }
69 |
70 | final BluetoothServerSocket lSock = srvSock;
71 | listen = new ListenThread(srvSock);
72 |
73 | filter = new IntentFilter("android.bluetooth.device.action.ACL_CONNECTED");
74 | connectReceiver = new ConnectReceiver(handler);
75 | activity.registerReceiver(connectReceiver, filter);
76 |
77 | ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 1 );
78 | scheduler.execute(listen);
79 | }
80 |
81 | public void stopDiscoverable() {
82 | if(listen!=null)
83 | {
84 | listen.halt();
85 | }
86 | if(bluetoothAdapter.isDiscovering())
87 | {
88 | bluetoothAdapter.cancelDiscovery();
89 | }
90 | }
91 |
92 | public void connect(BluetoothDevice device) {
93 | connect(device.getAddress(), 4);
94 | }
95 |
96 | public void connect(PumpData pumpData, int retries)
97 | {
98 | this.pumpData = pumpData;
99 | connect(pumpData.getPumpMac(),retries);
100 | }
101 |
102 | private int state = 0;
103 |
104 | private void connect(String deviceAddress, int retry) {
105 |
106 | if(state!=0)
107 | {
108 | handler.log("in connect!");
109 | return;
110 | }
111 | state=1;
112 | BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
113 |
114 | BluetoothSocket tmp = null;
115 | try {
116 | try {
117 | Thread.sleep(500);
118 | } catch (InterruptedException e) {
119 | e.printStackTrace();
120 | }
121 |
122 | tmp = device.createInsecureRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"));
123 | } catch (IOException e) {
124 | handler.fail("socket create() failed: "+e.getMessage());
125 | }
126 | if(tmp != null) {
127 | stopDiscoverable();
128 | activateConnection(tmp);
129 | }
130 | else
131 | {
132 | handler.fail("failed the pump connection( retries left: "+retry+")");
133 | try {
134 | Thread.sleep(100);
135 | } catch (InterruptedException e) {
136 | e.printStackTrace();
137 | }
138 | if(retry>0)
139 | {
140 | connect(deviceAddress,retry-1);
141 | }
142 | else
143 | {
144 | handler.fail("Failed to connect");
145 | }
146 | }
147 | }
148 |
149 | private void startReadThread() {
150 | new Thread() {
151 | @Override
152 | public void run() {
153 | try {
154 | currentConnection.connect();//This method will block until a connection is made or the connection fails. If this method returns without an exception then this socket is now connected.
155 | currentInput = currentConnection.getInputStream();
156 | currentOutput = currentConnection.getOutputStream();
157 | } catch (IOException e) {
158 | //e.printStackTrace();
159 | handler.fail("no connection possible: " + e.getMessage());
160 |
161 |
162 | //??????????
163 | //state=1;
164 | //return;
165 | }
166 | try {
167 | pumpData.getActivity().unregisterReceiver(connectReceiver);
168 | }catch(Exception e){/*ignore*/}
169 | try {
170 | pumpData.getActivity().unregisterReceiver(pairingReciever);
171 | }catch(Exception e){/*ignore*/}
172 | state=0;
173 |
174 | //here check if really connected!
175 | //this will start thread to write
176 | handler.deviceConnected();//in ruffy.java
177 |
178 |
179 | try {
180 | Thread.sleep(100);
181 | } catch (InterruptedException e) {
182 | e.printStackTrace();
183 | }
184 |
185 | byte[] buffer = new byte[512];
186 | while (true) {
187 | try {
188 | int bytes = currentInput.read(buffer);
189 | handler.log("read "+bytes+": "+ Utils.byteArrayToHexString(buffer,bytes));
190 | handler.handleRawData(buffer,bytes);
191 | } catch (Exception e) {
192 | //e.printStackTrace();
193 | //do not fail here as we maybe just closed the socket..
194 | handler.log("got error in read");
195 | return;
196 | }
197 | }
198 | }
199 | }.start();
200 | }
201 |
202 | public void writeCommand(byte[] key) {
203 | List out = new LinkedList();
204 | for(Byte b : key)
205 | out.add(b);
206 | for (Byte n : pumpData.getNonceTx())
207 | out.add(n);
208 | Utils.addCRC(out);
209 |
210 | List temp = Frame.frameEscape(out);
211 |
212 | byte[] ro = new byte[temp.size()];
213 | int i = 0;
214 | for(byte b : temp)
215 | ro[i++]=b;
216 |
217 | StringBuilder sb = new StringBuilder();
218 | for (i = 0; i < key.length; i++) {
219 | sb.append(String.format("%02X ", key[i]));
220 | }
221 | handler.log("writing command: "+sb.toString());
222 | write(ro);
223 | }
224 |
225 | private void activateConnection(BluetoothSocket newConnection){
226 | if(this.currentConnection!=null)
227 | {
228 | try {
229 | this.currentOutput.close();
230 | } catch (Exception e) {/*ignore*/}
231 | try {
232 | this.currentInput.close();
233 | } catch (Exception e) {/*ignore*/}
234 | try {
235 | this.currentConnection.close();
236 | } catch (Exception e) {/*ignore*/}
237 | this.currentInput=null;
238 | this.currentOutput=null;
239 | this.currentConnection=null;
240 | handler.fail("closed current Connection");
241 | }
242 | handler.fail("got new Connection: "+newConnection);
243 | this.currentConnection = newConnection;
244 | if(newConnection!=null)
245 | {
246 | startReadThread();
247 | }
248 | }
249 |
250 | public void write(byte[] ro){
251 |
252 | handler.log("!!!write!!!");
253 |
254 | if(this.currentConnection==null)
255 | {
256 | handler.fail("unable to write: no socket");
257 | return;
258 | }
259 | try {
260 | currentOutput.write(ro);
261 | handler.log("wrote "+ro.length+" bytes: "+ Utils.byteArrayToHexString(ro,ro.length));
262 | }catch(Exception e)
263 | {
264 | //e.printStackTrace();
265 | handler.fail("failed write of "+ro.length+" bytes!");
266 | }
267 | }
268 |
269 | public void log(String s) {
270 | if(handler!=null)
271 | handler.log(s);
272 | }
273 |
274 | public void disconnect() {
275 | try {
276 | this.currentOutput.close();
277 | } catch (Exception e) {/*ignore*/}
278 | try {
279 | this.currentInput.close();
280 | } catch (Exception e) {/*ignore*/}
281 | try {
282 | this.currentConnection.close();
283 | } catch (Exception e) {/*ignore*/}
284 | this.currentInput=null;
285 | this.currentOutput=null;
286 | this.currentConnection=null;
287 | this.pumpData = null;
288 |
289 | handler.log("disconnect() closed current Connection");
290 | }
291 |
292 | public PumpData getPumpData() {
293 | return pumpData;
294 | }
295 |
296 | public boolean isConnected() {
297 | return this.currentConnection != null && this.currentConnection.isConnected();
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/app/src/main/java/org/monkey/d/ruffy/ruffy/driver/Application.java:
--------------------------------------------------------------------------------
1 | package org.monkey.d.ruffy.ruffy.driver;
2 |
3 | import android.util.Log;
4 |
5 | import java.nio.ByteBuffer;
6 | import java.nio.ByteOrder;
7 | import java.util.List;
8 |
9 | /**
10 | * Some statics to do some things on application level
11 | */
12 | public class Application {
13 | /**
14 | * how often we accept an mode error before accepting we are in the wrong mode
15 | */
16 | public static int MODE_ERROR_TRESHHOLD = 3;
17 |
18 | public static enum Command
19 | {
20 | COMMANDS_SERVICES_VERSION,
21 | REMOTE_TERMINAL_VERSION,
22 | BINDING,
23 | COMMAND_MODE,
24 | COMMAND_DEACTIVATE,
25 | RT_MODE,
26 | RT_DEACTIVATE,
27 | DEACTIVATE_ALL,
28 | APP_DISCONNECT,
29 | CMD_PING,
30 | }
31 |
32 | public static void sendAppConnect(BTConnection btConn) {
33 | ByteBuffer payload = null;
34 | byte[] connect_app_layer = {16, 0, 85, -112};
35 |
36 | payload = ByteBuffer.allocate(8); //4 bytes for application header, 4 for payload
37 | payload.put(connect_app_layer); //Add prefix array
38 | payload.order(ByteOrder.LITTLE_ENDIAN);
39 | payload.putInt(Integer.parseInt("12345"));
40 |
41 | Application.sendData(payload,true,btConn);
42 | }
43 |
44 | private static void sendData(ByteBuffer payload, boolean reliable,BTConnection btConn) {
45 | btConn.getPumpData().incrementNonceTx();
46 |
47 | byte[] sendR = {16,3,0,0,0};
48 |
49 | List packet = Packet.buildPacket(sendR, payload, true,btConn); //Add the payload, set the address if valid
50 |
51 | if(reliable) {
52 | int seq = btConn.seqNo;
53 | packet.set(1, setSeqRel(packet.get(1), true,btConn)); //Set the sequence and reliable bits
54 | }
55 | Packet.adjustLength(packet, payload.capacity()); //Set the payload length
56 |
57 | packet = Utils.ccmAuthenticate(packet, btConn.getPumpData().getToPumpKey(), btConn.getPumpData().getNonceTx()); //Authenticate packet
58 |
59 |
60 | List temp = Frame.frameEscape(packet);
61 | byte[] ro = new byte[temp.size()];
62 | int i = 0;
63 | for(byte b : temp)
64 | ro[i++]=b;
65 |
66 | btConn.write(ro);
67 | }
68 |
69 | private static Byte setSeqRel(Byte b, boolean rel,BTConnection btConn)
70 | {
71 | b = (byte) (b | btConn.seqNo); //Set the sequence bit
72 |
73 | if(rel)
74 | b = (byte) (b |0x20); //Set the reliable bit
75 |
76 | btConn.seqNo ^= 0x80;
77 |
78 | return b;
79 | }
80 | private static byte[] service_activate = {16, 0, 0x66, (byte)0x90};
81 | private static byte[] service_deactivate = {16, 0, 0x69, (byte)0x90};
82 |
83 | public static void sendAppCommand(Command command, BTConnection btConn){
84 | ByteBuffer payload = null;
85 |
86 | String s = "";
87 |
88 | boolean reliable = true;
89 |
90 | switch(command)
91 | {
92 | case COMMAND_MODE:
93 | s = "COMMAND_ACTIVATE";
94 | payload = ByteBuffer.allocate(7);
95 | payload.put(service_activate);
96 | payload.put((byte)0xB7);
97 | payload.put((byte)0x01);
98 | payload.put((byte)0x00);
99 | reliable = true;
100 | break;
101 |
102 | case COMMAND_DEACTIVATE:
103 | s = "COMMAND DEACTIVATE";
104 | payload = ByteBuffer.allocate(5);
105 | payload.put(service_deactivate);
106 | payload.put((byte)0xB7);
107 | reliable = true;
108 | break;
109 | case RT_MODE:
110 | s = "RT_ACTIVATE";
111 | payload = ByteBuffer.allocate(7);
112 | payload.put(service_activate);
113 | payload.put((byte)0x48);
114 | payload.put((byte)0x01);
115 | payload.put((byte)0x00);
116 | reliable = true;
117 | break;
118 | case RT_DEACTIVATE:
119 | s = "RT DEACTIVATE";
120 | payload = ByteBuffer.allocate(5);
121 | payload.put(service_deactivate);
122 | payload.put((byte)0x48);
123 | reliable = true;
124 | break;
125 | case COMMANDS_SERVICES_VERSION:
126 | s = "COMMAND_SERVICES_VERSION";
127 | payload = ByteBuffer.allocate(5);
128 | payload.put((byte)16);
129 | payload.put((byte)0);
130 | payload.put((byte) (((short)0x9065) & 0xFF));
131 | payload.put((byte) ((((short)0x9065)>>8) & 0xFF));
132 | payload.put((byte)0xb7);
133 | reliable = true;
134 | break;
135 | case REMOTE_TERMINAL_VERSION:
136 | s = "REMOTE_TERMINAL_VERSION";
137 | payload = ByteBuffer.allocate(5);
138 | payload.put((byte)16);
139 | payload.put((byte)0);
140 | payload.put((byte) (((short)0x9065) & 0xFF));
141 | payload.put((byte) ((((short)0x9065)>>8) & 0xFF));
142 | payload.put((byte)0x48);
143 | reliable = true;
144 | break;
145 | case BINDING:
146 | s = "BINDING";
147 | payload = ByteBuffer.allocate(5);
148 | payload.put((byte)16);
149 | payload.put((byte)0);
150 | payload.put((byte) (((short)0x9095) & 0xFF));
151 | payload.put((byte) ((((short)0x9095)>>8) & 0xFF));
152 | payload.put((byte) 0x48); //Binding OK
153 | reliable = true;
154 | break;
155 |
156 | case APP_DISCONNECT:
157 | byte[] connect_app_layer = {16 , 0, 0x5a, 0x00};
158 | payload = ByteBuffer.allocate(6); //4 bytes for application header, 4 for payload
159 | payload.put(connect_app_layer); //Add prefix array
160 | payload.order(ByteOrder.LITTLE_ENDIAN);
161 | payload.putShort((byte)0x6003);
162 | reliable = true;
163 | break;
164 |
165 | case CMD_PING:
166 | payload.put((byte)16);
167 | payload.put((byte)0xB7);
168 | payload.put((byte) (0x9AAA & 0xFF));
169 | payload.put((byte) ((0x9AAA>>8) & 0xFF));
170 | reliable = false;
171 | case DEACTIVATE_ALL:
172 | Log.d("ApplicatioN","Send deactivate");
173 | payload = ByteBuffer.allocate(4);
174 | payload.put((byte)16);
175 | payload.put((byte)0);
176 | payload.put((byte)(0x6A));
177 | payload.put((byte)(0x90));
178 | reliable = true;
179 | break;
180 |
181 | default:
182 | s = "uknown subcommand: "+command;
183 | break;
184 | }
185 |
186 | if(payload != null)
187 | {
188 | sendData(payload,reliable,btConn);
189 | }
190 | }
191 |
192 | public static void sendAppDisconnect(BTConnection btConn) {
193 | sendAppCommand(Command.APP_DISCONNECT,btConn);
194 | }
195 |
196 | public static void cmdPing(BTConnection btConn)
197 | {
198 | sendAppCommand(Command.CMD_PING,btConn);
199 | }
200 |
201 | public static short sendRTKeepAlive(short rtSeq, BTConnection btConn) {
202 | ByteBuffer payload = ByteBuffer.allocate(6);
203 | payload.put((byte)16);
204 | payload.put((byte)0x48);
205 | payload.put((byte) (0x0566 & 0xFF));
206 | payload.put((byte) ((0x0566>>8) & 0xFF));
207 |
208 | payload.put((byte) (rtSeq & 0xFF));
209 | payload.put((byte) ((rtSeq>>8) & 0xFF));
210 |
211 | sendData(payload,false,btConn);
212 |
213 | btConn.log("/////////////////////////////////////////////////////////////////////");
214 | btConn.log("send alive with seq: "+rtSeq);
215 | btConn.log("/////////////////////////////////////////////////////////////////////");
216 | rtSeq++;
217 | return rtSeq;
218 |
219 | }
220 |
221 | public static void cmdErrStatus(BTConnection btConn)
222 | {
223 | ByteBuffer payload = ByteBuffer.allocate(4);
224 | payload.put((byte)16);
225 |
226 | payload.put((byte)0x48);
227 |
228 | payload.put((byte) (0x9AA5 & 0xFF));
229 | payload.put((byte) ((0x9AA5>>8) & 0xFF));
230 |
231 | sendData(payload, true, btConn);
232 | }
233 |
234 | public static short rtSendKey(byte key, boolean changed,short rtSeq, BTConnection btConn)
235 | {
236 | ByteBuffer payload = ByteBuffer.allocate(8);
237 |
238 | payload.put((byte)16);
239 | payload.put((byte)0x48);
240 | payload.put((byte) 0x65);
241 | payload.put((byte) 0x05);
242 |
243 | payload.put((byte) (rtSeq & 0xFF));
244 | payload.put((byte) ((rtSeq>>8) & 0xFF));
245 |
246 | payload.put(key);
247 |
248 | if(changed)
249 | payload.put((byte) 0xB7);
250 | else
251 | payload.put((byte) 0x48);
252 |
253 | btConn.log("/////////////////////////////////////////////////////////////////////");
254 | String k = "";
255 | switch (key)
256 | {
257 | case 0x00:
258 | k="NOKEY";
259 | break;
260 | case 0x03:
261 | k="MENU";
262 | break;
263 | case 0x0C:
264 | k="CHECK";
265 | break;
266 | case 0x30:
267 | k="UP";
268 | break;
269 | case (byte)0xC0:
270 | k="DOWN";
271 | break;
272 | }
273 | btConn.log("send key "+k+" with seq: "+rtSeq);
274 | btConn.log("/////////////////////////////////////////////////////////////////////");
275 | sendData(payload, false, btConn);
276 |
277 | rtSeq++;
278 | return rtSeq;
279 | }
280 |
281 | public static void processAppResponse(byte[] payload, boolean reliable, AppHandler handler) {
282 | handler.log("processing app response");
283 | ByteBuffer b = ByteBuffer.wrap(payload);
284 | b.order(ByteOrder.LITTLE_ENDIAN);
285 |
286 | b.get();//ignore
287 | byte servId = b.get();
288 | short commId = b.getShort();
289 |
290 | handler.log("Service ID: " + String.format("%X", servId) + " Comm ID: " + String.format("%X", commId) + " reliable: " + reliable);
291 |
292 | String descrip = null;
293 | if(reliable)
294 | {
295 | short error = b.getShort();
296 | if (!cmdProcessError(error,handler)) {
297 | return;
298 | }
299 |
300 | switch (commId) {
301 | case (short) 0xA055://connect answer:
302 | handler.connected();
303 | break;
304 | case (short) 0xA065://something
305 | case (short) 0xA095://bind
306 | handler.log("not should happen here!");
307 | break;
308 | case (short) 0xA066: {//activate service:
309 | byte service = b.get();
310 | if (service == -73)
311 | handler.cmdModeActivated();
312 | else
313 | handler.rtModeActivated();
314 | } break;
315 | case (short) 0x005A://AL_DISCONNECT_RES:
316 | descrip = "AL_DISCONNECT_RES";
317 | break;
318 | case (short) 0xA069: {//service deactivated
319 | byte service = b.get();
320 | descrip = "AL_DEACTIVATE_RES";
321 | if (service == -73)
322 | handler.cmdModeDeactivated();
323 | else
324 | handler.rtModeDeactivated();
325 | } break;
326 | case (short) 0xA06A://service all deactivate
327 | descrip = "AL_DEACTIVATE_ALL_RES";
328 | handler.modeDeactivated();
329 | break;
330 | default:
331 | descrip = "UNKNOWN";
332 | break;
333 | }
334 | } else {
335 | switch (commId) {
336 | case (short) 0x0555://Display frame
337 | handler.addDisplayFrame(b);
338 | break;
339 | case (short) 0x0556://key answer
340 | break;
341 | case (short) 0x0566://alive answer, often missed
342 | break;
343 | default:
344 | descrip = "UNKNOWN";
345 | break;
346 | }
347 | }
348 | handler.log("appProcess: "+descrip);
349 | }
350 |
351 | private static boolean cmdProcessError(short error, AppHandler handler) {
352 | String desc = "Error > " + String.format("%X", error) + " ";
353 |
354 | if (error == 0x0000) {
355 | desc = "No error found!";
356 | return true;
357 | } else {
358 | switch (error) {
359 | //Application Layer **********************************************//
360 | case (short) 0xF003:
361 | desc = "Unknown Service ID, AL, RT, or CMD";
362 | break;
363 | case (short) 0xF006:
364 | desc = "Invalid payload length";
365 | break;
366 | case (short) 0xF05F:
367 | desc = "wrong mode";
368 | handler.modeError();
369 | break;
370 |
371 | case (short) 0xF50C:
372 | desc = "wrong sequence";
373 | handler.sequenceError();
374 | break;
375 | case (short) 0xF533:
376 | desc = "died - no alive";
377 | break;
378 | case (short) 0xF056:
379 | desc = "not complete connected";
380 | break;
381 | }
382 |
383 | handler.error(error,desc);
384 | return false;
385 | }
386 | }
387 | }
388 |
--------------------------------------------------------------------------------
/app/src/main/java/org/monkey/d/ruffy/ruffy/SetupFragment.java:
--------------------------------------------------------------------------------
1 | package org.monkey.d.ruffy.ruffy;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.BluetoothDevice;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.support.v4.app.Fragment;
9 | import android.support.v7.app.AlertDialog;
10 | import android.text.InputType;
11 | import android.text.method.ScrollingMovementMethod;
12 | import android.view.LayoutInflater;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.widget.Button;
16 | import android.widget.EditText;
17 | import android.widget.TextView;
18 |
19 | import org.monkey.d.ruffy.ruffy.driver.Application;
20 | import org.monkey.d.ruffy.ruffy.driver.BTConnection;
21 | import org.monkey.d.ruffy.ruffy.driver.BTHandler;
22 | import org.monkey.d.ruffy.ruffy.driver.Frame;
23 | import org.monkey.d.ruffy.ruffy.driver.Packet;
24 | import org.monkey.d.ruffy.ruffy.driver.Protocol;
25 | import org.monkey.d.ruffy.ruffy.driver.Twofish_Algorithm;
26 | import org.monkey.d.ruffy.ruffy.driver.Utils;
27 |
28 | import java.nio.ByteBuffer;
29 | import java.nio.ByteOrder;
30 | import java.util.ArrayList;
31 | import java.util.List;
32 |
33 | /**
34 | * A placeholder fragment containing a simple view.
35 | */
36 | public class SetupFragment extends Fragment implements View.OnClickListener {
37 |
38 | private TextView connectLog;
39 | private BTConnection btConn;
40 | private int step = 0;
41 | private BluetoothDevice pairingDevice;
42 | private byte[] pin;
43 | final byte[] pairingKey = {16,9,2,0,-16};
44 |
45 | public SetupFragment() {
46 |
47 | }
48 |
49 | @Override
50 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
51 | Bundle savedInstanceState) {
52 | View v = inflater.inflate(R.layout.fragment_setup, container, false);
53 | Button connect = (Button) v.findViewById(R.id.setup_connect);
54 | connect.setOnClickListener(this);
55 |
56 | connectLog = (TextView) v.findViewById(R.id.setup_log);
57 | connectLog.setMovementMethod(new ScrollingMovementMethod());
58 |
59 | return v;
60 | }
61 |
62 | @Override
63 | public void onClick(final View view) {
64 | view.setEnabled(false);
65 | connectLog.setText("Starting rfcomm to wait for Pump connection…");
66 |
67 | btConn = new BTConnection(new BTHandler() {
68 | BluetoothDevice device;
69 |
70 | @Override
71 | public void deviceConnected() {
72 | appendLog("connected to device ");
73 | pairingDevice = device;
74 | appendLog("initiate pairing…");
75 | step = 1;
76 | btConn.writeCommand(pairingKey);
77 | }
78 |
79 | @Override
80 | public void log(String s) {
81 | if(step == 200 && s.equals("got error in read"))
82 | {
83 | getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.container,new MainFragment()).addToBackStack("Start").commit();
84 | return;
85 | }
86 | appendLog(s);
87 | }
88 |
89 | @Override
90 | public void fail(String s) {
91 | appendLog(s);
92 | if(step == 1)//trying to connect
93 | {
94 | appendLog("retrying to connect!");
95 | btConn.connect(pairingDevice);
96 | }
97 | }
98 |
99 | @Override
100 | public void deviceFound(BluetoothDevice device) {
101 | if (this.device == null) {
102 | this.device = device;
103 | appendLog("found device first time " + device + " waiting for next");
104 | } else if (this.device.getAddress().equals(device.getAddress())) {
105 | pairingDevice = device;
106 | btConn.connect(device);
107 | } else {
108 | this.device = device;
109 | appendLog("found device first time " + device + " waiting for next");
110 | }
111 | }
112 |
113 | @Override
114 | public void handleRawData(byte[] buffer, int bytes) {
115 | handleData(buffer,bytes);
116 | }
117 |
118 | @Override
119 | public void requestBlueTooth() {
120 | Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
121 | getActivity().startActivityForResult(enableBtIntent, 1);
122 | }
123 | });
124 | btConn.makeDiscoverable(getActivity());
125 | }
126 |
127 | private void appendLog(final String message) {
128 | // Log.v("RUFFY_LOG", message);
129 | if(getActivity()!=null) {
130 | getActivity().runOnUiThread(new Runnable() {
131 | @Override
132 | public void run() {
133 | connectLog.append("\n" + message);
134 | final int scrollAmount = connectLog.getLayout().getLineTop(connectLog.getLineCount()) - connectLog.getHeight();
135 | if (scrollAmount > 0)
136 | connectLog.scrollTo(0, scrollAmount);
137 | else
138 | connectLog.scrollTo(0, 0);
139 | }
140 | });
141 | }
142 | }
143 |
144 | public void handleRX(byte[] inBuf, int length, boolean rel) {
145 |
146 | ByteBuffer buffer = ByteBuffer.wrap(inBuf, 0, length);
147 | buffer.order(ByteOrder.LITTLE_ENDIAN);
148 |
149 | ByteBuffer pBuf;
150 |
151 | byte[] nonce, payload, umac, packetNoUmac;
152 |
153 | Byte command, addresses;
154 | buffer.get();
155 | command = (byte)(buffer.get() & 0x1F);
156 |
157 | short payloadlength = buffer.getShort();
158 |
159 | addresses = buffer.get();
160 |
161 | nonce = new byte[13]; //Copy buffers for nonce
162 | buffer.get(nonce, 0, nonce.length);
163 |
164 | payload = new byte[payloadlength]; //Payload
165 | buffer.get(payload, 0, payload.length);
166 | pBuf = ByteBuffer.wrap(payload);
167 |
168 | umac = new byte[8]; //U-MAC
169 | buffer.get(umac, 0, umac.length);
170 |
171 | packetNoUmac = new byte[buffer.capacity() - umac.length];
172 | buffer.rewind();
173 | for (int i = 0; i < packetNoUmac.length; i++)
174 | packetNoUmac[i] = buffer.get();
175 |
176 | byte c = (byte)(command & 0x1F);
177 | switch (c) {
178 | case 0x11://key response?
179 | try {
180 | Object tf = Twofish_Algorithm.makeKey(pin);
181 | btConn.getPumpData().setAndSaveAddress((byte) ((addresses << 4) & 0xF0)); //Get the address and reverse it since source and destination are reversed from the RX packet
182 |
183 | byte[] key_pd = new byte[16]; //Get the bytes for the keys
184 | byte[] key_dp = new byte[16];
185 |
186 | pBuf.rewind();
187 | pBuf.get(key_pd, 0, key_pd.length);
188 | pBuf.get(key_dp, 0, key_dp.length);
189 |
190 | String d = "";
191 | for (byte b : key_pd)
192 | d += String.format("%02X ", b);
193 | appendLog("parseRx >>> Key_PD: " + d);
194 |
195 | d = "";
196 | for (byte b : key_dp)
197 | d += String.format("%02X ", b);
198 | appendLog("parseRx >>> Key_DP: " + d);
199 |
200 |
201 | btConn.getPumpData().setAndSaveToDeviceKey(key_pd,tf);
202 | btConn.getPumpData().setAndSaveToPumpKey(key_dp,tf);
203 | btConn.getPumpData().setAndSavePumpMac(pairingDevice.getAddress());
204 | Protocol.sendIDReq(btConn);
205 | } catch (Exception e) {
206 | e.printStackTrace();
207 | appendLog("failed inRX: " + e.getMessage());
208 | }
209 | break;
210 | case 20:
211 | appendLog("got an id response");
212 | if (Utils.ccmVerify(packetNoUmac, btConn.getPumpData().getToDeviceKey(), umac, nonce)) {
213 | byte[] device = new byte[13];
214 |
215 | pBuf.order(ByteOrder.LITTLE_ENDIAN);
216 | int serverId = pBuf.getInt();
217 | pBuf.get(device);
218 | String deviceId = new String(device);
219 |
220 | appendLog("Server ID: " + String.format("%X", serverId) + " Device ID: " + deviceId);
221 |
222 | try {
223 | Protocol.sendSyn(btConn);
224 | appendLog("send Syn!");
225 | }catch(Exception e) {
226 | e.printStackTrace();
227 | appendLog("failed to send Syn!");
228 | }
229 |
230 | }
231 | break;
232 | case 24:
233 | appendLog("got a sync response ");
234 | if (Utils.ccmVerify(packetNoUmac, btConn.getPumpData().getToDeviceKey(), umac, nonce)) {
235 | btConn.seqNo = 0x00;
236 |
237 | appendLog("Sequence Number reset!");
238 | appendLog("parseRx >>> Sending APP_SEND_CONNECT!");
239 |
240 | if(step<100)
241 | Application.sendAppConnect(btConn);
242 | else
243 | {
244 | Application.sendAppDisconnect(btConn);
245 | step=200;
246 | }
247 | }
248 | break;
249 |
250 | case 0x23: //recieved reliable data/
251 | case 0x03: //recieve unreliable data
252 | if (Utils.ccmVerify(packetNoUmac, btConn.getPumpData().getToDeviceKey(), umac, nonce)) {
253 | SetupFragment.this.processAppResponse(payload, rel);
254 | }
255 | break;
256 |
257 | case 0x05://ack response
258 | break;
259 |
260 | default:
261 | appendLog("not yet implemented rx command: " + command + " ( " + String.format("%02X", command));
262 |
263 | }
264 | }
265 |
266 | private void processAppResponse(byte[] payload, boolean reliable) {
267 | appendLog("processing app response");
268 | ByteBuffer b = ByteBuffer.wrap(payload);
269 | b.order(ByteOrder.LITTLE_ENDIAN);
270 |
271 | b.get();
272 | byte servId = b.get();
273 | short commId = b.getShort();
274 |
275 | appendLog("Service ID: " + String.format("%X", servId) + " Comm ID: " + String.format("%X", commId) + " reliable: " + reliable);
276 |
277 | short error = b.getShort();
278 | if (error != 0) {
279 | appendLog("got error :(");
280 | return;
281 | }
282 |
283 | switch (commId) {
284 | case (short) 0xA055:
285 | Application.sendAppCommand(Application.Command.COMMANDS_SERVICES_VERSION,btConn);
286 | break;
287 | case (short) 0xA065:
288 | Application.sendAppCommand(Application.Command.BINDING,btConn);
289 | break;
290 | case (short) 0xA095:
291 | step+=100;
292 | Protocol.sendSyn(btConn);
293 | break;
294 | }
295 | }
296 |
297 | void handleData(byte buffer[], int bytes) {
298 | switch (step) {
299 | case 1: //we requested con, now we try to request auth
300 | {
301 | appendLog(this.getId() + " doing A_KEY_REQ");
302 | byte[] key = {16, 12, 2, 0, -16};
303 |
304 | btConn.writeCommand(key);
305 |
306 | if(getActivity()!=null) {
307 | getActivity().runOnUiThread(new Runnable() {
308 | @Override
309 | public void run() {
310 | final EditText pinIn = new EditText(getContext());
311 | pinIn.setInputType(InputType.TYPE_CLASS_NUMBER);
312 | pinIn.setHint("XXX XXX XXXX");
313 | new AlertDialog.Builder(getContext())
314 | .setTitle("Enter Pin")
315 | .setMessage("Read the Pin-Code from pump and enter it")
316 | .setView(pinIn)
317 | .setPositiveButton("ENTER", new DialogInterface.OnClickListener() {
318 | public void onClick(DialogInterface dialog, int whichButton) {
319 | String pin = pinIn.getText().toString();
320 | appendLog("got the pin: " + pin);
321 | SetupFragment.this.pin = Utils.generateKey(pin);
322 | step = 2;
323 | //sending key available:
324 | appendLog(" doing A_KEY_AVA");
325 | byte[] key = {16, 15, 2, 0, -16};
326 | btConn.writeCommand(key);
327 |
328 | }
329 | })
330 | .show();
331 | }
332 | });
333 | }
334 |
335 | }
336 | break;
337 | default: //we indicated that we have a key, now lets handle the handle to the handle with an handler
338 | {
339 | List t = new ArrayList<>();
340 | for (int i = 0; i < bytes; i++)
341 | t.add(buffer[i]);
342 | for (List x : Frame.frameDeEscaping(t)) {
343 | byte[] xx = new byte[x.size()];
344 | for (int i = 0; i < x.size(); i++)
345 | xx[i] = x.get(i);
346 | boolean rel = false;
347 | if ((x.get(1) & 0x20) == 0x20) {
348 | rel = true;
349 |
350 | byte seq = 0x00;
351 | if ((x.get(1) & 0x80) == 0x80)
352 | seq = (byte) 0x80;
353 |
354 | btConn.getPumpData().incrementNonceTx();
355 |
356 | List packet = Packet.buildPacket(new byte[]{16, 5, 0, 0, 0}, null, true,btConn);
357 |
358 | packet.set(1, (byte) (packet.get(1) | seq)); //OR the received sequence number
359 |
360 | packet = Utils.ccmAuthenticate(packet, btConn.getPumpData().getToPumpKey(), btConn.getPumpData().getNonceTx());
361 |
362 | List temp = Frame.frameEscape(packet);
363 | byte[] ro = new byte[temp.size()];
364 | int i = 0;
365 | for (byte b : temp)
366 | ro[i++] = b;
367 | try {
368 | btConn.write(ro);
369 | appendLog(this.getId() + ": succesful wrote " + temp.size() + " bytes!");
370 | } catch (Exception e) {
371 | e.printStackTrace();
372 | appendLog(this.getId() + ": error in tx: " + e.getMessage());
373 | }
374 |
375 | } else {
376 | rel = false;
377 | }
378 | handleRX(xx, x.size(), rel);
379 | }
380 | }
381 | break;
382 | }
383 |
384 | }
385 |
386 | }
387 |
--------------------------------------------------------------------------------
/app/src/main/java/org/monkey/d/ruffy/ruffy/MainFragment.java:
--------------------------------------------------------------------------------
1 | package org.monkey.d.ruffy.ruffy;
2 |
3 | import android.app.AlertDialog;
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.content.Intent;
9 | import android.content.ServiceConnection;
10 | import android.content.pm.PackageManager;
11 | import android.os.Bundle;
12 | import android.os.IBinder;
13 | import android.os.RemoteException;
14 | import android.support.v4.app.Fragment;
15 | import android.text.method.ScrollingMovementMethod;
16 | import android.util.Log;
17 | import android.view.LayoutInflater;
18 | import android.view.MotionEvent;
19 | import android.view.View;
20 | import android.view.ViewGroup;
21 | import android.widget.Button;
22 | import android.widget.LinearLayout;
23 | import android.widget.TextView;
24 |
25 | import org.monkey.d.ruffy.ruffy.driver.IRTHandler.Stub;
26 | import org.monkey.d.ruffy.ruffy.driver.IRuffyService;
27 | import org.monkey.d.ruffy.ruffy.driver.Ruffy;
28 | import org.monkey.d.ruffy.ruffy.driver.display.Menu;
29 | import org.monkey.d.ruffy.ruffy.driver.display.MenuAttribute;
30 | import org.monkey.d.ruffy.ruffy.view.PumpDisplayView;
31 |
32 | import java.util.concurrent.Executors;
33 | import java.util.concurrent.ScheduledExecutorService;
34 | import java.text.SimpleDateFormat;
35 | import java.util.Date;
36 |
37 |
38 |
39 | /**
40 | * A placeholder fragment containing a simple view.
41 | */
42 | public class MainFragment extends Fragment implements View.OnClickListener {
43 |
44 | private TextView connectLog;
45 | private TextView frameCounter;
46 | private Button connect;
47 | private PumpDisplayView displayView;
48 | private LinearLayout displayLayout;
49 | private TextView versionNameView;
50 |
51 |
52 | private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 3 );
53 |
54 | public boolean mServiceBound = false;
55 | public IRuffyService mBoundService;
56 |
57 | public MainFragment() {
58 | }
59 |
60 | private ServiceConnection mServiceConnection = new ServiceConnection() {
61 |
62 | @Override
63 | public void onServiceDisconnected(ComponentName name) {
64 | mServiceBound = false;
65 | }
66 |
67 | @Override
68 | public void onServiceConnected(ComponentName name, IBinder service) {
69 | mBoundService = IRuffyService.Stub.asInterface(service);
70 | mServiceBound = true;
71 |
72 | try {
73 | mBoundService.setHandler(handler);
74 | } catch (RemoteException e) {
75 | e.printStackTrace();
76 | }
77 | }
78 | };
79 |
80 | @Override
81 | public void onDestroy() {
82 | super.onDestroy();
83 | if (mServiceBound) {
84 |
85 | try {
86 | appendLog("Try to disconnect before exit");
87 | mBoundService.doRTDisconnect();
88 | } catch (RemoteException e) {
89 | e.printStackTrace();
90 | }
91 |
92 | getActivity().unbindService(mServiceConnection);
93 | mServiceBound = false;
94 | }
95 | }
96 |
97 | private int upRunning = 0;
98 | private int downRunning = 0;
99 |
100 | private Runnable upThread = new Runnable()
101 | {
102 | @Override
103 | public void run() {
104 | while(upRunning >0)
105 | {
106 | if(upRunning==1) {
107 | upRunning++;
108 | rtSendKey(Ruffy.Key.UP,true);
109 | } else {
110 | rtSendKey(Ruffy.Key.UP,false);
111 | }
112 | try{sleep(200);}catch(Exception e){}
113 | }
114 | rtSendKey(Ruffy.Key.NO_KEY,true);
115 | }
116 | };
117 |
118 | private Runnable downThread = new Runnable()
119 | {
120 | @Override
121 | public void run() {
122 | while(downRunning >0)
123 | {
124 | if(downRunning==1) {
125 | downRunning++;
126 | rtSendKey(Ruffy.Key.DOWN,true);
127 | }
128 | else
129 | {
130 | rtSendKey(Ruffy.Key.DOWN,false);
131 | }
132 | try{sleep(200);}catch(Exception e){}
133 | }
134 | rtSendKey(Ruffy.Key.NO_KEY,true);
135 | }
136 | };
137 |
138 | private void sleep(long millis)
139 | {
140 | try{Thread.sleep(millis);}catch(Exception e){/*ignore*/}
141 | }
142 |
143 | private void rtSendKey(byte keyCode, boolean changed) {
144 | try {
145 | if (mBoundService.isConnected()) {
146 | try {
147 | mBoundService.rtSendKey(keyCode, changed);
148 | } catch (RemoteException re) {
149 | re.printStackTrace();
150 | appendLog("failed keySend: " + re.getMessage());
151 | }
152 | }
153 | } catch (RemoteException e) {
154 | e.printStackTrace();
155 | }
156 | }
157 |
158 | private void resetPairing()
159 | {
160 | try {
161 | mBoundService.resetPairing();
162 | }catch(RemoteException re)
163 | {
164 | re.printStackTrace();
165 | appendLog("failed keySend: "+re.getMessage());
166 | }
167 | }
168 |
169 | private Stub handler = new Stub() {
170 | @Override
171 | public void log(String message) throws RemoteException {
172 | appendLog(message);
173 | }
174 |
175 | @Override
176 | public void fail(String message) throws RemoteException {
177 | appendLog("fail: "+message);
178 | /*getActivity().runOnUiThread(new Runnable() {
179 | @Override
180 | public void run() {
181 | connect.setText("Try Connect again!");
182 | connect.setEnabled(true);
183 | }
184 | });*/
185 | }
186 |
187 | @Override
188 | public void requestBluetooth() throws RemoteException {
189 | Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
190 | getActivity().startActivityForResult(enableBtIntent, 1);
191 | }
192 |
193 | public void rtStarted()
194 | {
195 | getActivity().runOnUiThread(new Runnable() {
196 | @Override
197 | public void run() {
198 | try {
199 | if (mBoundService.isConnected()) {
200 | connect.setText("Disconnect");
201 | } else {
202 | connect.setText("Connect");
203 | }
204 | } catch (RemoteException e) {
205 | e.printStackTrace();
206 | }
207 | connect.setEnabled(true);
208 | }
209 | });
210 | }
211 |
212 | @Override
213 | public void rtClearDisplay() throws RemoteException {
214 | displayView.clear();
215 | }
216 |
217 | //TODO just for debug marker byte[][] display = new byte[4][];
218 | @Override
219 | public void rtUpdateDisplay(byte[] quarter, int which) throws RemoteException {
220 |
221 | displayView.update(quarter,which);
222 |
223 | //TODO just for debug marker display[which] = quarter;
224 | if (connectLog.getVisibility() != View.GONE)
225 | getActivity().runOnUiThread(new Runnable() {
226 | @Override
227 | public void run() {
228 | connectLog.setVisibility(View.GONE);
229 | }
230 | });
231 | }
232 |
233 | @Override
234 | public void rtDisplayHandleMenu(final Menu menu) throws RemoteException {
235 | getActivity().runOnUiThread(new Runnable() {
236 | @Override
237 | public void run() {
238 | String s = "";
239 | for(MenuAttribute ma : menu.attributes())
240 | {
241 | s+="\n"+ma+": "+menu.getAttribute(ma);
242 | }
243 |
244 | frameCounter.setText("display found: "+menu.getType()+s);
245 | }
246 | });
247 | }
248 |
249 | @Override
250 | public void rtDisplayHandleNoMenu() throws RemoteException {
251 | getActivity().runOnUiThread(new Runnable() {
252 | @Override
253 | public void run() {
254 | frameCounter.setText("no display found");
255 | /* DisplayParser.findMenu(display, new DisplayParserHandler() {
256 | @Override
257 | public void menuFound(Menu menu) {
258 |
259 | }
260 |
261 | @Override
262 | public void noMenuFound() {
263 |
264 | }
265 | });*///TODO just for debug marker
266 | }
267 | });
268 | }
269 |
270 | public void rtStopped()
271 | {
272 | getActivity().runOnUiThread(new Runnable() {
273 | @Override
274 | public void run() {
275 | connectLog.setVisibility(View.VISIBLE);
276 | }
277 | });
278 | }
279 | };
280 |
281 | @Override
282 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
283 | Bundle savedInstanceState) {
284 |
285 | View v = inflater.inflate(R.layout.fragment_main, container, false);
286 |
287 | try {
288 | versionNameView = (TextView) v.findViewById(R.id.versionName);
289 | versionNameView.setText(getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), 0).versionName);
290 | } catch (PackageManager.NameNotFoundException e) {}
291 |
292 | connect = (Button) v.findViewById(R.id.main_connect);
293 | connect.setOnClickListener(this);
294 |
295 | Button reset = (Button) v.findViewById(R.id.main_reset);
296 | reset.setOnClickListener(new View.OnClickListener() {
297 | @Override
298 | public void onClick(View v) {
299 | new AlertDialog.Builder(getContext()).setTitle("remove bonding?").setMessage("Really delete bonding informations with pump?").setPositiveButton("YES", new DialogInterface.OnClickListener() {
300 | @Override
301 | public void onClick(DialogInterface dialog, int which) {
302 | resetPairing();
303 | getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.container,new SetupFragment()).addToBackStack("Start").commit();
304 | }
305 | }).setNegativeButton("NO",null).show();
306 | }
307 | });
308 |
309 | connectLog = (TextView) v.findViewById(R.id.main_log);
310 | connectLog.setMovementMethod(new ScrollingMovementMethod());
311 | connectLog.setTextIsSelectable(true);
312 |
313 | displayLayout= (LinearLayout) v.findViewById(R.id.pumpPanel);
314 | displayView = (PumpDisplayView) displayLayout.findViewById(R.id.pumpView);
315 |
316 | frameCounter = (TextView) v.findViewById(R.id.frameCounter);
317 | Button menu = (Button) displayLayout.findViewById(R.id.pumpMenu);
318 | menu.setOnClickListener(new View.OnClickListener() {
319 | @Override
320 | public void onClick(View v) {
321 | rtSendKey(Ruffy.Key.MENU,true);
322 | sleep(100);
323 | rtSendKey(Ruffy.Key.NO_KEY,true);
324 | }
325 | });
326 | Button check = (Button) displayLayout.findViewById(R.id.pumpCheck);
327 | check.setOnClickListener(new View.OnClickListener() {
328 | @Override
329 | public void onClick(View v) {
330 | rtSendKey(Ruffy.Key.CHECK,true);
331 | sleep(100);
332 | rtSendKey(Ruffy.Key.NO_KEY,true);
333 | }
334 | });
335 | Button up = (Button) displayLayout.findViewById(R.id.pumpUp);
336 | up.setOnTouchListener(new View.OnTouchListener() {
337 | @Override
338 | public boolean onTouch(View v, MotionEvent event) {
339 | switch(event.getAction())
340 | {
341 | case MotionEvent.ACTION_DOWN:
342 | if(upRunning==0) {
343 | upRunning = 1;
344 | scheduler.execute(upThread);
345 | }
346 | break;
347 |
348 | case MotionEvent.ACTION_UP:
349 | upRunning=0;
350 | break;
351 | }
352 |
353 | return false;
354 | }
355 | });
356 | Button down= (Button) displayLayout.findViewById(R.id.pumpDown);
357 | down.setOnTouchListener(new View.OnTouchListener() {
358 | @Override
359 | public boolean onTouch(View v, MotionEvent event) {
360 | switch(event.getAction())
361 | {
362 | case MotionEvent.ACTION_DOWN:
363 | if(downRunning==0) {
364 | downRunning = 1;
365 | scheduler.execute(downThread);
366 | }
367 | break;
368 |
369 | case MotionEvent.ACTION_UP:
370 | downRunning=0;
371 | break;
372 | }
373 |
374 | return false;
375 | }
376 | });
377 | Intent intent = new Intent(getActivity(), Ruffy.class);
378 | ComponentName name = getActivity().startService(intent);
379 | if(name != null)
380 | {
381 | if(getActivity().bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE))
382 | {
383 | // Log.v("Start","bound it");
384 | }
385 | }
386 | return v;
387 | }
388 |
389 | @Override
390 | public void onClick(final View view) {
391 | view.setEnabled(false);
392 | if(connect.getText().toString().startsWith("Disco"))
393 | {
394 | try {
395 | mBoundService.doRTDisconnect();//FIXME
396 | } catch (RemoteException e) {
397 | e.printStackTrace();
398 | }
399 | connect.setText("Connect!");
400 | return;
401 | }
402 | try {
403 | mBoundService.doRTConnect();
404 | } catch (RemoteException e) {
405 | e.printStackTrace();
406 | }
407 | }
408 |
409 | private void appendLog(final String message) {
410 | String currentDateTime = "NO_DATE";
411 | try {
412 | SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSS");
413 | currentDateTime = dateFormat.format(new Date()); // Find todays date
414 | } catch (Exception e) {
415 | e.printStackTrace();
416 | }
417 |
418 | final String message_time = currentDateTime + " - " + message;
419 | // Log.v("RUFFY_LOG", message);
420 |
421 | if(connectLog.getVisibility()!=View.GONE) {
422 | getActivity().runOnUiThread(new Runnable() {
423 | @Override
424 | public void run() {
425 | if (connectLog.getLineCount() < 1000) {
426 | connectLog.append("\n" + message_time);
427 | } else {
428 | connectLog.setText("");
429 | }
430 | // TODO not sure how or why, but I kept getting NPEs with the getLayout call
431 | // here when doing the initial pairing
432 | if (connectLog.getLayout() != null) {
433 | final int scrollAmount = connectLog.getLayout().getLineTop(connectLog.getLineCount()) - connectLog.getHeight();
434 | if (scrollAmount > 0)
435 | connectLog.scrollTo(0, scrollAmount);
436 | else
437 | connectLog.scrollTo(0, 0);
438 | }
439 | }
440 | });
441 | }
442 | }
443 | }
444 |
--------------------------------------------------------------------------------
/app/src/main/java/org/monkey/d/ruffy/ruffy/driver/Ruffy.java:
--------------------------------------------------------------------------------
1 | package org.monkey.d.ruffy.ruffy.driver;
2 |
3 | import android.app.Activity;
4 | import android.app.Service;
5 | import android.bluetooth.BluetoothDevice;
6 | import android.content.Intent;
7 | import android.content.SharedPreferences;
8 | import android.os.IBinder;
9 | import android.os.RemoteException;
10 | import android.os.SystemClock;
11 | import android.util.Log;
12 | import org.apache.commons.lang3.exception.ExceptionUtils;
13 | import org.monkey.d.ruffy.ruffy.driver.display.DisplayParser;
14 | import org.monkey.d.ruffy.ruffy.driver.display.DisplayParserHandler;
15 | import org.monkey.d.ruffy.ruffy.driver.display.Menu;
16 |
17 | import java.nio.ByteBuffer;
18 | import java.util.concurrent.Executors;
19 | import java.util.concurrent.ScheduledExecutorService;
20 |
21 | /**
22 | * Created by fishermen21 on 25.05.17.
23 | */
24 |
25 | public class Ruffy extends Service {
26 |
27 | public static class Key {
28 | public static byte NO_KEY =(byte)0x00;
29 | public static byte MENU =(byte)0x03;
30 | public static byte CHECK =(byte)0x0C;
31 | public static byte UP =(byte)0x30;
32 | public static byte DOWN =(byte)0xC0;
33 | }
34 |
35 | private IRTHandler rtHandler = null;
36 | private BTConnection btConn;
37 | private PumpData pumpData;
38 |
39 | private boolean rtModeRunning = false;
40 | private long lastRtMessageSent = 0;
41 |
42 | private final Object rtSequenceSemaphore = new Object();
43 | private short rtSequence = 0;
44 |
45 | private int modeErrorCount = 0;
46 | private int step = 0;
47 |
48 | private boolean synRun=false;//With set to false, write process is started at first time
49 | private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 3 );
50 |
51 | private Display display;
52 |
53 | private final IRuffyService.Stub serviceBinder = new IRuffyService.Stub(){
54 |
55 | @Override
56 | public void setHandler(IRTHandler handler) throws RemoteException {
57 | Ruffy.this.rtHandler = handler;
58 | }
59 |
60 | @Override
61 | public int doRTConnect() throws RemoteException {
62 | if(isConnected() && rtModeRunning)
63 | {
64 | rtHandler.rtStarted();
65 | return 0;
66 | }
67 | step= 0;
68 | if(Ruffy.this.rtHandler==null)
69 | {
70 | throw new IllegalStateException("XXX");
71 |
72 | // return -2;//FIXME make errors
73 | }
74 | if(!isConnected()) {
75 | if (pumpData == null) {
76 | pumpData = PumpData.loadPump(Ruffy.this, rtHandler);
77 | }
78 | if (pumpData != null) {
79 | inShutDown=false;
80 | btConn = new BTConnection(rtBTHandler);
81 | rtModeRunning = true;
82 | btConn.connect(pumpData, 10);
83 | return 0;
84 | }
85 | } else {
86 | inShutDown=false;
87 | rtModeRunning = true;
88 | Application.sendAppCommand(Application.Command.COMMAND_DEACTIVATE,btConn);
89 | }
90 | return -1;
91 | }
92 |
93 | public void doRTDisconnect()
94 | {
95 | step = 200;
96 | stopRT();
97 | }
98 |
99 | public void rtSendKey(byte keyCode, boolean changed)
100 | {
101 | //FIXME
102 | lastRtMessageSent = System.currentTimeMillis();
103 | synchronized (rtSequenceSemaphore) {
104 | rtSequence = Application.rtSendKey(keyCode, changed, rtSequence, btConn);
105 | }
106 | }
107 |
108 | public void resetPairing()
109 | {
110 | SharedPreferences prefs = Ruffy.this.getSharedPreferences("pumpdata", Activity.MODE_PRIVATE);
111 | prefs.edit().putBoolean("paired",false).apply();
112 |
113 | /*
114 | String bondedDeviceId = prefs.getString("device", null);
115 | if (bondedDeviceId != null) {
116 | BluetoothDevice boundedPump = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bondedDeviceId);
117 | // TODO I know!
118 | try {
119 | Method removeBound = boundedPump.getClass().getMethod("removeBond", (Class>[]) null);
120 | removeBound.invoke(boundedPump, (Object[]) null);
121 | } catch (ReflectiveOperationException e) {
122 | // it's not going better here either
123 | }
124 | }
125 | */
126 |
127 | synRun=false;
128 | rtModeRunning =false;
129 | }
130 |
131 | public boolean isConnected() {
132 | return btConn != null && btConn.isConnected();
133 | }
134 |
135 | public String getMacAddress() {
136 | return pumpData != null ? pumpData.getPumpMac() : null;
137 | }
138 | };
139 |
140 |
141 | @Override
142 | public void onCreate() {
143 | super.onCreate();
144 | }
145 |
146 | @Override
147 | public IBinder onBind(Intent intent) {
148 | return serviceBinder;
149 | }
150 |
151 | @Override
152 | public void onRebind(Intent intent) {
153 | super.onRebind(intent);
154 | }
155 |
156 | @Override
157 | public boolean onUnbind(Intent intent) {
158 | return true;
159 | }
160 |
161 | @Override
162 | public void onDestroy() {
163 | super.onDestroy();
164 | }
165 |
166 | private BTHandler rtBTHandler = new BTHandler() {
167 | @Override
168 | public void deviceConnected() {
169 | inShutDown=false;
170 | log("connected to pump");
171 | if(synRun==false && !inShutDown) {
172 | synRun = true;
173 | log("start synThread");
174 | scheduler.execute(synThread);
175 | }
176 | }
177 |
178 | @Override
179 | public void log(String s) {
180 | Ruffy.this.log(s);
181 | if(s.equals("got error in read") && step < 200 && !inShutDown)
182 | {
183 | synRun=false;
184 | btConn.connect(pumpData,4);
185 | }
186 | Log.v("RuffyService",s);
187 | }
188 |
189 | @Override
190 | public void fail(String s) {
191 | log("failed: "+s);
192 | synRun=false;
193 | if(step < 200)
194 | btConn.connect(pumpData,4);
195 | else
196 | Ruffy.this.fail(s);
197 |
198 | }
199 |
200 | @Override
201 | public void deviceFound(BluetoothDevice bd) {
202 | log("not be here!?!");
203 | }
204 |
205 | @Override
206 | public void handleRawData(byte[] buffer, int bytes) {
207 | log("got data from pump");
208 | synRun=false;
209 | step=0;
210 | Packet.handleRawData(buffer,bytes, rtPacketHandler);
211 | }
212 |
213 | @Override
214 | public void requestBlueTooth() {
215 | try {
216 | rtHandler.requestBluetooth();
217 | } catch (RemoteException e) {
218 | e.printStackTrace();
219 | }
220 | }
221 | };
222 |
223 | private void stopRT()
224 | {
225 | step=200;
226 | rtModeRunning = false;
227 | // wait for the keep alive thread to detect rtModeRunning has become false
228 | // so it won't trigger during disconnect
229 | SystemClock.sleep(500 + 250);
230 | Application.sendAppCommand(Application.Command.DEACTIVATE_ALL,btConn);
231 | }
232 |
233 | private void startRT() {
234 | log("starting RT keepAlive");
235 | new Thread(){
236 | @Override
237 | public void run() {
238 | rtModeRunning = true;
239 | rtSequence = 0;
240 | lastRtMessageSent = System.currentTimeMillis();
241 | rtModeRunning = true;
242 | try {
243 | display = new Display(new DisplayUpdater() {
244 | @Override
245 | public void clear() {
246 | try {
247 | rtHandler.rtClearDisplay();
248 | } catch (RemoteException e) {
249 | e.printStackTrace();
250 | }
251 | }
252 |
253 | @Override
254 | public void update(byte[] quarter, int which) {
255 | try {
256 | rtHandler.rtUpdateDisplay(quarter,which);
257 | } catch (RemoteException e) {
258 | e.printStackTrace();
259 | }
260 | }
261 | });
262 | display.setCompletDisplayHandler(new CompleteDisplayHandler() {
263 | @Override
264 | public void handleCompleteFrame(byte[][] pixels) {
265 | DisplayParser.findMenu(pixels, new DisplayParserHandler() {
266 | @Override
267 | public void menuFound(Menu menu) {
268 | try {
269 | rtHandler.rtDisplayHandleMenu(menu);
270 | } catch (RemoteException e) {
271 | e.printStackTrace();
272 | }
273 | }
274 |
275 | @Override
276 | public void noMenuFound() {
277 | try {
278 | rtHandler.rtDisplayHandleNoMenu();
279 | } catch (RemoteException e) {
280 | e.printStackTrace();
281 | }
282 | }
283 | });
284 |
285 | }
286 | });
287 | rtHandler.rtStarted();
288 | } catch (RemoteException e) {
289 | e.printStackTrace();
290 | }
291 | while(rtModeRunning)
292 | {
293 | try {
294 | if (System.currentTimeMillis() > lastRtMessageSent + 1000L) {
295 | log("sending keep alive");
296 | synchronized (rtSequenceSemaphore) {
297 | rtSequence = Application.sendRTKeepAlive(rtSequence, btConn);
298 | lastRtMessageSent = System.currentTimeMillis();
299 | }
300 | }
301 | } catch (Exception e) {
302 | if (rtModeRunning) {
303 | fail("Error sending keep alive while rtModeRunning is still true: " + ExceptionUtils.getStackTrace(e));
304 | } else {
305 | fail("Error sending keep alive. rtModeRunning is false, so this is most likely a race condition during disconnect: " + ExceptionUtils.getStackTrace(e));
306 | }
307 | }
308 | try{
309 | Thread.sleep(500);}catch(Exception e){/*ignore*/}
310 | }
311 | try {
312 | rtHandler.rtStopped();
313 | } catch (RemoteException e) {
314 | e.printStackTrace();
315 | }
316 |
317 | }
318 | }.start();
319 | }
320 |
321 | private Runnable synThread = new Runnable(){
322 | @Override
323 | public void run() {
324 | while(synRun && rtModeRunning)
325 | {
326 | Protocol.sendSyn(btConn);
327 | try {
328 | Thread.sleep(500);
329 | } catch (InterruptedException e) {
330 | e.printStackTrace();
331 | }
332 | }
333 | }
334 | };
335 |
336 | private AppHandler rtAppHandler = new AppHandler() {
337 | @Override
338 | public void log(String s) {
339 | Ruffy.this.log(s);
340 | }
341 |
342 | @Override
343 | public void connected() {
344 | Application.sendAppCommand(Application.Command.RT_MODE, btConn);
345 | }
346 |
347 | @Override
348 | public void rtModeActivated() {
349 | startRT();
350 | }
351 |
352 | @Override
353 | public void cmdModeActivated() {
354 | // not there yet
355 | }
356 |
357 | @Override
358 | public void rtModeDeactivated() {
359 | rtSequence =0;
360 |
361 | if(rtHandler!=null)
362 | try {rtHandler.rtStopped();} catch (RemoteException e){};
363 | if(!inShutDown) {
364 | inShutDown = true;
365 | Application.sendAppDisconnect(btConn);
366 | btConn.disconnect();
367 | }
368 | }
369 |
370 | @Override
371 | public void cmdModeDeactivated() {
372 | }
373 |
374 | @Override
375 | public void modeDeactivated() {
376 | rtModeRunning = false;
377 | rtSequence =0;
378 | if(rtHandler!=null)
379 | try {rtHandler.rtStopped();} catch (RemoteException e){};
380 | if(!inShutDown) {
381 | inShutDown = true;
382 | Application.sendAppDisconnect(btConn);
383 | btConn.disconnect();
384 | }
385 | }
386 |
387 | @Override
388 | public void addDisplayFrame(ByteBuffer b) {
389 | display.addDisplayFrame(b);
390 | }
391 |
392 | @Override
393 | public void modeError() {
394 | modeErrorCount++;
395 |
396 | if (modeErrorCount > Application.MODE_ERROR_TRESHHOLD) {
397 | stopRT();
398 | log("wrong mode, deactivate");
399 |
400 | modeErrorCount = 0;
401 | Application.sendAppCommand(Application.Command.DEACTIVATE_ALL, btConn);
402 | }
403 | }
404 |
405 | @Override
406 | public void sequenceError() {
407 | Application.sendAppCommand(Application.Command.APP_DISCONNECT, btConn);
408 | }
409 |
410 | @Override
411 | public void error(short error, String desc) {
412 | switch (error)
413 | {
414 | case (short) 0xF056:
415 | PumpData d = btConn.getPumpData();
416 | btConn.disconnect();
417 | btConn.connect(d,4);
418 | break;
419 | default:
420 | log(desc);
421 | }
422 | }
423 | };
424 |
425 | public void log(String s) {
426 | try{
427 | rtHandler.log(s);
428 | }catch(Exception e){e.printStackTrace();}
429 | }
430 |
431 | public void fail(String s) {
432 | try{
433 | rtHandler.fail(s);
434 | }catch(Exception e){e.printStackTrace();}
435 | }
436 |
437 | private boolean inShutDown = false;
438 | private PacketHandler rtPacketHandler = new PacketHandler(){
439 | @Override
440 | public void sendImidiateAcknowledge(byte sequenceNumber) {
441 | if(!inShutDown)
442 | Protocol.sendAck(sequenceNumber,btConn);
443 | }
444 |
445 | @Override
446 | public void log(String s) {
447 | Ruffy.this.log(s);
448 | }
449 |
450 | @Override
451 | public void handleResponse(Packet.Response response, boolean reliableFlagged, byte[] payload) {
452 | switch (response)
453 | {
454 | case ID:
455 | Protocol.sendSyn(btConn);
456 | break;
457 | case SYNC:
458 | btConn.seqNo = 0x00;
459 |
460 | if(step<100)
461 | Application.sendAppConnect(btConn);
462 | else
463 | {
464 | Application.sendAppDisconnect(btConn);
465 | step = 200;
466 | }
467 | break;
468 | case UNRELIABLE_DATA:
469 | case RELIABLE_DATA:
470 | Application.processAppResponse(payload, reliableFlagged, rtAppHandler);
471 | break;
472 | }
473 | }
474 |
475 | @Override
476 | public void handleErrorResponse(byte errorCode, String errDecoded, boolean reliableFlagged, byte[] payload) {
477 | log(errDecoded);
478 | }
479 |
480 | @Override
481 | public Object getToDeviceKey() {
482 | return pumpData.getToDeviceKey();
483 | }
484 | };
485 | }
486 |
--------------------------------------------------------------------------------
/app/src/main/java/org/monkey/d/ruffy/ruffy/driver/display/menu/TitleResolver.java:
--------------------------------------------------------------------------------
1 | package org.monkey.d.ruffy.ruffy.driver.display.menu;
2 |
3 | /**
4 | * Created by fishermen21 on 22.05.17.
5 | */
6 |
7 | class TitleResolver {
8 | public static Title resolve(String title) {
9 |
10 | /**english titles**/
11 | if(title.equalsIgnoreCase("bolus amount"))
12 | return Title.BOLUS_AMOUNT;
13 | if(title.equalsIgnoreCase("immediate bolus"))
14 | return Title.IMMEDIATE_BOLUS;
15 | if(title.equalsIgnoreCase("bolus duration"))
16 | return Title.BOLUS_DURATION;
17 | if(title.equalsIgnoreCase("quick info"))
18 | return Title.QUICK_INFO;
19 | if(title.equalsIgnoreCase("bolus data"))
20 | return Title.BOLUS_DATA;
21 | if(title.equalsIgnoreCase("error data"))
22 | return Title.ERROR_DATA;
23 | if(title.equalsIgnoreCase("daily totals"))
24 | return Title.DAILY_TOTALS;
25 | if(title.equalsIgnoreCase("tbr data"))
26 | return Title.TBR_DATA;
27 | if(title.equalsIgnoreCase("tbr percentage"))
28 | return Title.TBR_SET;
29 | if(title.equalsIgnoreCase("tbr duration"))
30 | return Title.TBR_DURATION;
31 |
32 | /**german titles**/
33 | if(title.equalsIgnoreCase("bolus-menge"))
34 | return Title.BOLUS_AMOUNT;
35 | if(title.equalsIgnoreCase("sofortige abgabe"))
36 | return Title.IMMEDIATE_BOLUS;
37 | if(title.equalsIgnoreCase("abgabedauer"))
38 | return Title.BOLUS_DURATION;
39 | if(title.equalsIgnoreCase("quick info"))
40 | return Title.QUICK_INFO;
41 | if(title.equalsIgnoreCase("bolusinformation"))
42 | return Title.BOLUS_DATA;
43 | if(title.equalsIgnoreCase("fehlermeldungen"))
44 | return Title.ERROR_DATA;
45 | if(title.equalsIgnoreCase("tagesgesamtmenge"))
46 | return Title.DAILY_TOTALS;
47 | if(title.equalsIgnoreCase("tbr-information"))
48 | return Title.TBR_DATA;
49 | if(title.equalsIgnoreCase("tbr wert"))
50 | return Title.TBR_SET;
51 | if(title.equalsIgnoreCase("tbr dauer"))
52 | return Title.TBR_DURATION;
53 |
54 | /**French titles**/
55 | if(title.equalsIgnoreCase("quantité bolus"))
56 | return Title.BOLUS_AMOUNT;
57 | if(title.equalsIgnoreCase("quanti. immédiate"))
58 | return Title.IMMEDIATE_BOLUS;
59 | if(title.equalsIgnoreCase("durée du bolus"))
60 | return Title.BOLUS_DURATION;
61 | if(title.equalsIgnoreCase("quick info"))
62 | return Title.QUICK_INFO;
63 | if(title.equalsIgnoreCase("bolus"))
64 | return Title.BOLUS_DATA;
65 | if(title.equalsIgnoreCase("erreurs"))
66 | return Title.ERROR_DATA;
67 | if(title.equalsIgnoreCase("quantités journ."))
68 | return Title.DAILY_TOTALS;
69 | if(title.equalsIgnoreCase("dbt"))
70 | return Title.TBR_DATA;
71 | if(title.equalsIgnoreCase("valeur du dbt"))
72 | return Title.TBR_SET;
73 | if(title.equalsIgnoreCase("durée du dbt"))
74 | return Title.TBR_DURATION;
75 |
76 |
77 | /**spanish titles**/
78 | if(title.equalsIgnoreCase("cantidad de bolo"))
79 | return Title.BOLUS_AMOUNT;
80 | if(title.equalsIgnoreCase("bolo inmediato"))
81 | return Title.IMMEDIATE_BOLUS;
82 | if(title.equalsIgnoreCase("duración de bolo"))
83 | return Title.BOLUS_DURATION;
84 | if(title.equalsIgnoreCase("quick info"))
85 | return Title.QUICK_INFO;
86 | if(title.equalsIgnoreCase("datos de bolo"))
87 | return Title.BOLUS_DATA;
88 | if(title.equalsIgnoreCase("datos de error"))
89 | return Title.ERROR_DATA;
90 | if(title.equalsIgnoreCase("totales diarios"))
91 | return Title.DAILY_TOTALS;
92 | if(title.equalsIgnoreCase("datos de dbt"))
93 | return Title.TBR_DATA;
94 | if(title.equalsIgnoreCase("porcentaje dbt"))
95 | return Title.TBR_SET;
96 | if(title.equalsIgnoreCase("duración de dbt"))
97 | return Title.TBR_DURATION;
98 |
99 |
100 | /**italian titles**/
101 | if(title.equalsIgnoreCase("quantita bolo")) //multiwave 1
102 | return Title.BOLUS_AMOUNT;
103 | if(title.equalsIgnoreCase("bolo immediato")) //multiwave 2
104 | return Title.IMMEDIATE_BOLUS;
105 | if(title.equalsIgnoreCase("tempo erogazione")) //multiwave 3
106 | return Title.BOLUS_DURATION;
107 | if(title.equalsIgnoreCase("quick info")) //check1
108 | return Title.QUICK_INFO;
109 | if(title.equalsIgnoreCase("memoria boli")) //check2, mydata 1
110 | return Title.BOLUS_DATA;
111 | if(title.equalsIgnoreCase("memoria allarmi")) //mydata 2
112 | return Title.ERROR_DATA;
113 | if(title.equalsIgnoreCase("totali giornata")) //mydata 3
114 | return Title.DAILY_TOTALS;
115 | if(title.equalsIgnoreCase("memoria pbt")) //mydata 4
116 | return Title.TBR_DATA;
117 | if(title.equalsIgnoreCase("percentuale pbt")) //TBR 1
118 | return Title.TBR_SET;
119 | if(title.equalsIgnoreCase("durata pbt")) //TBR 2
120 | return Title.TBR_DURATION;
121 |
122 | /**dutch titles**/
123 | if(title.equalsIgnoreCase("bolushoeveelheid")) //multiwave 1
124 | return Title.BOLUS_AMOUNT;
125 | if(title.equalsIgnoreCase("directe bolus")) //multiwave 2
126 | return Title.IMMEDIATE_BOLUS;
127 | if(title.equalsIgnoreCase("bolusduur")) //multiwave 3
128 | return Title.BOLUS_DURATION;
129 | if(title.equalsIgnoreCase("quick info")) //check1
130 | return Title.QUICK_INFO;
131 | if(title.equalsIgnoreCase("bolusgegevens")) //check2, mydata 1
132 | return Title.BOLUS_DATA;
133 | if(title.equalsIgnoreCase("foutengegevens")) //mydata 2
134 | return Title.ERROR_DATA;
135 | if(title.equalsIgnoreCase("dagtotalen")) //mydata 3
136 | return Title.DAILY_TOTALS;
137 | if(title.equalsIgnoreCase("tbd-gegevens")) //mydata 4
138 | return Title.TBR_DATA;
139 | if(title.equalsIgnoreCase("tbd-percentage")) //TBR 1
140 | return Title.TBR_SET;
141 | if(title.equalsIgnoreCase("tbd-duur")) //TBR 2
142 | return Title.TBR_DURATION;
143 |
144 | /**norwegian titles**/
145 | if(title.equalsIgnoreCase("bolusmengde")) //multiwave 1
146 | return Title.BOLUS_AMOUNT;
147 | if(title.equalsIgnoreCase("umiddelbar bolus")) //multiwave 2
148 | return Title.IMMEDIATE_BOLUS;
149 | if(title.equalsIgnoreCase("bolusvarighet")) //multiwave 3
150 | return Title.BOLUS_DURATION;
151 | if(title.equalsIgnoreCase("quick info")) //check1
152 | return Title.QUICK_INFO;
153 | if(title.equalsIgnoreCase("bolusdata")) //check2, mydata 1
154 | return Title.BOLUS_DATA;
155 | if(title.equalsIgnoreCase("feildata")) //mydata 2
156 | return Title.ERROR_DATA;
157 | if(title.equalsIgnoreCase("døgnmengde")) //mydata 3
158 | return Title.DAILY_TOTALS;
159 | if(title.equalsIgnoreCase("mbd-data")) //mydata 4
160 | return Title.TBR_DATA;
161 | if(title.equalsIgnoreCase("mbd-prosent")) //TBR 1
162 | return Title.TBR_SET;
163 | if(title.equalsIgnoreCase("mbd-varighet")) //TBR 2
164 | return Title.TBR_DURATION;
165 |
166 | /**polish titles**/
167 | if(title.equalsIgnoreCase("wielkość bolusa")) //multiwave 1
168 | return Title.BOLUS_AMOUNT;
169 | if(title.equalsIgnoreCase("bolus natychm.")) //multiwave 2
170 | return Title.IMMEDIATE_BOLUS;
171 | if(title.equalsIgnoreCase("cz. trw. bolusa")) //multiwave 3
172 | return Title.BOLUS_DURATION;
173 | if(title.equalsIgnoreCase("quick info")) //check1
174 | return Title.QUICK_INFO;
175 | if(title.equalsIgnoreCase("dane bolusa")) //check2, mydata 1
176 | return Title.BOLUS_DATA;
177 | if(title.equalsIgnoreCase("dane błędu")) //mydata 2
178 | return Title.ERROR_DATA;
179 | if(title.equalsIgnoreCase("dzien. d. całk.")) //mydata 3
180 | return Title.DAILY_TOTALS;
181 | if(title.equalsIgnoreCase("dane tdp")) //mydata 4
182 | return Title.TBR_DATA;
183 | if(title.equalsIgnoreCase("procent tdp")) //TBR 1
184 | return Title.TBR_SET;
185 | if(title.equalsIgnoreCase("czas trwania tdp")) //TBR 2
186 | return Title.TBR_DURATION;
187 |
188 | /**cz titles**/
189 | if(title.equalsIgnoreCase("množství bolusu")) //multiwave 1
190 | return Title.BOLUS_AMOUNT;
191 | if(title.equalsIgnoreCase("okamžitý bolus")) //multiwave 2
192 | return Title.IMMEDIATE_BOLUS;
193 | if(title.equalsIgnoreCase("trvání bolusu")) //multiwave 3
194 | return Title.BOLUS_DURATION;
195 | if(title.equalsIgnoreCase("quick info")) //check1
196 | return Title.QUICK_INFO;
197 | if(title.equalsIgnoreCase("údaje bolusů")) //check2, mydata 1
198 | return Title.BOLUS_DATA;
199 | if(title.equalsIgnoreCase("údaje chyb")) //mydata 2
200 | return Title.ERROR_DATA;
201 | if(title.equalsIgnoreCase("celk. den. dávky")) //mydata 3
202 | return Title.DAILY_TOTALS;
203 | if(title.equalsIgnoreCase("údaje dbd")) //mydata 4
204 | return Title.TBR_DATA;
205 | if(title.equalsIgnoreCase("procento dbd")) //TBR 1
206 | return Title.TBR_SET;
207 | if(title.equalsIgnoreCase("trvání dbd")) //TBR 2
208 | return Title.TBR_DURATION;
209 |
210 | /**finnish titles**/
211 | if(title.equalsIgnoreCase("boluksen määrä")) //multiwave 1
212 | return Title.BOLUS_AMOUNT;
213 | if(title.equalsIgnoreCase("nopea bolus")) //multiwave 2
214 | return Title.IMMEDIATE_BOLUS;
215 | if(title.equalsIgnoreCase("boluksen kesto")) //multiwave 3
216 | return Title.BOLUS_DURATION;
217 | if(title.equalsIgnoreCase("quick info")) //check1
218 | return Title.QUICK_INFO;
219 | if(title.equalsIgnoreCase("bolustiedot")) //check2, mydata 1
220 | return Title.BOLUS_DATA;
221 | if(title.equalsIgnoreCase("hälytystiedot")) //mydata 2
222 | return Title.ERROR_DATA;
223 | if(title.equalsIgnoreCase("päiv. kok.annos")) //mydata 3
224 | return Title.DAILY_TOTALS;
225 | if(title.equalsIgnoreCase("tba - tiedot")) //mydata 4
226 | return Title.TBR_DATA;
227 | if(title.equalsIgnoreCase("tba - prosentti")) //TBR 1
228 | return Title.TBR_SET;
229 | if(title.equalsIgnoreCase("tba - kesto")) //TBR 2
230 | return Title.TBR_DURATION;
231 |
232 | /**turkish titles**/
233 | if(title.equalsIgnoreCase("bolus mİktari")) //multiwave 1
234 | return Title.BOLUS_AMOUNT;
235 | if(title.equalsIgnoreCase("hemen bolus uygl")) //multiwave 2
236 | return Title.IMMEDIATE_BOLUS;
237 | if(title.equalsIgnoreCase("bolus süresİ")) //multiwave 3
238 | return Title.BOLUS_DURATION;
239 | if(title.equalsIgnoreCase("quick info")) //check1
240 | return Title.QUICK_INFO;
241 | if(title.equalsIgnoreCase("bolus verİlerİ")) //check2, mydata 1
242 | return Title.BOLUS_DATA;
243 | if(title.equalsIgnoreCase("hata verİlerİ")) //mydata 2
244 | return Title.ERROR_DATA;
245 | if(title.equalsIgnoreCase("günlük toplam")) //mydata 3
246 | return Title.DAILY_TOTALS;
247 | if(title.equalsIgnoreCase("gbh verİlerİ")) //mydata 4
248 | return Title.TBR_DATA;
249 | if(title.equalsIgnoreCase("gbh yüzdesİ")) //TBR 1
250 | return Title.TBR_SET;
251 | if(title.equalsIgnoreCase("gbh süresİ")) //TBR 2
252 | return Title.TBR_DURATION;
253 |
254 | /**romanian titles**/
255 | if(title.equalsIgnoreCase("cantitate bolus")) //multiwave 1
256 | return Title.BOLUS_AMOUNT;
257 | if(title.equalsIgnoreCase("bolus imediat")) //multiwave 2
258 | return Title.IMMEDIATE_BOLUS;
259 | if(title.equalsIgnoreCase("durată bolus")) //multiwave 3
260 | return Title.BOLUS_DURATION;
261 | if(title.equalsIgnoreCase("quick info")) //check1
262 | return Title.QUICK_INFO;
263 | if(title.equalsIgnoreCase("date bolus")) //check2, mydata 1
264 | return Title.BOLUS_DATA;
265 | if(title.equalsIgnoreCase("date eroare")) //mydata 2
266 | return Title.ERROR_DATA;
267 | if(title.equalsIgnoreCase("totaluri zilnice")) //mydata 3
268 | return Title.DAILY_TOTALS;
269 | if(title.equalsIgnoreCase("date rbt")) //mydata 4
270 | return Title.TBR_DATA;
271 | if(title.equalsIgnoreCase("procent rbt")) //TBR 1
272 | return Title.TBR_SET;
273 | if(title.equalsIgnoreCase("durata rbt")) //TBR 2
274 | return Title.TBR_DURATION;
275 |
276 | /**swedish titles**/
277 | if(title.equalsIgnoreCase("bolusmängd")) //multiwave 1
278 | return Title.BOLUS_AMOUNT;
279 | if(title.equalsIgnoreCase("direkt bolus")) //multiwave 2
280 | return Title.IMMEDIATE_BOLUS;
281 | if(title.equalsIgnoreCase("bolusduration")) //multiwave 3
282 | return Title.BOLUS_DURATION;
283 | if(title.equalsIgnoreCase("quick info")) //check1
284 | return Title.QUICK_INFO;
285 | if(title.equalsIgnoreCase("bolusdata")) //check2, mydata 1
286 | return Title.BOLUS_DATA;
287 | if(title.equalsIgnoreCase("feldata")) //mydata 2
288 | return Title.ERROR_DATA;
289 | if(title.equalsIgnoreCase("dygnshistorik")) //mydata 3
290 | return Title.DAILY_TOTALS;
291 | if(title.equalsIgnoreCase("tbd data")) //mydata 4
292 | return Title.TBR_DATA;
293 | if(title.equalsIgnoreCase("tbd procent")) //TBR 1
294 | return Title.TBR_SET;
295 | if(title.equalsIgnoreCase("tbd duration")) //TBR 2
296 | return Title.TBR_DURATION;
297 |
298 |
299 | /**danish titles**/
300 | if(title.equalsIgnoreCase("bolusmængde")) //multiwave 1
301 | return Title.BOLUS_AMOUNT;
302 | if(title.equalsIgnoreCase("umiddelbar bolus")) //multiwave 2
303 | return Title.IMMEDIATE_BOLUS;
304 | if(title.equalsIgnoreCase("bolusvarighed")) //multiwave 3
305 | return Title.BOLUS_DURATION;
306 | if(title.equalsIgnoreCase("quick info")) //check1
307 | return Title.QUICK_INFO;
308 | if(title.equalsIgnoreCase("bolusdata")) //check2, mydata 1
309 | return Title.BOLUS_DATA;
310 | if(title.equalsIgnoreCase("fejldata")) //mydata 2
311 | return Title.ERROR_DATA;
312 | if(title.equalsIgnoreCase("daglig total")) //mydata 3
313 | return Title.DAILY_TOTALS;
314 | if(title.equalsIgnoreCase("mbr-data")) //mydata 4
315 | return Title.TBR_DATA;
316 | if(title.equalsIgnoreCase("mbr-procent")) //TBR 1
317 | return Title.TBR_SET;
318 | if(title.equalsIgnoreCase("mbr-varighed")) //TBR 2
319 | return Title.TBR_DURATION;
320 |
321 | /**hungarian titles**/
322 | if(title.equalsIgnoreCase("bólusmennyiség")) //multiwave 1
323 | return Title.BOLUS_AMOUNT;
324 | if(title.equalsIgnoreCase("azonnali bólus")) //multiwave 2
325 | return Title.IMMEDIATE_BOLUS;
326 | if(title.equalsIgnoreCase("bólus időtartam")) //multiwave 3
327 | return Title.BOLUS_DURATION;
328 | if(title.equalsIgnoreCase("quick info")) //check1
329 | return Title.QUICK_INFO;
330 | if(title.equalsIgnoreCase("bólusadatok")) //check2, mydata 1
331 | return Title.BOLUS_DATA;
332 | if(title.equalsIgnoreCase("hibaadatok")) //mydata 2
333 | return Title.ERROR_DATA;
334 | if(title.equalsIgnoreCase("napi teljes")) //mydata 3
335 | return Title.DAILY_TOTALS;
336 | if(title.equalsIgnoreCase("tbr-adatok")) //mydata 4
337 | return Title.TBR_DATA;
338 | if(title.equalsIgnoreCase("tbr százalék")) //TBR 1
339 | return Title.TBR_SET;
340 | if(title.equalsIgnoreCase("tbr időtartam")) //TBR 2
341 | return Title.TBR_DURATION;
342 |
343 |
344 | /**slovak titles**/
345 | if(title.equalsIgnoreCase("množstvo bolusu")) //multiwave 1
346 | return Title.BOLUS_AMOUNT;
347 | if(title.equalsIgnoreCase("okamžitý bolus")) //multiwave 2
348 | return Title.IMMEDIATE_BOLUS;
349 | if(title.equalsIgnoreCase("trvanie bolusu")) //multiwave 3
350 | return Title.BOLUS_DURATION;
351 | if(title.equalsIgnoreCase("quick info")) //check1
352 | return Title.QUICK_INFO;
353 | if(title.equalsIgnoreCase("bolusové dáta")) //check2, mydata 1
354 | return Title.BOLUS_DATA;
355 | if(title.equalsIgnoreCase("dáta o chybách")) //mydata 2
356 | return Title.ERROR_DATA;
357 | if(title.equalsIgnoreCase("súčty dňa")) //mydata 3
358 | return Title.DAILY_TOTALS;
359 | if(title.equalsIgnoreCase("dbd dáta")) //mydata 4
360 | return Title.TBR_DATA;
361 | if(title.equalsIgnoreCase("percento dbd")) //TBR 1
362 | return Title.TBR_SET;
363 | if(title.equalsIgnoreCase("trvanie dbd")) //TBR 2
364 | return Title.TBR_DURATION;
365 |
366 | /**portugues titles (on some newer pumps translations have changed, so a menu can have multiple names) **/
367 | if (title.equalsIgnoreCase("volume do bolus")
368 | || title.equalsIgnoreCase("dose do bolus")) //multiwave 1
369 | return Title.BOLUS_AMOUNT;
370 | if(title.equalsIgnoreCase("bolus imediato")) //multiwave 2
371 | return Title.IMMEDIATE_BOLUS;
372 | if(title.equalsIgnoreCase("duraÇão do bolus")) //multiwave 3
373 | return Title.BOLUS_DURATION;
374 | if(title.equalsIgnoreCase("quick info")) //check1
375 | return Title.QUICK_INFO;
376 | if(title.equalsIgnoreCase("dados de bolus")) //check2, mydata 1
377 | return Title.BOLUS_DATA;
378 | if (title.equalsIgnoreCase("dados de erros")
379 | || title.equalsIgnoreCase("dados de alarmes")) //mydata 2
380 | return Title.ERROR_DATA;
381 | if(title.equalsIgnoreCase("totais diários")) //mydata 3
382 | return Title.DAILY_TOTALS;
383 | if(title.equalsIgnoreCase("dados dbt")) //mydata 4
384 | return Title.TBR_DATA;
385 | if(title.equalsIgnoreCase("dbt percentagem")) //TBR 1
386 | return Title.TBR_SET;
387 | if(title.equalsIgnoreCase("dbt duraÇão")) //TBR 2
388 | return Title.TBR_DURATION;
389 |
390 |
391 | /**russian titles**/
392 | if(title.equalsIgnoreCase("OбъEм бOлюCа")) //multiwave 1
393 | return Title.BOLUS_AMOUNT;
394 | if(title.equalsIgnoreCase("пPямOй бOлюC")) //multiwave 2
395 | return Title.IMMEDIATE_BOLUS;
396 | if(title.equalsIgnoreCase("пPOдOлж. бOлюCа")) //multiwave 3
397 | return Title.BOLUS_DURATION;
398 | if(title.equalsIgnoreCase("quick info")) //check1
399 | return Title.QUICK_INFO;
400 | if(title.equalsIgnoreCase("даHHыE O бOлюCE")) //check2, mydata 1
401 | return Title.BOLUS_DATA;
402 | if(title.equalsIgnoreCase("даHHыE Oб O иб.")) //mydata 2
403 | return Title.ERROR_DATA;
404 | if(title.equalsIgnoreCase("CуTOчHыE дOзы")) //mydata 3
405 | return Title.DAILY_TOTALS;
406 | if(title.equalsIgnoreCase("даHHыE O BбC")) //mydata 4
407 | return Title.TBR_DATA;
408 | if(title.equalsIgnoreCase("пPOцEHT BбC")) //TBR 1
409 | return Title.TBR_SET;
410 | if(title.equalsIgnoreCase("пPOдOлжиT. BбC")) //TBR 2
411 | return Title.TBR_DURATION;
412 |
413 | /**croatian titles**/
414 | if(title.equalsIgnoreCase("KOLIčINA BOLUSA")) //multiwave 1
415 | return Title.BOLUS_AMOUNT;
416 | if(title.equalsIgnoreCase("TRENUTNI BOLUS")) //multiwave 2
417 | return Title.IMMEDIATE_BOLUS;
418 | if(title.equalsIgnoreCase("TRAJANJE BOLUSA")) //multiwave 3
419 | return Title.BOLUS_DURATION;
420 | if(title.equalsIgnoreCase("quick info")) //check1
421 | return Title.QUICK_INFO;
422 | if(title.equalsIgnoreCase("PODACI O BOLUSU")) //check2, mydata 1
423 | return Title.BOLUS_DATA;
424 | if(title.equalsIgnoreCase("PODACI O GREšK.")) //mydata 2
425 | return Title.ERROR_DATA;
426 | if(title.equalsIgnoreCase("UKUPNE DNEV.DOZE")) //mydata 3
427 | return Title.DAILY_TOTALS;
428 | if(title.equalsIgnoreCase("PODACI O PBD-u")) //mydata 4
429 | return Title.TBR_DATA;
430 | if(title.equalsIgnoreCase("POSTOTAK PBD-a")) //TBR 1
431 | return Title.TBR_SET;
432 | if(title.equalsIgnoreCase("TRAJANJE PBD-a")) //TBR 2
433 | return Title.TBR_DURATION;
434 |
435 | /**greek titles**/
436 | if(title.equalsIgnoreCase("пOΣOTHTа ΔOΣHΣ")) //multiwave 1
437 | return Title.BOLUS_AMOUNT;
438 | if(title.equalsIgnoreCase("амEΣH ΔOΣH")) //multiwave 2
439 | return Title.IMMEDIATE_BOLUS;
440 | if(title.equalsIgnoreCase("ΔIаPKEIа ΔOΣHΣ")) //multiwave 3
441 | return Title.BOLUS_DURATION;
442 | if(title.equalsIgnoreCase("quick info")) //check1
443 | return Title.QUICK_INFO;
444 | if(title.equalsIgnoreCase("ΔEΔOмENа ΔOΣEΩN")) //check2, mydata 1
445 | return Title.BOLUS_DATA;
446 | if(title.equalsIgnoreCase("ΔEΔOм. ΣΦаΛмаTΩN")) //mydata 2
447 | return Title.ERROR_DATA;
448 | if(title.equalsIgnoreCase("HмEPHΣIO ΣυNOΛO")) //mydata 3
449 | return Title.DAILY_TOTALS;
450 | if(title.equalsIgnoreCase("ΔEΔOмENа п.B.P.")) //mydata 4
451 | return Title.TBR_DATA;
452 | if(title.equalsIgnoreCase("пOΣOΣTO п.B.P.")) //TBR 1
453 | return Title.TBR_SET;
454 | if(title.equalsIgnoreCase("ΔIаPKEIа п.B.P.")) //TBR 2
455 | return Title.TBR_DURATION;
456 |
457 | // some pumps came preconfigured with a different quick info name
458 | if (title.equalsIgnoreCase("accu chek spirit"))
459 | return Title.QUICK_INFO;
460 |
461 | //FIXME add Translations
462 | return null;
463 | }
464 | }
465 |
--------------------------------------------------------------------------------