@@ -16,7 +16,6 @@ import com.henrykvdb.sttt.R
16
16
import com.henrykvdb.sttt.Source
17
17
import org.json.JSONException
18
18
import org.json.JSONObject
19
- import java.io.Closeable
20
19
import java.io.IOException
21
20
import java.io.InputStream
22
21
import java.io.OutputStream
@@ -32,12 +31,15 @@ class BtGame(val callback: RemoteCallback, val res: Resources) : RemoteGame {
32
31
override var state = RemoteState .NONE
33
32
34
33
// Other
35
- private var btThread: CloseableThread ? = null
34
+ private var btThread: Thread ? = null
36
35
private var connectedDeviceName: String? = null
37
36
private lateinit var requestState: GameState
38
- private var outStream: OutputStream ? = null
39
37
private var boards = LinkedList (listOf (Board ()))
40
38
39
+ private var inStream: InputStream ? = null
40
+ private var outStream: OutputStream ? = null
41
+
42
+
41
43
override fun listen (gs : GameState ) {
42
44
requestState = gs
43
45
@@ -50,262 +52,213 @@ class BtGame(val callback: RemoteCallback, val res: Resources) : RemoteGame {
50
52
51
53
override fun connect (adr : String ) {
52
54
val device = btAdapter.getRemoteDevice(adr)
53
- callback.toast(
54
- device?.name?.let { res.getString(R .string.connecting_to, it) } ? : res.getString(R .string.connecting))
55
-
55
+ callback.toast(device?.name?.let { res.getString(R .string.connecting_to, it) } ? : res.getString(R .string.connecting))
56
56
close()
57
- btThread = device?.let { ConnectingThread (it) }
58
- btThread?.start()
57
+
58
+ device?.let {
59
+ btThread = ConnectingThread (it)
60
+ btThread?.start()
61
+ }
59
62
}
60
63
61
- override fun close () {
64
+ override fun close (){
62
65
try {
63
- btThread?.close()
64
- } catch (e: IOException ) {
65
- e.printStackTrace()
66
+ btThread?.interrupt() ? : Unit
67
+ inStream?.close()
68
+ outStream?.close()
69
+ } catch (e: IOException ){
70
+ Log .e(LOG_TAG ," Error closing btGame" ,e)
66
71
}
67
-
68
- callback.turnLocal()
69
72
}
70
73
71
- abstract class CloseableThread : Thread (), Closeable
72
-
73
- private inner class ListenThread : CloseableThread () {
74
+ private inner class ListenThread : Thread () {
74
75
private var serverSocket: BluetoothServerSocket ? = null
75
76
private var socket: BluetoothSocket ? = null
76
77
77
78
init {
78
- this @BtGame.state = RemoteState .LISTENING
79
-
80
79
try {
80
+ this @BtGame.state = RemoteState .LISTENING
81
81
serverSocket = btAdapter.listenUsingRfcommWithServiceRecord(" SuperTTT" , UUID )
82
82
} catch (e: IOException ) {
83
83
Log .e(LOG_TAG , " listen() failed" , e)
84
+ interrupt()
84
85
}
85
-
86
86
}
87
87
88
88
override fun run () {
89
- Log .e(LOG_TAG , " BEGIN ListenThread " + this )
89
+ Log .e(LOG_TAG , " BEGIN ListenThread" + this )
90
90
91
- // Listen to the server socket if we're not connected
92
91
while (this @BtGame.state != RemoteState .CONNECTED && ! isInterrupted) {
93
92
try {
94
- // This is a blocking call and will only return on a successful connection or an exception
95
- socket = serverSocket?.accept()
93
+ socket = serverSocket?.accept() // Blocking call
96
94
} catch (e: IOException ) {
97
95
Log .e(LOG_TAG , " accept() failed" , e)
98
- break
96
+ interrupt()
99
97
}
100
98
101
- if (! isInterrupted)
102
- socket?.let { connected(it, true ) }
99
+ if (! isInterrupted) socket?.let { connected(it, true ) } // Manage connection, blocking call
103
100
}
104
101
105
- this @BtGame.state = RemoteState .NONE
106
- Log .e(LOG_TAG , " END ListenThread" )
107
- }
102
+ try {
103
+ this @BtGame.state = RemoteState .NONE
104
+ serverSocket?.close()
105
+ socket?.close()
106
+ } catch (e: IOException ) {
107
+ Log .e(LOG_TAG , " Could not close sockets when closing ListenThread" )
108
+ }
108
109
109
- @Throws(IOException ::class )
110
- override fun close () {
111
- serverSocket?.close()
112
- socket?.close()
110
+ Log .e(LOG_TAG , " END ListenThread $this " )
113
111
}
114
112
}
115
113
116
- private inner class ConnectingThread (device : BluetoothDevice ) : CloseableThread () {
114
+ private inner class ConnectingThread (device : BluetoothDevice ) : Thread () {
117
115
private var socket: BluetoothSocket ? = null
118
116
119
117
init {
120
- this @BtGame.state = RemoteState .CONNECTING
121
-
122
118
try {
119
+ this @BtGame.state = RemoteState .CONNECTING
123
120
socket = device.createRfcommSocketToServiceRecord(UUID )
124
121
} catch (e: IOException ) {
125
- e.printStackTrace()
122
+ throw e
126
123
}
127
-
128
124
}
129
125
130
126
override fun run () {
131
127
Log .e(LOG_TAG , " BEGIN connectingThread" + this )
132
128
133
- btAdapter.cancelDiscovery()
134
-
135
129
try {
136
- // This is a blocking call and will only return on a successful connection or an exception
137
- socket?.connect()
130
+ btAdapter.cancelDiscovery()
131
+ socket?.connect() // Blocking call
138
132
139
- // Manage the connection, returns when the connection is stopped
140
- socket?.let { connected(it, false ) }
141
-
142
- // No longer connected, close the socket
143
- socket!! .close()
133
+ if (! isInterrupted) {
134
+ socket?.let { connected(it, false ) } // Manage connection, blocking call
135
+ }
144
136
} catch (e: IOException ) {
145
- this @BtGame.state = RemoteState .NONE
146
137
callback.toast(res.getString(R .string.unable_to_connect))
147
138
Log .e(LOG_TAG , " Unable to connect to device" , e)
139
+ }
148
140
149
- try {
150
- socket!! .close()
151
- } catch (e2: IOException ) {
152
- Log .e(LOG_TAG , " unable to interrupt() socket during connection failure" , e2)
153
- }
154
-
141
+ try {
142
+ this @BtGame.state = RemoteState .NONE
143
+ socket?.close()
144
+ } catch (e2: IOException ) {
145
+ Log .e(LOG_TAG , " unable to interrupt() socket during connection failure" , e2)
155
146
}
156
147
157
148
Log .e(LOG_TAG , " END connectingThread" + this )
158
149
}
159
-
160
- @Throws(IOException ::class )
161
- override fun close () {
162
- socket?.close()
163
- }
164
150
}
165
151
166
152
private fun connected (socket : BluetoothSocket , isHost : Boolean ) {
167
- Log .e(LOG_TAG , " BEGIN connected thread" )
168
-
169
- if (Thread .interrupted()) return
170
-
171
- val inStream: InputStream
172
153
try {
173
154
inStream = socket.inputStream
174
155
outStream = socket.outputStream
156
+
175
157
state = RemoteState .CONNECTED
158
+ connectedDeviceName = socket.remoteDevice.name
159
+ callback.toast(res.getString(R .string.connected_to, connectedDeviceName))
160
+
161
+ if (isHost) {
162
+ callback.newGame(requestState)
163
+ boards = LinkedList (listOf (requestState.board()))
164
+
165
+ outStream?.write(JSONObject ().apply {
166
+ put(" message" , RemoteMessageType .SETUP .ordinal)
167
+ put(" start" , requestState.players.first == Source .REMOTE )
168
+ put(" board" , requestState.board().toJSON().toString())
169
+ }.toString().toByteArray(Charsets .UTF_8 ))
170
+ }
176
171
} catch (e: IOException ) {
177
172
Log .e(LOG_TAG , " connected sockets not created" , e)
178
- state = RemoteState .NONE
179
- return
173
+ Thread .currentThread().interrupt()
174
+ } catch (e: JSONException ) {
175
+ e.printStackTrace()
176
+ Thread .currentThread().interrupt()
180
177
}
181
178
182
- connectedDeviceName = socket.remoteDevice.name
183
- Log .e(LOG_TAG , " CONNECTED to $connectedDeviceName " )
184
- callback.toast(res.getString(R .string.connected_to, connectedDeviceName))
185
-
186
179
val buffer = ByteArray (1024 )
187
-
188
- if (isHost && ! Thread .interrupted()) sendSetup()
189
-
190
- // Keep listening to the InputStream while connected
191
180
while (state == RemoteState .CONNECTED && ! Thread .interrupted()) {
192
181
try {
193
- // Read from the InputStream
194
- inStream.read(buffer) // TODO improve
195
-
182
+ // TODO improve reading of the stream, replace buffer
183
+ inStream?.read(buffer)
196
184
val json = JSONObject (String (buffer, Charsets .UTF_8 ))
197
185
198
- val message = json.getInt(" message" )
199
-
200
- Log .e(LOG_TAG , " RECEIVED BTMESSAGE: $message " )
201
- when (RemoteMessageType .values()[message]){
202
- RemoteMessageType .BOARD_UPDATE -> {
186
+ when (RemoteMessageType .values()[json.getInt(" message" )]) {
187
+ RemoteMessageType .BOARD_UPDATE -> {
203
188
val newBoard = JSONBoard .fromJSON(JSONObject (json.getString(" board" )))
204
- val newMove = newBoard.lastMove()!!
205
-
206
189
if (isValidBoard(boards.peek(), newBoard)) {
207
190
Log .e(LOG_TAG , " We received a valid board" )
208
191
boards.push(newBoard)
209
- callback.move(newMove )
192
+ callback.move(newBoard.lastMove() !! )
210
193
} else {
211
194
callback.toast(res.getString(R .string.desync_message))
212
195
close()
213
196
}
214
197
}
215
- RemoteMessageType .SETUP -> {
198
+ RemoteMessageType .SETUP -> {
216
199
val board = JSONBoard .fromJSON(JSONObject (json.getString(" board" )))
217
200
boards = LinkedList (listOf (board))
218
201
callback.newGame(GameState .Builder ().bt().board(board).swapped(! json.getBoolean(" start" )).build())
219
202
}
220
- RemoteMessageType .UNDO -> {
203
+ RemoteMessageType .UNDO -> {
221
204
val force = json.getBoolean(" force" )
222
205
callback.undo(force)
223
- if (force) boards.pop()
206
+ if (force) boards.pop()
224
207
}
225
208
}
226
209
} catch (e: IOException ) {
227
210
Log .e(LOG_TAG , " disconnected" , e)
228
211
callback.toast(res.getString(R .string.connection_lost))
229
- break
212
+ Thread .currentThread().interrupt()
230
213
} catch (e: JSONException ) {
231
214
Log .e(LOG_TAG , " JSON read parsing failed" )
232
- break
215
+ callback.toast(res.getString(R .string.json_parsing_failed))
216
+ Thread .currentThread().interrupt()
233
217
}
234
218
}
235
219
236
220
state = RemoteState .NONE
237
221
callback.turnLocal()
238
222
239
223
try {
240
- inStream.close()
224
+ Thread .currentThread().interrupt()
225
+ inStream?.close()
241
226
outStream?.close()
227
+ socket.close()
242
228
} catch (e: IOException ) {
243
229
e.printStackTrace()
244
230
}
245
-
246
- Log .e(LOG_TAG , " END connected thread" )
247
231
}
248
232
249
233
override fun sendUndo (force : Boolean ) {
250
- Log .e(LOG_TAG , " Sending undo" )
251
- if (state != RemoteState .CONNECTED ) return
252
- if (force) boards.pop()
253
-
254
234
try {
255
- val json = JSONObject ()
256
- json.put( " message " , RemoteMessageType . UNDO .ordinal)
257
- json. put(" force " , force )
258
- val data = json.toString().toByteArray( Charsets . UTF_8 )
259
- outStream?.write(data )
235
+ if (force) boards.pop ()
236
+ outStream?.write( JSONObject (). apply {
237
+ put(" message " , RemoteMessageType . UNDO .ordinal )
238
+ put( " force " , force )
239
+ }.toString().toByteArray( Charsets . UTF_8 ) )
260
240
} catch (e: IOException ) {
261
- Log .e(LOG_TAG , " Exception during undo " , e)
241
+ Log .e(LOG_TAG , " Exception in sendUndo " , e)
262
242
} catch (e: JSONException ) {
263
243
e.printStackTrace()
264
244
}
265
245
}
266
246
267
247
override fun sendBoard (board : Board ) {
268
- Log .e(LOG_TAG , " Sending board" )
269
- boards.push(board)
270
-
271
248
try {
272
- val json = JSONObject ()
273
-
274
- json.put(" message" , RemoteMessageType .BOARD_UPDATE .ordinal)
275
- json.put(" board" , board.toJSON().toString())
276
-
277
- val data = json.toString().toByteArray(Charsets .UTF_8 )
278
-
279
- outStream?.write(data)
280
- } catch (e: IOException ) {
281
- Log .e(LOG_TAG , " Exception during boardUpdate" , e)
282
- } catch (e: JSONException ) {
283
- e.printStackTrace()
284
- }
285
- }
286
-
287
- private fun sendSetup () { // TODO only called once: inline
288
- callback.newGame(requestState)
289
- Log .e(LOG_TAG , " Sending setup, starting: " + requestState.players.first)
290
-
291
- boards = LinkedList (listOf (requestState.board()))
292
-
293
- try {
294
- val json = JSONObject ()
295
- json.put(" message" , RemoteMessageType .SETUP .ordinal)
296
- json.put(" start" , requestState.players.first == Source .REMOTE )
297
- json.put(" board" , requestState.board().toJSON().toString())
298
- val data = json.toString().toByteArray(Charsets .UTF_8 )
299
-
300
- outStream?.write(data)
249
+ boards.push(board)
250
+ outStream?.write(JSONObject ().apply {
251
+ put(" message" , RemoteMessageType .BOARD_UPDATE .ordinal)
252
+ put(" board" , board.toJSON().toString())
253
+ }.toString().toByteArray(Charsets .UTF_8 ))
301
254
} catch (e: IOException ) {
302
- Log .e(LOG_TAG , " Exception during boardUpdate " , e)
255
+ Log .e(LOG_TAG , " Exception in sendBoard() " , e)
303
256
} catch (e: JSONException ) {
304
257
e.printStackTrace()
305
258
}
306
259
}
307
260
308
261
override val localName get() = listOfNotNull(btAdapter.name, btAdapter.address, " ERROR" ).first()
309
- override val remoteName get() = if (state == RemoteState .CONNECTED ) connectedDeviceName else null
262
+ override val remoteName get() = if (state == RemoteState .CONNECTED ) connectedDeviceName else null
310
263
override val lastBoard: Board get() = boards.last
311
264
}
0 commit comments