/[projects]/android/TrainInfo/src/dk/thoerup/traininfo/DepartureList.java
ViewVC logotype

Contents of /android/TrainInfo/src/dk/thoerup/traininfo/DepartureList.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1798 - (show annotations) (download)
Wed Apr 18 17:38:54 2012 UTC (12 years ago) by torben
File size: 21069 byte(s)
Avoid NullPointerException when trying to load metro data from a saved instance
1 package dk.thoerup.traininfo;
2
3 import static dk.thoerup.traininfo.R.string.departurelist_fetcharrivals;
4 import static dk.thoerup.traininfo.R.string.departurelist_fetchdepartures;
5 import static dk.thoerup.traininfo.R.string.generic_cancel;
6 import static dk.thoerup.traininfo.R.string.generic_retry;
7
8 import java.text.NumberFormat;
9
10 import android.app.AlertDialog;
11 import android.app.Dialog;
12 import android.app.ListActivity;
13 import android.app.ProgressDialog;
14 import android.content.ActivityNotFoundException;
15 import android.content.DialogInterface;
16 import android.content.Intent;
17 import android.content.SharedPreferences;
18 import android.graphics.Typeface;
19 import android.net.Uri;
20 import android.os.AsyncTask;
21 import android.os.Bundle;
22 import android.preference.PreferenceManager;
23 import android.util.Log;
24 import android.view.Menu;
25 import android.view.MenuItem;
26 import android.view.View;
27 import android.view.View.OnClickListener;
28 import android.widget.Button;
29 import android.widget.ListView;
30 import android.widget.TableLayout;
31 import android.widget.TableRow;
32 import android.widget.TextView;
33 import android.widget.Toast;
34 import dk.thoerup.android.traininfo.common.DepartureBean;
35 import dk.thoerup.android.traininfo.common.DepartureEntry;
36 import dk.thoerup.android.traininfo.common.MetroBean;
37 import dk.thoerup.android.traininfo.common.MetroBean.MetroEntry;
38 import dk.thoerup.android.traininfo.common.StationEntry;
39 import dk.thoerup.traininfo.provider.DepartureProvider;
40 import dk.thoerup.traininfo.provider.MetroProvider;
41 import dk.thoerup.traininfo.provider.ProviderFactory;
42
43 import dk.thoerup.traininfo.util.FavoritesHelper;
44 import dk.thoerup.traininfo.util.MessageBox;
45 import dk.thoerup.traininfo.util.StationEntryCsv;
46
47 public class DepartureList extends ListActivity {
48
49 public static final int DLG_PROGRESS = 1;
50 static final int MENU_MAP = 100;
51 static final int MENU_NOTIFICATIONS = 101;
52 static final int MENU_METROMAP = 102;
53 static final int MENU_TOGGLEDETAILS= 103;
54
55 static final int MENU_FAVORITES_ADD = 104;
56 static final int MENU_FAVORITES_REMOVE = 105;
57
58
59 DepartureListAdapter adapter;
60 DepartureProvider provider;
61 DepartureBean departures;
62
63 MetroBean metroBean;
64 MetroProvider metro;
65
66 int selectedItemId;
67
68 FavoritesHelper favorites;
69
70 //DepartureBean currentDeparture;
71
72 ProgressDialog pgDialog;
73
74 DepartureFetcher fetcher;
75 MetroFetcher metroFetcher;
76
77 StationEntry station;
78
79 String trainType = "REGIONAL";
80
81 boolean arrival = false;
82
83 int commFailCounter = 0;
84
85 @Override
86 protected void onCreate(Bundle savedInstanceState) {
87 super.onCreate(savedInstanceState);
88 setContentView(R.layout.departurelist);
89
90 favorites = new FavoritesHelper(this);
91
92 adapter = new DepartureListAdapter(this);
93 setListAdapter(adapter);
94
95 Intent launchedBy = getIntent();
96
97 station = (StationEntry) launchedBy.getSerializableExtra("stationbean");
98
99 ((TextView) findViewById(R.id.stationName)).setText( station.getName() );
100
101
102 ((TextView) findViewById(R.id.stationAddr)).setText( station.getAddress() );
103
104 final Button departureBtn = (Button) findViewById(R.id.departurebtn);
105 final Button arrivalBtn = (Button) findViewById(R.id.arrivalbtn);
106 final Button metroBtn = (Button) findViewById(R.id.metrobtn);
107 final Button regionalBtn = (Button) findViewById(R.id.regionalbtn);
108 final Button stogBtn = (Button) findViewById(R.id.stogbtn);
109
110 final View metroView = findViewById(R.id.metroonly);
111
112 departureBtn.setOnClickListener( new OnClickListener() {
113 @Override
114 public void onClick(View arg0) {
115 arrivalBtn.setBackgroundResource(R.drawable.custom_button);
116 departureBtn.setBackgroundResource(R.drawable.custom_button_hilight);
117 metroBtn.setBackgroundResource(R.drawable.custom_button);
118
119 getListView().setVisibility( View.VISIBLE );
120 metroView.setVisibility( View.GONE );
121 arrival = false;
122 startDepartureFetcher();
123 }
124 });
125 arrivalBtn.setOnClickListener( new OnClickListener() {
126 @Override
127 public void onClick(View arg0) {
128 arrivalBtn.setBackgroundResource(R.drawable.custom_button_hilight);
129 departureBtn.setBackgroundResource(R.drawable.custom_button);
130 metroBtn.setBackgroundResource(R.drawable.custom_button);
131
132 getListView().setVisibility( View.VISIBLE );
133 metroView.setVisibility( View.GONE );
134 arrival = true;
135 startDepartureFetcher();
136 }
137 });
138
139 regionalBtn.setOnClickListener( new OnClickListener() {
140 @Override
141 public void onClick(View arg0) {
142 regionalBtn.setBackgroundResource(R.drawable.custom_button_hilight);
143 stogBtn.setBackgroundResource(R.drawable.custom_button);
144 metroBtn.setBackgroundResource(R.drawable.custom_button);
145
146 departureBtn.setVisibility( View.VISIBLE );
147 arrivalBtn.setVisibility( View.VISIBLE );
148
149 getListView().setVisibility( View.VISIBLE );
150 metroView.setVisibility( View.GONE );
151 trainType = "REGIONAL";
152 startDepartureFetcher();
153 }
154 });
155 stogBtn.setOnClickListener( new OnClickListener() {
156 @Override
157 public void onClick(View arg0) {
158 regionalBtn.setBackgroundResource(R.drawable.custom_button);
159 stogBtn.setBackgroundResource(R.drawable.custom_button_hilight);
160 metroBtn.setBackgroundResource(R.drawable.custom_button);
161
162
163 departureBtn.setVisibility( View.VISIBLE );
164 arrivalBtn.setVisibility( View.VISIBLE );
165
166 getListView().setVisibility( View.VISIBLE );
167 metroView.setVisibility( View.GONE );
168 trainType = "STOG";
169 startDepartureFetcher();
170 }
171 });
172
173
174
175 metroBtn.setOnClickListener( new OnClickListener() {
176 @Override
177 public void onClick(View v) {
178 regionalBtn.setBackgroundResource(R.drawable.custom_button);
179 stogBtn.setBackgroundResource(R.drawable.custom_button);
180 metroBtn.setBackgroundResource(R.drawable.custom_button_hilight);
181
182 departureBtn.setVisibility( View.GONE );
183 arrivalBtn.setVisibility( View.GONE );
184
185 getListView().setVisibility( View.GONE );
186 metroView.setVisibility( View.VISIBLE );
187 startMetroFetcher();
188 }
189 });
190
191
192
193
194 // findViewById(R.id.header).setOnClickListener( mapLauncher );
195
196 int distance = station.getCalcdist();
197 if (distance != 0) {
198 NumberFormat format = NumberFormat.getNumberInstance();
199 format.setMaximumFractionDigits(1);
200 format.setMinimumFractionDigits(1);
201
202 ((TextView) findViewById(R.id.stationDistance)).setText( format.format((double)distance/1000.0) + " km." );
203 } else {
204 ((TextView) findViewById(R.id.stationDistance)).setVisibility(View.GONE);
205 }
206
207 ProviderFactory.purgeOldEntries(); //cleanup before fetching more data
208
209 Log.e("Station", StationEntryCsv.toCSV(station) );
210
211
212
213 if (station.isMetro() == false) {
214 metroBtn.setVisibility( View.GONE );
215 }
216
217 metro = ProviderFactory.getMetroProvider();
218
219 if (station.isRegional() == false ) {
220 regionalBtn.setVisibility(View.GONE);
221 }
222
223 if (station.isStrain() == false ) {
224 stogBtn.setVisibility(View.GONE);
225 }
226
227 if (station.isRegional() == true && station.isStrain() == false ) {
228 if ( station.isMetro() == false )
229 regionalBtn.setVisibility(View.GONE);
230 trainType = "REGIONAL";
231 }
232
233 if (station.isRegional() == false && station.isStrain() == true) {
234 if (station.isMetro() == false)
235 stogBtn.setVisibility(View.GONE);
236
237 stogBtn.setBackgroundResource(R.drawable.custom_button_hilight);
238 trainType = "STOG";
239
240 }
241 //Both enabled - use preferred from preferences
242 if (station.isRegional() == true && station.isStrain() == true ) {
243 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
244 trainType = prefs.getString("traintype", "REGIONAL"); //default value is gps
245
246 if (trainType.equals("STOG") ) {
247 stogBtn.setBackgroundResource(R.drawable.custom_button_hilight);
248 regionalBtn.setBackgroundResource(R.drawable.custom_button);
249 }
250 }
251
252
253 if (station.isRegional() == false && station.isStrain() == false) {
254 getListView().setVisibility( View.GONE );
255 metroView.setVisibility( View.VISIBLE );
256
257 departureBtn.setVisibility( View.GONE );
258 arrivalBtn.setVisibility(View.GONE);
259 metroBtn.setVisibility( View.GONE );
260
261
262
263 if (savedInstanceState == null) {
264 startMetroFetcher();
265 } else {
266 metroBean = (MetroBean) savedInstanceState.getSerializable("metro");
267 if (metroBean != null) { // Avoid reported NPE
268 loadMetroData();
269 } else {
270 startMetroFetcher();
271 }
272 }
273
274 } else {
275 provider = ProviderFactory.getDepartureProvider();
276
277 if (savedInstanceState == null) {
278 startDepartureFetcher();
279 } else {
280 departures = (DepartureBean) savedInstanceState.getSerializable("departures");
281
282 if ( (departures != null) && (departures.entries != null) ) {
283 adapter.setDepartures(departures.entries);
284 }
285 selectedItemId = savedInstanceState.getInt("selectedItemId");
286
287 if ( hasNotifications() ) {
288 findViewById(R.id.notifIcon).setVisibility(View.VISIBLE);
289 }
290
291 }
292 }
293 }
294
295
296
297 boolean hasNotifications() {
298 return (departures != null && departures.notifications.size() > 0);
299 }
300
301 @Override
302 public void onSaveInstanceState(Bundle outState)
303 {
304 if (pgDialog != null && pgDialog.isShowing())
305 dismissDialog(DLG_PROGRESS);
306
307 outState.putInt("selectedItemId", selectedItemId);
308
309 outState.putSerializable("departures", departures);
310 outState.putSerializable("metro", metroBean);
311 }
312
313
314
315 @Override
316 protected void onDestroy() {
317 super.onDestroy();
318
319 if (fetcher != null) {
320 fetcher.cancel(true);
321 }
322
323 if (metroFetcher != null) {
324 metroFetcher.cancel(true);
325 }
326 }
327
328 @Override
329 protected void onListItemClick(ListView l, View v, int position, long id) {
330 super.onListItemClick(l, v, position, id);
331
332 //how can this happen ??
333 if (departures == null || departures.entries == null || departures.entries.size() == 0) {
334 Toast.makeText(this, "No departures in list ?!?", Toast.LENGTH_LONG).show(); //TODO: translate
335 return;
336 }
337
338 selectedItemId = position;
339
340 DepartureEntry dep = departures.entries.get(selectedItemId);
341
342 Intent intent = new Intent(this, TimetableList.class);
343 intent.putExtra("departure", dep);
344
345 startActivity(intent);
346
347 }
348
349
350 @Override
351 protected void onPrepareDialog(int id, Dialog dialog) {
352 super.onPrepareDialog(id, dialog);
353
354 switch (id) {
355 case DLG_PROGRESS:
356 pgDialog = (ProgressDialog) dialog;
357 int messageId = arrival == false ? departurelist_fetchdepartures : departurelist_fetcharrivals;
358 pgDialog.setMessage( getString(messageId) );
359 break;
360 }
361 }
362
363 @Override
364 protected Dialog onCreateDialog(int id) {
365 switch (id) {
366 case DLG_PROGRESS:
367
368 ProgressDialog dlg = new ProgressDialog(this);
369 dlg.setCancelable(true);
370 return dlg;
371 default:
372 return super.onCreateDialog(id);
373 }
374 }
375
376
377
378 @Override
379 public boolean onPrepareOptionsMenu(Menu menu) {
380 super.onPrepareOptionsMenu(menu);
381
382 ///////////////////////
383
384 MenuItem item = menu.findItem( MENU_NOTIFICATIONS );
385 boolean notifEnabled = hasNotifications();
386 item.setEnabled(notifEnabled);
387
388 //////////////////////////
389
390
391
392
393 return true;
394 }
395
396 @Override
397 public boolean onCreateOptionsMenu(Menu menu) {
398 MenuItem item;
399
400
401 item = menu.add(0, MENU_TOGGLEDETAILS, 0, getString(R.string.departurelist_toggledetails));
402 item.setIcon(android.R.drawable.ic_menu_view);
403
404 item = menu.add(0, MENU_MAP, 0, getString(R.string.departurelist_showonmap) );
405 item.setIcon(android.R.drawable.ic_menu_mapmode);
406
407 item = menu.add(0, MENU_NOTIFICATIONS, 0, getString(R.string.departurelist_notifications) );
408 item.setIcon(android.R.drawable.ic_menu_info_details);
409
410 boolean notifEnabled = hasNotifications();
411 item.setEnabled(notifEnabled);
412
413 ///////////////////////////////////////
414 if ( ! favorites.hasFavorite( this.station.getId() ) ) {
415 item = menu.add(0, MENU_FAVORITES_ADD, 0, getString(dk.thoerup.traininfo.R.string.stationlist_addfavorite) );
416 } else {
417 item = menu.add(0, MENU_FAVORITES_REMOVE, 0, getString(dk.thoerup.traininfo.R.string.stationlist_removefavorite) );
418 }
419 item.setIcon(android.R.drawable.ic_menu_save);
420 /////////////////////////////
421
422 if (station.isMetro()) {
423 item = menu.add(0, MENU_METROMAP, 0, "Metro" ); //TODO:translate!?!
424 item.setIcon(android.R.drawable.ic_menu_mapmode);
425 }
426
427 return true;
428 }
429
430 @Override
431 public boolean onOptionsItemSelected(MenuItem item) {
432 boolean res = true;
433 switch(item.getItemId()) {
434 case MENU_MAP:
435 Uri uri = Uri.parse("geo:" + station.getLatitude() + "," + station.getLongitude() + "?z=16");
436
437 try {
438 startActivity( new Intent(Intent.ACTION_VIEW, uri));
439 } catch (ActivityNotFoundException anfe) {
440 Toast.makeText(this, "Could not launch google maps", Toast.LENGTH_LONG).show();
441 }
442
443 break;
444 case MENU_NOTIFICATIONS:
445 Intent i = new Intent(this,dk.thoerup.traininfo.NotificationList.class);
446 i.putExtra(NotificationList.EXTRA_NOTIFICATIONS, departures.notifications);
447 startActivity(i);
448 break;
449 case MENU_METROMAP:
450 Intent metroMap = new Intent(this,dk.thoerup.traininfo.MetroMap.class);
451 startActivity(metroMap);
452 break;
453 case MENU_TOGGLEDETAILS:
454 adapter.toggleShowDetails();
455 break;
456 case MENU_FAVORITES_ADD:
457 favorites.addFavorite( this.station.getId() );
458 Toast.makeText(this, getString(dk.thoerup.traininfo.R.string.stationlist_stationadded), Toast.LENGTH_SHORT).show();
459 break;
460 case MENU_FAVORITES_REMOVE:
461 favorites.removeFavorite( station.getId() );
462 Toast.makeText(this, getString(dk.thoerup.traininfo.R.string.stationlist_stationremoved), Toast.LENGTH_SHORT).show();
463 break;
464 default:
465 res = super.onOptionsItemSelected(item);
466 }
467 return res;
468 }
469
470 void startDepartureFetcher() {
471 showDialog(DLG_PROGRESS);
472 fetcher = new DepartureFetcher();
473 fetcher.execute(station.getId());
474 }
475
476 void startMetroFetcher() {
477 showDialog(DLG_PROGRESS);
478 metroFetcher = new MetroFetcher();
479 metroFetcher.execute(station.getId());
480 }
481
482 class DialogDismisser implements View.OnClickListener {
483
484 Dialog dlg;
485 public DialogDismisser(Dialog d) {
486 dlg = d;
487 }
488
489 @Override
490 public void onClick(View v) {
491 if (dlg.isShowing())
492 dlg.dismiss();
493 }
494 }
495
496 /*View.OnClickListener mapLauncher = new View.OnClickListener() {
497 @Override
498 public void onClick(View v) {
499 Uri uri = Uri.parse("geo:" + station.getLatitude() + "," + station.getLongitude());
500 startActivity( new Intent(Intent.ACTION_VIEW, uri));
501 }
502 };*/
503
504
505
506 class DepartureFetcher extends AsyncTask<Integer, Void, Void> {
507
508 @Override
509 protected void onPostExecute(Void result) {
510 super.onPostExecute(result);
511
512
513 pgDialog.dismiss();
514
515 if (departures != null && departures.errorCode == null) {
516 commFailCounter = 0;
517 DepartureList.this.getListView().setVisibility(View.GONE); //Experimental, inspired by http://osdir.com/ml/Android-Developers/2010-04/msg01198.html
518 adapter.setDepartures(departures.entries);
519 DepartureList.this.getListView().setVisibility(View.VISIBLE);
520
521
522 // handle notification icon.
523 View notifIcon = findViewById(R.id.notifIcon);
524 if ( hasNotifications() ) {
525 notifIcon.setVisibility(View.VISIBLE);
526 notifIcon.setClickable(true);
527 notifIcon.setOnClickListener( new View.OnClickListener() {
528 @Override
529 public void onClick(View v) {
530 Intent i = new Intent(DepartureList.this, dk.thoerup.traininfo.NotificationList.class);
531 i.putExtra(NotificationList.EXTRA_NOTIFICATIONS, departures.notifications);
532 startActivity(i);
533 }
534 });
535 } else {
536 notifIcon.setVisibility(View.INVISIBLE);
537 }
538
539 if (departures.entries.size() == 0) {
540 int msgId = (arrival==false) ? R.string.departurelist_nodepartures : R.string.departurelist_noarrivals;
541 MessageBox.showMessage(DepartureList.this, getString(msgId), false);
542 }
543 } else { // communication or parse error
544 commFailCounter++;
545 AlertDialog.Builder builder = new AlertDialog.Builder(DepartureList.this);
546
547 if (departures != null && departures.errorCode != null ) { //got an error xml back
548 commFailCounter = 10;
549 builder.setMessage( getString(R.string.no_backend) );
550 } else {
551 builder.setMessage( getString(R.string.departurelist_fetcherror) );
552 }
553 builder.setCancelable(true);
554
555 if (commFailCounter < 3) {
556 builder.setPositiveButton(getString(generic_retry), new DialogInterface.OnClickListener() {
557 public void onClick(DialogInterface dialog, int id) {
558 dialog.dismiss();
559 startDepartureFetcher();
560
561 }
562 });
563 }
564 builder.setNegativeButton(getString(generic_cancel), new DialogInterface.OnClickListener() {
565 public void onClick(DialogInterface dialog, int id) {
566 dialog.dismiss();
567 DepartureList.this.finish();
568 }
569 });
570
571 try { //TODO: is this still necessary after the 0.9.4.1 fix ?
572 builder.show();
573 } catch (android.view.WindowManager.BadTokenException e) {
574 Log.i("DepartureList", "BadTokenException"); // this can happen if the user switched away from this activity, while doInBackground was running
575 }
576 }
577 }
578
579 @Override
580 protected Void doInBackground(Integer... params) {
581 departures = provider.lookupDepartures(params[0], DepartureList.this.arrival, trainType);
582 return null;
583 }
584
585 }
586
587 public void loadMetroData() {
588 ((TextView) findViewById(R.id.operations)).setText( metroBean.operationInfo );
589 ((TextView) findViewById(R.id.plan)).setText( metroBean.plan );
590
591
592 TableLayout table = (TableLayout) findViewById(R.id.metrotable);
593 table.removeAllViews();
594
595 TableRow head = new TableRow(this);
596
597 TextView h1 = new TextView(this);
598 h1.setText("Metro");
599 h1.setTextSize(16);
600 h1.setTypeface( Typeface.defaultFromStyle(Typeface.BOLD));
601
602
603 TableRow.LayoutParams params = new TableRow.LayoutParams();
604 params.span = 2;
605 head.addView(h1, params);
606
607
608
609 TextView h2 = new TextView(this);
610 h2.setTextSize(16);
611 h2.setTypeface( Typeface.defaultFromStyle(Typeface.BOLD));
612 h2.setText("Om minutter");
613
614 params = new TableRow.LayoutParams();
615 params.weight = 2;
616 head.addView(h2,params);
617
618
619
620 table.addView(head);
621
622 for (MetroEntry entry : metroBean.entries) {
623 TableRow row = new TableRow(this);
624
625 Log.e("Test", "" + entry.destination);
626
627 TextView v1 = new TextView(this);
628 v1.setTextSize(16);
629 v1.setText( entry.metro );
630 row.addView(v1);
631
632 TextView v2 = new TextView(this);
633 v2.setTextSize(16);
634 v2.setText( entry.destination );
635 row.addView(v2);
636
637 TextView v3 = new TextView(this);
638 v3.setTextSize(16);
639 v3.setText( entry.minutes );
640 row.addView(v3);
641
642 table.addView(row);
643
644 }
645 findViewById(R.id.rootView).requestLayout();
646 }
647
648 class MetroFetcher extends AsyncTask<Integer, Void, Void> {
649
650 @Override
651 protected void onPostExecute(Void result) {
652 super.onPostExecute(result);
653
654
655
656 pgDialog.dismiss();
657
658 if (metroBean != null) {
659 loadMetroData();
660 } else { // communication or parse error
661 commFailCounter++;
662 AlertDialog.Builder builder = new AlertDialog.Builder(DepartureList.this);
663 builder.setMessage("Error finding metro data");
664 builder.setCancelable(true);
665 if (commFailCounter < 3) {
666 builder.setPositiveButton(getString(generic_retry), new DialogInterface.OnClickListener() {
667 public void onClick(DialogInterface dialog, int id) {
668 dialog.dismiss();
669 startMetroFetcher();
670
671 }
672 });
673 }
674 builder.setNegativeButton(getString(generic_cancel), new DialogInterface.OnClickListener() {
675 public void onClick(DialogInterface dialog, int id) {
676 dialog.dismiss();
677 DepartureList.this.finish(); //TODO: should we really close the activity ??
678 }
679 });
680
681 try { //TODO: is this still necessary after the 0.9.4.1 fix ?
682 builder.show();
683 } catch (android.view.WindowManager.BadTokenException e) {
684 Log.i("DepartureList", "BadTokenException"); // this can happen if the user switched away from this activity, while doInBackground was running
685 }
686 }
687 }
688
689 @Override
690 protected Void doInBackground(Integer... params) {
691 metroBean = metro.lookupMetroInfo(params[0]);
692 return null;
693 }
694
695 }
696
697 }

  ViewVC Help
Powered by ViewVC 1.1.20