Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
86 Kevin 1
package org.vt.ece4564.latmb;
2
 
3
import java.util.ArrayList;
4
import java.util.Calendar;
5
import java.util.GregorianCalendar;
6
import java.util.List;
7
 
8
import org.jboss.netty.channel.Channel;
9
import org.vt.ece4564.latmb.LATMBProtocol.Message;
10
import org.vt.ece4564.latmb.LATMBProtocol.Message.Builder;
11
import org.vt.ece4564.latmb.LATMBProtocol.Position;
12
import org.vt.ece4564.latmb.LATMBProtocol.TrackingMessage;
13
import org.vt.ece4564.latmb.LATMBProtocol.DateTime;
14
 
15
import android.app.Activity;
16
import android.content.Intent;
17
import android.content.SharedPreferences;
18
import android.location.Location;
19
import android.location.LocationListener;
20
import android.location.LocationManager;
21
import android.location.LocationProvider;
22
import android.os.Bundle;
23
import android.os.Handler;
24
import android.preference.PreferenceManager;
25
import android.util.Log;
26
import android.view.Menu;
27
import android.view.MenuInflater;
28
import android.view.MenuItem;
29
import android.view.View;
30
import android.view.View.OnClickListener;
31
import android.view.ViewGroup;
32
import android.widget.Button;
33
import android.widget.EditText;
34
import android.widget.TextView;
35
import android.widget.Toast;
36
 
