Skip to content

Commit 405a757

Browse files
committed
Cleaned up BtGame
1 parent 7eac079 commit 405a757

File tree

5 files changed

+92
-138
lines changed

5 files changed

+92
-138
lines changed

app/src/main/java/com/henrykvdb/sttt/Remote/BtGame.kt renamed to app/src/main/java/com/henrykvdb/sttt/remotebt/BtGame.kt

Lines changed: 91 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import com.henrykvdb.sttt.R
1616
import com.henrykvdb.sttt.Source
1717
import org.json.JSONException
1818
import org.json.JSONObject
19-
import java.io.Closeable
2019
import java.io.IOException
2120
import java.io.InputStream
2221
import java.io.OutputStream
@@ -32,12 +31,15 @@ class BtGame(val callback: RemoteCallback, val res: Resources) : RemoteGame {
3231
override var state = RemoteState.NONE
3332

3433
//Other
35-
private var btThread: CloseableThread? = null
34+
private var btThread: Thread? = null
3635
private var connectedDeviceName: String? = null
3736
private lateinit var requestState: GameState
38-
private var outStream: OutputStream? = null
3937
private var boards = LinkedList(listOf(Board()))
4038

39+
private var inStream: InputStream? = null
40+
private var outStream: OutputStream? = null
41+
42+
4143
override fun listen(gs: GameState) {
4244
requestState = gs
4345

@@ -50,262 +52,213 @@ class BtGame(val callback: RemoteCallback, val res: Resources) : RemoteGame {
5052

5153
override fun connect(adr: String) {
5254
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))
5656
close()
57-
btThread = device?.let { ConnectingThread(it) }
58-
btThread?.start()
57+
58+
device?.let {
59+
btThread = ConnectingThread(it)
60+
btThread?.start()
61+
}
5962
}
6063

61-
override fun close() {
64+
override fun close(){
6265
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)
6671
}
67-
68-
callback.turnLocal()
6972
}
7073

71-
abstract class CloseableThread : Thread(), Closeable
72-
73-
private inner class ListenThread : CloseableThread() {
74+
private inner class ListenThread : Thread() {
7475
private var serverSocket: BluetoothServerSocket? = null
7576
private var socket: BluetoothSocket? = null
7677

7778
init {
78-
this@BtGame.state = RemoteState.LISTENING
79-
8079
try {
80+
this@BtGame.state = RemoteState.LISTENING
8181
serverSocket = btAdapter.listenUsingRfcommWithServiceRecord("SuperTTT", UUID)
8282
} catch (e: IOException) {
8383
Log.e(LOG_TAG, "listen() failed", e)
84+
interrupt()
8485
}
85-
8686
}
8787

8888
override fun run() {
89-
Log.e(LOG_TAG, "BEGIN ListenThread " + this)
89+
Log.e(LOG_TAG, "BEGIN ListenThread" + this)
9090

91-
// Listen to the server socket if we're not connected
9291
while (this@BtGame.state != RemoteState.CONNECTED && !isInterrupted) {
9392
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
9694
} catch (e: IOException) {
9795
Log.e(LOG_TAG, "accept() failed", e)
98-
break
96+
interrupt()
9997
}
10098

101-
if (!isInterrupted)
102-
socket?.let { connected(it, true) }
99+
if (!isInterrupted) socket?.let { connected(it, true) } //Manage connection, blocking call
103100
}
104101

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+
}
108109

109-
@Throws(IOException::class)
110-
override fun close() {
111-
serverSocket?.close()
112-
socket?.close()
110+
Log.e(LOG_TAG, "END ListenThread $this")
113111
}
114112
}
115113

116-
private inner class ConnectingThread(device: BluetoothDevice) : CloseableThread() {
114+
private inner class ConnectingThread(device: BluetoothDevice) : Thread() {
117115
private var socket: BluetoothSocket? = null
118116

119117
init {
120-
this@BtGame.state = RemoteState.CONNECTING
121-
122118
try {
119+
this@BtGame.state = RemoteState.CONNECTING
123120
socket = device.createRfcommSocketToServiceRecord(UUID)
124121
} catch (e: IOException) {
125-
e.printStackTrace()
122+
throw e
126123
}
127-
128124
}
129125

130126
override fun run() {
131127
Log.e(LOG_TAG, "BEGIN connectingThread" + this)
132128

133-
btAdapter.cancelDiscovery()
134-
135129
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
138132

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+
}
144136
} catch (e: IOException) {
145-
this@BtGame.state = RemoteState.NONE
146137
callback.toast(res.getString(R.string.unable_to_connect))
147138
Log.e(LOG_TAG, "Unable to connect to device", e)
139+
}
148140

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)
155146
}
156147

