|
@@ -29,7 +29,27 @@ public class UserList {
|
29
|
29
|
}
|
30
|
30
|
|
31
|
31
|
/**
|
32
|
|
- * Discover all active users over the network. Observers are notified for each user discovered.
|
|
32
|
+ * Adds an observer for new user connection events
|
|
33
|
+ *
|
|
34
|
+ * @param connectionCallback The function to add as listener
|
|
35
|
+ */
|
|
36
|
+ public void addNewUserObserver(UserConnectionCallback connectionCallback) {
|
|
37
|
+ newUsersObservers.add(connectionCallback);
|
|
38
|
+ }
|
|
39
|
+
|
|
40
|
+ /**
|
|
41
|
+ * Notifies observers the given user is a new connection
|
|
42
|
+ *
|
|
43
|
+ * @param user The newly connected user
|
|
44
|
+ */
|
|
45
|
+ private void notifyNewUserObservers(PeerUser user) {
|
|
46
|
+ newUsersObservers.forEach(callback -> callback.onUserConnected(user));
|
|
47
|
+ }
|
|
48
|
+
|
|
49
|
+ /**
|
|
50
|
+ * Discovers all active users over the network
|
|
51
|
+ * and create a connection for each.
|
|
52
|
+ * Observers are notified for each new successful connection.
|
33
|
53
|
*
|
34
|
54
|
* @param errorCallback The function to call on error
|
35
|
55
|
* @see UserList#addNewUserObserver(UserConnectionCallback)
|
|
@@ -45,6 +65,10 @@ public class UserList {
|
45
|
65
|
}, errorCallback);
|
46
|
66
|
}
|
47
|
67
|
|
|
68
|
+ /**
|
|
69
|
+ * Starts listening for other broadcasts.
|
|
70
|
+ * Only answers and waits for them to initiate the connection.
|
|
71
|
+ */
|
48
|
72
|
public void startDiscoveryListening() {
|
49
|
73
|
netDiscoverer.startDiscoveryListening(
|
50
|
74
|
"CLAVARDATOR_RESPONSE",
|
|
@@ -52,6 +76,18 @@ public class UserList {
|
52
|
76
|
Throwable::printStackTrace);
|
53
|
77
|
}
|
54
|
78
|
|
|
79
|
+ public void retrievedPreviousUsers(UserListLoadedCallback onFinish) {
|
|
80
|
+ db.getAllUsers(users -> {
|
|
81
|
+ users.forEach(user -> createNewUser(user.id, user.getUsername()));
|
|
82
|
+ onFinish.onLoaded();
|
|
83
|
+ });
|
|
84
|
+ }
|
|
85
|
+
|
|
86
|
+ /**
|
|
87
|
+ * Starts listening to user connection requests.
|
|
88
|
+ *
|
|
89
|
+ * @param errorCallback Callback on error
|
|
90
|
+ */
|
55
|
91
|
public void startUserListening(ErrorCallback errorCallback) {
|
56
|
92
|
connectionListener.acceptConnection(
|
57
|
93
|
(clientSocket) -> {
|
|
@@ -66,11 +102,24 @@ public class UserList {
|
66
|
102
|
errorCallback);
|
67
|
103
|
}
|
68
|
104
|
|
|
105
|
+ /**
|
|
106
|
+ * Notify observers and subscribe to user changes.
|
|
107
|
+ *
|
|
108
|
+ * @param user The newly connected user
|
|
109
|
+ */
|
69
|
110
|
private void onUserConnectionSuccess(PeerUser user) {
|
70
|
111
|
notifyNewUserObservers(user);
|
71
|
112
|
user.addObserver(evt -> userChangeObserver(user, evt));
|
72
|
113
|
}
|
73
|
114
|
|
|
115
|
+ /**
|
|
116
|
+ * Creates a new user from its id and puts it in the inactive user list.
|
|
117
|
+ * We first try to fetch it from active and inactive users to prevent duplicates.
|
|
118
|
+ * We do not notify observers yet as the connection may be canceled on username checks.
|
|
119
|
+ *
|
|
120
|
+ * @param id The new user's id.
|
|
121
|
+ * @return A new PeerUser, or null if the user is already connected
|
|
122
|
+ */
|
74
|
123
|
private PeerUser createNewUser(int id) {
|
75
|
124
|
// If already connected, warn and return
|
76
|
125
|
if (activeUsers.containsKey(id)) {
|
|
@@ -83,65 +132,92 @@ public class UserList {
|
83
|
132
|
PeerUser user = inactiveUsers.get(id);
|
84
|
133
|
// else create it
|
85
|
134
|
if (user == null) {
|
86
|
|
- // Username is set on TCP connection start
|
|
135
|
+ // Username is set on TCP connection start or db fetch
|
87
|
136
|
user = new PeerUser(id);
|
88
|
137
|
inactiveUsers.put(id, user);
|
89
|
138
|
}
|
|
139
|
+ return user;
|
|
140
|
+ }
|
90
|
141
|
|
|
142
|
+ /**
|
|
143
|
+ * Creates a new user from its id and username and put it in inactive user list.
|
|
144
|
+ * We first try to fetch it from active and inactive users to prevent duplicates.
|
|
145
|
+ * We DO notify observers as the created user has all the information needed.
|
|
146
|
+ *
|
|
147
|
+ * @param id The new user's id.
|
|
148
|
+ * @param username The new user's username.
|
|
149
|
+ * @return A new PeerUser, or null if the user is already connected
|
|
150
|
+ */
|
|
151
|
+ private PeerUser createNewUser(int id, String username) {
|
|
152
|
+ final PeerUser user = createNewUser(id);
|
|
153
|
+ if (user != null) {
|
|
154
|
+ user.setUsername(username);
|
|
155
|
+ notifyNewUserObservers(user);
|
|
156
|
+ }
|
91
|
157
|
return user;
|
92
|
158
|
}
|
93
|
159
|
|
94
|
|
- private void userChangeObserver(PeerUser user, PropertyChangeEvent evt) {
|
95
|
|
- int id = user.id;
|
|
160
|
+ /**
|
|
161
|
+ * Tries to move the given user from inactive list to active list
|
|
162
|
+ *
|
|
163
|
+ * @param user The user to move
|
|
164
|
+ */
|
|
165
|
+ private void moveUserToActiveList(PeerUser user) {
|
|
166
|
+ final int id = user.id;
|
|
167
|
+ if (!inactiveUsers.containsKey(id)) {
|
|
168
|
+ if (activeUsers.containsKey(id)) {
|
|
169
|
+ Log.w(getClass().getSimpleName(), "Tried to set state " + PeerUser.State.CONNECTED + " on an already connected user: user id " + id);
|
|
170
|
+ } else {
|
|
171
|
+ Log.w(getClass().getSimpleName(), "Tried to set state " + PeerUser.State.CONNECTED + " on an unknown user: user id " + id);
|
|
172
|
+ }
|
|
173
|
+ return;
|
|
174
|
+ }
|
|
175
|
+ inactiveUsers.remove(user.id);
|
|
176
|
+ activeUsers.put(user.id, user);
|
|
177
|
+ }
|
96
|
178
|
|
|
179
|
+ /**
|
|
180
|
+ * Tries to move the given user from active list to inactive list
|
|
181
|
+ *
|
|
182
|
+ * @param user The user to move
|
|
183
|
+ */
|
|
184
|
+ private void moveUserToInactiveList(PeerUser user) {
|
|
185
|
+ final int id = user.id;
|
|
186
|
+ if (!activeUsers.containsKey(id)) {
|
|
187
|
+ if (inactiveUsers.containsKey(id)) {
|
|
188
|
+ Log.w(getClass().getSimpleName(), "Tried to set state " + PeerUser.State.DISCONNECTED + " on an already disconnected user: user id " + id);
|
|
189
|
+ } else {
|
|
190
|
+ Log.w(getClass().getSimpleName(), "Tried to set state " + PeerUser.State.DISCONNECTED + " on an unknown user: user id " + id);
|
|
191
|
+ }
|
|
192
|
+ return;
|
|
193
|
+ }
|
|
194
|
+ activeUsers.remove(id);
|
|
195
|
+ inactiveUsers.put(id, user);
|
|
196
|
+ }
|
|
197
|
+
|
|
198
|
+ /**
|
|
199
|
+ * Move user from active or inactive list when its state changes.
|
|
200
|
+ *
|
|
201
|
+ * @param user The user which changed
|
|
202
|
+ * @param evt The change event
|
|
203
|
+ */
|
|
204
|
+ private void userChangeObserver(PeerUser user, PropertyChangeEvent evt) {
|
97
|
205
|
if (evt.getPropertyName().equals("state")) {
|
98
|
206
|
PeerUser.State oldState = (PeerUser.State) evt.getOldValue();
|
99
|
207
|
PeerUser.State newState = (PeerUser.State) evt.getNewValue();
|
100
|
208
|
|
101
|
|
- if ((oldState == PeerUser.State.DISCONNECTED || oldState == PeerUser.State.CONNECTING) &&
|
102
|
|
- newState == PeerUser.State.CONNECTED) {
|
103
|
|
- // Connection handling
|
104
|
|
- if (!inactiveUsers.containsKey(id)) {
|
105
|
|
- if (activeUsers.containsKey(id)) {
|
106
|
|
- Log.w(getClass().getSimpleName(), "Tried to set state CONNECTED on an already connected user: user id " + id);
|
107
|
|
- } else {
|
108
|
|
- Log.w(getClass().getSimpleName(), "Tried to set state CONNECTED on an unknown user: user id " + id);
|
109
|
|
- }
|
110
|
|
- return;
|
111
|
|
- }
|
112
|
|
-
|
113
|
|
- inactiveUsers.remove(id);
|
114
|
|
- activeUsers.put(id, user);
|
115
|
|
- Log.v(getClass().getSimpleName(), "State of user " + id + " updated from " +
|
116
|
|
- oldState.toString() + " to " + newState.toString());
|
|
209
|
+ if ((oldState == PeerUser.State.DISCONNECTED ||
|
|
210
|
+ (oldState == PeerUser.State.CONNECTING) && newState == PeerUser.State.CONNECTED)) {
|
|
211
|
+ moveUserToActiveList(user);
|
117
|
212
|
} else if (oldState == PeerUser.State.CONNECTED &&
|
118
|
213
|
(newState == PeerUser.State.DISCONNECTED || newState == PeerUser.State.CONNECTING)) {
|
119
|
|
- // Disconnection
|
120
|
|
- if (!activeUsers.containsKey(id)) {
|
121
|
|
- if (inactiveUsers.containsKey(id)) {
|
122
|
|
- Log.w(getClass().getSimpleName(), "Tried to set state DISCONNECTED on an already disconnected user: user id " + id);
|
123
|
|
- } else {
|
124
|
|
- Log.w(getClass().getSimpleName(), "Tried to set state DISCONNECTED on an unknown user: user id " + id);
|
125
|
|
- }
|
126
|
|
- return;
|
127
|
|
- }
|
128
|
|
-
|
129
|
|
- activeUsers.remove(id);
|
130
|
|
- inactiveUsers.put(id, user);
|
131
|
|
- Log.v(getClass().getSimpleName(), "State of user " + id + " updated from " +
|
132
|
|
- oldState.toString() + " to " + newState.toString());
|
|
214
|
+ moveUserToInactiveList(user);
|
133
|
215
|
}
|
|
216
|
+ Log.v(getClass().getSimpleName(), "State of user " + user.id + " updated from " +
|
|
217
|
+ oldState.toString() + " to " + newState.toString());
|
134
|
218
|
}
|
135
|
219
|
}
|
136
|
220
|
|
137
|
|
- public void addNewUserObserver(UserConnectionCallback connectionCallback) {
|
138
|
|
- newUsersObservers.add(connectionCallback);
|
139
|
|
- }
|
140
|
|
-
|
141
|
|
- private void notifyNewUserObservers(PeerUser user) {
|
142
|
|
- newUsersObservers.forEach(callback -> callback.onUserConnected(user));
|
143
|
|
- }
|
144
|
|
-
|
145
|
221
|
/**
|
146
|
222
|
* Tests locally if a username is available
|
147
|
223
|
*
|
|
@@ -155,7 +231,7 @@ public class UserList {
|
155
|
231
|
}
|
156
|
232
|
|
157
|
233
|
/**
|
158
|
|
- * Tell all active users that our username changed
|
|
234
|
+ * Tells all active users that our username changed
|
159
|
235
|
*/
|
160
|
236
|
public void propagateUsernameChange() {
|
161
|
237
|
activeUsers.forEach((id, user) -> {
|
|
@@ -164,13 +240,13 @@ public class UserList {
|
164
|
240
|
}
|
165
|
241
|
|
166
|
242
|
/**
|
167
|
|
- * Close all running threads, sockets and db connection
|
168
|
|
- * Must be called before exiting the app
|
|
243
|
+ * Closes all running threads, sockets and db connection.
|
|
244
|
+ * Must be called before exiting the app.
|
169
|
245
|
*/
|
170
|
246
|
public void destroy() {
|
171
|
247
|
netDiscoverer.stopDiscovery();
|
172
|
248
|
connectionListener.stopAccepting();
|
173
|
|
-// db.close();
|
|
249
|
+ db.close();
|
174
|
250
|
for (PeerUser user : activeUsers.values()) {
|
175
|
251
|
user.disconnect();
|
176
|
252
|
}
|
|
@@ -180,4 +256,7 @@ public class UserList {
|
180
|
256
|
void onUserConnected(PeerUser user);
|
181
|
257
|
}
|
182
|
258
|
|
|
259
|
+ public interface UserListLoadedCallback {
|
|
260
|
+ void onLoaded();
|
|
261
|
+ }
|
183
|
262
|
}
|