37
public class MessageBoardActivity extends Activity implements OnClickListener, 
38
		LocationListener, NetworkMessageHandler<TrackingMessage> {
39
 
40
	private static final String TAG = MessageBoardActivity.class.getName();
41
 
42
	private EditText newEntry_;
43
	private Button submit_;
44
	private TextView status_;
45
	private ViewGroup messageContainer_;
46
 
47
	private String server_;
48
	private int port_;
49
	private String username_;
50
	private String chatroom_;
51
	private boolean useGPS_;
52
	private boolean gpsAcquired_ = false;
53
	private boolean gpsChanged_ = false;
54
 
55
	private Handler handler_ = new Handler();
56
 
57
	private Boolean connected_ = false;
58
	private long reconnectInterval_ = 5000;
59
	private boolean autoReconnect_ = false;
60
 
61
	private double latitude_ = 0;
62
	private double longitude_ = 0;
63
	private double radius_ = 0;
64
 
65
	private LocationManager locationManager_;
66
 
67
	private ClientHandler<LATMBProtocol.TrackingMessage> msgHandler_;
68
 
69
	// List of messages stored locally
70
	private static List<LATMBMessage> storedMessages_ = new ArrayList<LATMBMessage>();
71
 
72
	@Override
73
	public boolean onCreateOptionsMenu(Menu menu) {
74
		// Create the menu
75
	    MenuInflater inflater = getMenuInflater();
76
	    inflater.inflate(R.menu.messagemenu, menu);
77
	    return true;
78
	}
79
 
80
	@Override
81
	public boolean onOptionsItemSelected(MenuItem item) {
82
	    // Handle item selection
83
	    switch (item.getItemId()) {
84
	    case R.id.menuPreferences:
85
	    	startActivity(new Intent(this, PreferenceActivity.class));
86
	        return true;
87
	    case R.id.menuDisconnect:
88
	    	disconnectFromServer();
89
	        return true;
90
	    case R.id.menuChangeChatroom:
91
	    	finish();
92
	    	return true;
93
	    case R.id.menuRefresh:
94
	    	storedMessages_.clear();
95
	    	getInitialMessages();
96
	    	return true;
97
	    default:
98
	        return super.onOptionsItemSelected(item);
99
	    }
100
	}
101
 
102
	@Override
103
	protected void onCreate(Bundle savedInstanceState) {
104
		super.onCreate(savedInstanceState);
105
 
106
		setContentView(R.layout.messageboard);
107
 
108
		newEntry_ = (EditText) findViewById(R.id.editMessage);
109
		submit_ = (Button) findViewById(R.id.buttonSubmit);
110
		status_ = (TextView) findViewById(R.id.textStatus);
111
		messageContainer_ = (ViewGroup) findViewById(R.id.messageList);
112
 
113
		Intent i = getIntent();
114
		server_ = i.getStringExtra("server");
115
		port_ = Integer.parseInt(i.getStringExtra("port"));
116
		username_ = i.getStringExtra("username");
117
		chatroom_ = i.getStringExtra("chatroom");
118
		useGPS_ = i.getBooleanExtra("gps", false);
119
		radius_ = i.getDoubleExtra("radius", 1);
120
 
121
		submit_.setOnClickListener(this);
122
 
123
		locationManager_ = (LocationManager) getSystemService(LOCATION_SERVICE);
124
	}
125
 
126
	@Override
127
	protected void onPause() {
128
		super.onPause();
129
 
130
		// Stop the GPS updater if location is used
131
		if (useGPS_) {
132
			locationManager_.removeUpdates(this);
133
		}
134
		stopAutoReconnect();
135
		disconnectFromServer();
136
	}
137
 
138
	@Override
139
	protected void onResume() {
140
		super.onResume();
141
 
142
		// Start the GPS updater if location is used
143
		if (useGPS_) {
144
			locationManager_.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
145
		}
146
		startAutoReconnect();
147
	}
148
 
149
	@Override
150
	public void onClick(View arg0) {
151
		// Send message if client is connected to server and location is acquired (only if gps is used)
152
		if ((connected_ && !useGPS_) || (connected_ && gpsAcquired_)) {
153
			SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
154
 
155
			String text = newEntry_.getText().toString();
156
 
157
			Calendar c = Calendar.getInstance();
158
			DateTime timestamp = DateTime.newBuilder()
159
					.setYear(c.get(Calendar.YEAR))
160
					.setMonth(c.get(Calendar.MONTH))
161
					.setDay(c.get(Calendar.DAY_OF_MONTH))
162
					.setHour(c.get(Calendar.HOUR_OF_DAY))
163
					.setMinute(c.get(Calendar.MINUTE))
164
					.setSecond(c.get(Calendar.SECOND))
165
					.build();
166
 
167
			// Get the expiration time from saved preferences
168
			int exp_days = Integer.parseInt(sharedPrefs.getString("pref_MessageExpiration_Days", "1"));
169
			int exp_months = Integer.parseInt(sharedPrefs.getString("pref_MessageExpiration_Months", "0"));
170
			int exp_hours = Integer.parseInt(sharedPrefs.getString("pref_MessageExpiration_Hours", "0"));
171
			int exp_minutes = Integer.parseInt(sharedPrefs.getString("pref_MessageExpiration_Minutes", "0"));
172
 
173
			c.add(Calendar.DAY_OF_MONTH, exp_days);
174
			c.add(Calendar.MONTH, exp_months);
175
			c.add(Calendar.HOUR_OF_DAY, exp_hours);
176
			c.add(Calendar.MINUTE, exp_minutes);
177
 
178
			DateTime expiration = DateTime.newBuilder()
179
					.setYear(c.get(Calendar.YEAR))
180
					.setMonth(c.get(Calendar.MONTH))
181
					.setDay(c.get(Calendar.DAY_OF_MONTH))
182
					.setHour(c.get(Calendar.HOUR_OF_DAY))
183
					.setMinute(c.get(Calendar.MINUTE))
184
					.setSecond(c.get(Calendar.SECOND))
185
					.build();
186
 
187
			Builder message = Message.newBuilder()
188
					.setMessage(text)
189
					.setTimestamp(timestamp)
190
					.setExpiration(expiration);
191
 
192
			if (!username_.isEmpty()) {
193
				message.setUsername(username_);
194
			}
195
			if (!useGPS_) {
196
				message.setChatroom(chatroom_);
197
			} else {
198
				Position pos = Position.newBuilder()
199
						.setLatitude(latitude_)
200
						.setLongitude(longitude_)
201
						.setAccuracy(0)
202
						.build();
203
				message.setRadius(radius_);
204
				message.setCoordinates(pos);
205
			}
206
 
207
			TrackingMessage msg = TrackingMessage.newBuilder()
208
					// Type 1 == New Message
209
					.setId(0).setType(1)
210
					.addMessage(message)
211
					.build();
212
 
213
			// Send the message to the server
214
			msgHandler_.send(msg);
215
 
216
			newEntry_.setText("");
217
		}
218
	}
219
 
220
	private boolean connectToServer(String server, int port) {
221
		try {
222
			synchronized (connected_) {
223
				if (connected_)
224
					return true;
225
 
226
				updateStatus("Connecting to server...");
227
 
228
				msgHandler_ = LATMBClient.connect(server, port);
229
				msgHandler_.setListener(this);
230
				connected_ = true;
231
 
232
				// Remove all currently saved messages
233
				storedMessages_.clear();
234
 
235
				if (!useGPS_) {
236
					getInitialMessages();
237
				}
238
				// Start the thread to check for expired messages
239
				startUpdaterThread();
240
 
241
				updateStatus("Connected to server [" + server + ":" + port + "]");
242
			}
243
			return true;
244
		} catch (Exception e) {
245
			Log.e(TAG, "Unable to connect to server", e);
246
			return false;
247
		}
248
 
249
	}
250
 
251
	private class ConnectWorker implements Runnable {
252
 
253
		public void run() {
254
			doConnect();
255
		}
256
 
257
		public void doConnect() {
258
			try {
259
				while (autoReconnect_ && !connectToServer(server_, port_)) {
260
					updateStatus("Attempting to reconnect to server...");
261
					Thread.currentThread().sleep(reconnectInterval_);
262
				}
263
			} catch (Exception e) {
264
				doConnect();
265
			}
266
		}
267
	}
268
 
269
	private void startAutoReconnect() {
270
		autoReconnect_ = true;
271
		ConnectWorker w = new ConnectWorker();
272
		Thread t = new Thread(w);
273
		t.start();
274
 
275
	}
276
 
277
	private void stopAutoReconnect() {
278
		autoReconnect_ = false;
279
	}
280
 
281
	private void disconnectFromServer() {
282
		synchronized (connected_) {
283
			connected_ = false;
284
			msgHandler_.disconnect();
285
		}
286
	}
287
 
288
	private void updateStatus(final String status) {
289
		handler_.post(new Runnable() {
290
 
291
			@Override
292
			public void run() {
293
				status_.setText(status);
294
			}
295
		});
296
	}
297
 
298
	private void startUpdaterThread() {
299
		Thread t = new Thread(new Runnable() {
300
 
301
			@Override
302
			public void run() {
303
				while (true) {
304
					try {
305
						// If a new GPS coordinate is set, pull new list of messages
306
						if (gpsChanged_ && gpsAcquired_) {
307
							storedMessages_.clear();
308
							getInitialMessages();
309
							gpsChanged_ = false;
310
						}
311
						// Refresh the display
312
						updateMessageUI();
313
						Thread.currentThread().sleep(5000);
314
					} catch (Exception e) {
315
						e.printStackTrace();
316
					}
317
				}
318
			}
319
		});
320
		t.start();
321
	}
322
 
323
	private void getInitialMessages() {
324
		Runnable r = new Runnable() {
325
 
326
			@Override
327
			public void run() {
328
				if (connected_) {
329
					Calendar c = Calendar.getInstance();
330
					DateTime timestamp = DateTime.newBuilder()
331
							.setYear(c.get(Calendar.YEAR))
332
							.setMonth(c.get(Calendar.MONTH))
333
							.setDay(c.get(Calendar.DAY_OF_MONTH))
334
							.setHour(c.get(Calendar.HOUR_OF_DAY))
335
							.setMinute(c.get(Calendar.MINUTE))
336
							.setSecond(c.get(Calendar.SECOND))
337
							.build();
338
 
339
					Builder message = Message.newBuilder()
340
							.setMessage("")
341
							.setTimestamp(timestamp)
342
							.setExpiration(timestamp);
343
 
344
					if (!username_.isEmpty()) {
345
						message.setUsername(username_);
346
					}
347
					if (!useGPS_) {
348
						message.setChatroom(chatroom_);
349
					} else {
350
						Position pos = Position.newBuilder()
351
								.setLatitude(latitude_)
352
								.setLongitude(longitude_)
353
								.setAccuracy(0)
354
								.build();
355
						message.setRadius(radius_);
356
						message.setCoordinates(pos);
357
					}
358
 
359
					TrackingMessage msg = TrackingMessage.newBuilder()
360
							// Type 0 == Initial Message
361
							.setId(0).setType(0)
362
							.addMessage(message)
363
							.build();
364
 
365
					msgHandler_.send(msg);
366
				}
367
			}
368
		};
369
		handler_.post(r);
370
	}
371
 
372
	@Override
373
	public void onLocationChanged(Location location) {
374
		latitude_ = location.getLatitude();
375
		longitude_ = location.getLongitude();
376
		if (latitude_ != 0 && longitude_ != 0) {
377
			gpsChanged_ = true;
378
			gpsAcquired_ = true;
379
		}
380
	}
381
 
382
	@Override
383
	public void onProviderDisabled(String arg0) {
384
		updateStatus("GPS is disabled");
385
		Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
386
		startActivity(intent);
387
	}
388
 
389
	@Override
390
	public void onProviderEnabled(String arg0) {
391
		updateStatus("GPS is enabled");
392
	}
393
 
394
	@Override
395
	public void onStatusChanged(String provider, int status, Bundle bundle) {
396
		switch (status) {
397
		case LocationProvider.OUT_OF_SERVICE:
398
			Toast.makeText(this, "GPS Status Changed: Out of Service", Toast.LENGTH_SHORT).show();
399
			break;
400
		case LocationProvider.TEMPORARILY_UNAVAILABLE:
401
			Toast.makeText(this, "GPS Status Changed: Temporarily Unavailable", Toast.LENGTH_SHORT).show();
402
			break;
403
		case LocationProvider.AVAILABLE:
404
			Toast.makeText(this, "GPS Status Changed: Available", Toast.LENGTH_SHORT).show();
405
			break;
406
		}
407
 
408
	}
409
 
410
 
411
	@Override
412
	public void received(final TrackingMessage msg) {
413
		Runnable r = new Runnable() {
414
 
415
			@Override
416
			public void run() {
417
 
418
				// Create a LATMBMessage from the TrackingMessage and store it
419
				List<Message> messages = msg.getMessageList();
420
 
421
				for (int i = 0; i < messages.size(); i++) {
422
					Message message = messages.get(i);
423
 
424
					DateTime timestamp = message.getTimestamp();
425
					DateTime expiration = message.getExpiration();
426
 
427
					LATMBMessage newEntry = new LATMBMessage();
428
 
429
					newEntry.setMessage(message.getMessage());
430
 
431
					Calendar time = new GregorianCalendar();
432
					time.set(timestamp.getYear(), timestamp.getMonth(), timestamp.getDay(), 
433
							timestamp.getHour(), timestamp.getMinute(), timestamp.getSecond());
434
					newEntry.setTimestamp(time);
435
 
436
					Calendar exp = new GregorianCalendar();
437
					exp.set(expiration.getYear(), expiration.getMonth(), expiration.getDay(), 
438
							expiration.getHour(), expiration.getMinute(), expiration.getSecond());
439
					newEntry.setExpiration(exp);
440
 
441
					if (message.hasUsername()) {
442
						newEntry.setUsername(message.getUsername());
443
					} else {
444
						newEntry.setUsername("Anonymous");
445
					}
446
 
447
					if (message.hasChatroom()) {
448
						newEntry.setChatroom(message.getChatroom());
449
					}
450
 
451
					if (message.hasCoordinates()) {
452
						Position coord = message.getCoordinates();
453
						newEntry.setLatitude(coord.getLatitude());
454
						newEntry.setLongitude(coord.getLongitude());
455
					}
456
 
457
					if (message.hasRadius()) {
458
						newEntry.setRadius(message.getRadius());
459
					}
460
 
461
					storedMessages_.add(newEntry);
462
				}
463
			}
464
		};
465
		handler_.post(r);
466
		updateMessageUI();
467
	}
468
 
469
	// Updates the GUI to show stored entries
470
	public void updateMessageUI() {
471
		Runnable r = new Runnable() {
472
 
473
			@Override
474
			public void run() {
475
				if (connected_) {
476
					// Show the current chatroom or the current GPS coordinates
477
					if (!useGPS_) {
478
						updateStatus("Current chatroom: " + chatroom_);
479
					} else {
480
						if (!gpsAcquired_) {
481
							updateStatus("Waiting for location...");
482
						} else {
483
							updateStatus("Using location (" + latitude_ + "," + longitude_ + ")");
484
						}
485
					}
486
				}
487
 
488
				removeExpiredMessages();
489
 
490
				messageContainer_.removeAllViews();
491
 
492
				// Print each message
493
				for (LATMBMessage message : storedMessages_) {
494
					Calendar time = message.getTimestamp();
495
					String msg = String.format("%d/%d/%d @ %d:%d:%d ~ %s\n>> %s",
496
							time.get(Calendar.DAY_OF_MONTH), time.get(Calendar.MONTH),
497
							time.get(Calendar.YEAR), time.get(Calendar.HOUR_OF_DAY),
498
							time.get(Calendar.MINUTE), time.get(Calendar.SECOND),
499
							message.getUsername(), message.getMessage());
500
					TextView v = new TextView(MessageBoardActivity.this);
501
					v.setText(msg);
502
					messageContainer_.addView(v);
503
				}
504
			}	
505
		};
506
		handler_.post(r);
507
	}
508
 
509
	// Removes all stored expired messages
510
	public void removeExpiredMessages() {
511
		Runnable r = new Runnable() {
512
 
513
			@Override
514
			public void run() {
515
				List<LATMBMessage> toRemove = new ArrayList<LATMBMessage>();
516
				for (LATMBMessage message : storedMessages_) {
517
					Calendar c = Calendar.getInstance();
518
					if (c.after(message.getExpiration())) {
519
						toRemove.add(message);
520
					}
521
				}
522
				for (LATMBMessage message : toRemove) {
523
					storedMessages_.remove(message);
524
				}
525
			}
526
		};
527
		handler_.post(r);
528
	}
529
 
530
	@Override
531
	public void channelException(Throwable e) {
532
		updateStatus("Exception occured connecting to server");
533
		connected_ = false;
534
	}
535
 
536
	@Override
537
	public void channelClosed(Channel c) {
538
		updateStatus("Connection to server closed");
539
		connected_ = false;
540
	}
541
 
542
}