157148
Log.e(LOG_TAG, "END connectingThread" + this)
158149
}
159-
160-
@Throws(IOException::class)
161-
override fun close() {
162-
socket?.close()
163-
}
164150
}
165151

166152
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
172153
try {
173154
inStream = socket.inputStream
174155
outStream = socket.outputStream
156+
175157
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+
}
176171
} catch (e: IOException) {
177172
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()
180177
}
181178

182-
connectedDeviceName = socket.remoteDevice.name
183-
Log.e(LOG_TAG, "CONNECTED to $connectedDeviceName")
184-
callback.toast(res.getString(R.string.connected_to, connectedDeviceName))
185-
186179
val buffer = ByteArray(1024)
187-
188-
if (isHost && !Thread.interrupted()) sendSetup()
189-
190-
// Keep listening to the InputStream while connected
191180
while (state == RemoteState.CONNECTED && !Thread.interrupted()) {
192181
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)
196184
val json = JSONObject(String(buffer, Charsets.UTF_8))
197185

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 -> {
203188
val newBoard = JSONBoard.fromJSON(JSONObject(json.getString("board")))
204-
val newMove = newBoard.lastMove()!!
205-
206189
if (isValidBoard(boards.peek(), newBoard)) {
207190
Log.e(LOG_TAG, "We received a valid board")
208191
boards.push(newBoard)
209-
callback.move(newMove)
192+
callback.move(newBoard.lastMove()!!)
210193
} else {
211194
callback.toast(res.getString(R.string.desync_message))
212195
close()
213196
}
214197
}
215-
RemoteMessageType.SETUP->{
198+
RemoteMessageType.SETUP -> {
216199
val board = JSONBoard.fromJSON(JSONObject(json.getString("board")))
217200
boards = LinkedList(listOf(board))
218201
callback.newGame(GameState.Builder().bt().board(board).swapped(!json.getBoolean("start")).build())
219202
}
220-
RemoteMessageType.UNDO->{
203+
RemoteMessageType.UNDO -> {
221204
val force = json.getBoolean("force")
222205
callback.undo(force)
223-
if(force) boards.pop()
206+
if (force) boards.pop()
224207
}
225208
}
226209
} catch (e: IOException) {
227210
Log.e(LOG_TAG, "disconnected", e)
228211
callback.toast(res.getString(R.string.connection_lost))
229-
break
212+
Thread.currentThread().interrupt()
230213
} catch (e: JSONException) {
231214
Log.e(LOG_TAG, "JSON read parsing failed")
232-
break
215+
callback.toast(res.getString(R.string.json_parsing_failed))
216+
Thread.currentThread().interrupt()
233217
}
234218
}
235219

236220
state = RemoteState.NONE
237221
callback.turnLocal()
238222

239223
try {
240-
inStream.close()
224+
Thread.currentThread().interrupt()
225+
inStream?.close()
241226
outStream?.close()
227+
socket.close()
242228
} catch (e: IOException) {
243229
e.printStackTrace()
244230
}
245-
246-
Log.e(LOG_TAG, "END connected thread")
247231
}
248232

249233
override fun sendUndo(force: Boolean) {
250-
Log.e(LOG_TAG, "Sending undo")
251-
if (state != RemoteState.CONNECTED) return
252-
if (force) boards.pop()
253-
254234
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))
260240
} catch (e: IOException) {
261-
Log.e(LOG_TAG, "Exception during undo", e)
241+
Log.e(LOG_TAG, "Exception in sendUndo", e)
262242
} catch (e: JSONException) {
263243
e.printStackTrace()
264244
}
265245
}
266246

267247
override fun sendBoard(board: Board) {
268-
Log.e(LOG_TAG, "Sending board")
269-
boards.push(board)
270-
271248
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))
301254
} catch (e: IOException) {
302-
Log.e(LOG_TAG, "Exception during boardUpdate", e)
255+
Log.e(LOG_TAG, "Exception in sendBoard()", e)
303256
} catch (e: JSONException) {
304257
e.printStackTrace()
305258
}
306259
}
307260

308261
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
310263
override val lastBoard: Board get() = boards.last
311264
}

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<string name="join_bluetooth_game">Join Bluetooth game</string>
4949
<string name="host_bluetooth_game">Host Bluetooth game</string>
5050
<string name="desync_message">Games got desynchronized</string>
51+
<string name="json_parsing_failed">JSON parsing failed</string>
5152
<string name="joinable_devices">Joinable devices</string>
5253
<string name="new_ai_title">Start a new AI game?</string>
5354
<string name="new_local_title">Start a new game?</string>

0 commit comments

Comments
 (0)