source: other-projects/tipple-android/tipple-lib/src/org/greenstone/android/tipple/base/TippleActivity.java@ 26899

Last change on this file since 26899 was 26899, checked in by davidb, 11 years ago

Tipple reborn after Chris's Summer of Code 2013

File size: 19.5 KB
Line 
1package org.greenstone.android.tipple.base;
2
3import java.io.BufferedWriter;
4import java.io.File;
5import java.io.FileOutputStream;
6import java.io.FileWriter;
7import java.io.InputStream;
8import java.util.ArrayList;
9import java.util.HashMap;
10import java.util.Locale;
11
12import android.location.Criteria;
13import android.location.LocationManager;
14import android.os.Environment;
15import android.preference.PreferenceManager;
16import android.speech.tts.TextToSpeech;
17import android.speech.tts.TextToSpeech.OnInitListener;
18import android.text.method.ScrollingMovementMethod;
19import android.util.Log;
20import android.view.Gravity;
21import android.view.Menu;
22import android.view.MenuItem;
23import android.view.View;
24import android.view.ViewGroup;
25import android.app.Activity;
26import android.app.AlertDialog;
27import android.content.Context;
28import android.content.DialogInterface;
29import android.content.Intent;
30import android.content.SharedPreferences;
31import android.content.res.AssetManager;
32import android.location.LocationListener;
33import android.widget.FrameLayout;
34import android.widget.LinearLayout;
35import android.widget.Toast;
36import android.widget.LinearLayout.LayoutParams;
37import android.widget.TextView;
38
39
40import org.greenstone.android.tipple.R;
41import org.json1.JSONStringer;
42import org.json1.JSONWriter;
43import org.mapsforge.android.maps.MapActivity;
44import org.mapsforge.android.maps.MapView;
45
46
47public class TippleActivity extends MapActivity implements OnInitListener
48{
49 private final boolean debug_mode = true;
50
51 enum TextAudioModeEnum { TEXT_ONLY, TEXT_PLUS_AUDIO, AUDIO_ONLY, AUDIO_PLUS_TEXT };
52
53 /*
54 * GPS coords point to ==> uni rec centre
55 * TODO: Rather than hardcode a "starting point" location as shown below, get the last
56 * known location of the user.
57 */
58
59
60 private static final double DEFAULT_NO_GPS_LONGITUDE = 175.3141;
61 private static final double DEFAULT_NO_GPS_LATITUDE = -37.7876;
62
63
64 private static boolean first_time_starting = true;
65
66 // File storage
67 protected static String geodataDirectory;
68 protected static String logDirectory;
69 protected static String audioDirectory;
70
71 // Preferences
72 public static SharedPreferences sharedPreferences;
73 public static boolean centreOnGPSLocation;
74 public static boolean showScaleBar;
75 public static boolean showSights;
76 public static boolean showUserTrail;
77 public static TextAudioModeEnum textAudioMode;
78
79 // Logging
80 public static TippleLog log;
81
82 // Views
83 protected TextView longlat_view_;
84 protected MapView map_view_;
85 protected TextView text_view_;
86 protected FrameLayout text_map_composite_;
87 protected AudioPlayerView audio_player_view_;
88
89 protected ArrayList<TipLocation> user_trail_ = null;
90 protected String user_trail_filename_ = null;
91
92 // Text to Speech
93 protected TextToSpeech tts_ = null;
94
95 protected TipLocationManager tip_location_manager_ = null;
96 protected LocationManager location_manager_ = null;
97 protected LocationListener location_listener_ = null;
98
99 // Request codes
100 protected final int TTS_DATA_CHECK = 0;
101 private static final int SELECT_LOG_FILE = 1;
102
103 public static String DEFAULT_LOC_FILE = "hamilton";
104
105 public static String getGeodataDirectory()
106 {
107 return geodataDirectory;
108 }
109
110 public static String getLogDirectory()
111 {
112 return logDirectory;
113 }
114
115 public static String getAudioDirectory()
116 {
117 return audioDirectory;
118 }
119
120 /**
121 *
122 * @param asset_filename
123 * @param external_storage_filename
124 * @param debug_mode If this is set to true, then we will always push out new files to the SD card on Tipple launch
125 * @return
126 */
127 protected boolean assetToExternalStorage(String asset_filename, String external_storage_filename, boolean debug_mode)
128 {
129 boolean status = true;
130
131 try {
132
133 File external_storage_file = new File(external_storage_filename);
134 if (!external_storage_file.exists() || (debug_mode)) {
135 // Need to create the /sdcard version
136
137 AssetManager assetManager = getAssets();
138
139 InputStream ais = assetManager.open(asset_filename); // ais = asset input stream
140 FileOutputStream fos = new FileOutputStream(external_storage_file); // fos = file output stream
141
142 byte [] buffer = new byte[1024];
143
144 int buflen;
145 while ((buflen = ais.read(buffer))>0) {
146 fos.write(buffer,0,buflen);
147 }
148
149 ais.close();
150 fos.close();
151 }
152
153 }
154 catch (Exception e) {
155 e.printStackTrace();
156 status = false;
157 }
158
159 return status;
160 }
161
162 protected boolean initTippleStore(String location)
163 {
164 DEFAULT_LOC_FILE = location;
165
166 // Locate map data directory
167 File external_root_file = Environment.getExternalStorageDirectory();
168 String external_root = external_root_file.getAbsolutePath();
169 String tipple_store_dir = external_root + "/tipple-store";
170
171 geodataDirectory = tipple_store_dir + "/geodata/";
172 logDirectory = tipple_store_dir + "/logs/";
173 audioDirectory = tipple_store_dir + "/audio/";
174
175 File tipple_store_file = new File(tipple_store_dir);
176 if (!tipple_store_file.exists()) {
177 if (!tipple_store_file.mkdir()) {
178
179 String error = "Failed to create directory: " + tipple_store_dir;
180
181 System.err.println(error);
182 Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
183
184 return false;
185 }
186 }
187
188 File geodataDirFile = new File(geodataDirectory);
189 if (!geodataDirFile.exists()) {
190 if (!geodataDirFile.mkdir()) {
191
192 String error = "Failed to create directory: " + geodataDirectory;
193
194 System.err.println(error);
195 Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
196
197 return false;
198 }
199 }
200
201 File logDirFile = new File(logDirectory);
202 if (!logDirFile.exists()) {
203 if (!logDirFile.mkdir()) {
204
205 String error = "Failed to create directory: " + logDirectory;
206
207 System.err.println(error);
208 Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
209
210 return false;
211 }
212 }
213
214 File audioDirFile = new File(audioDirectory);
215 if (!audioDirFile.exists()) {
216 if (!audioDirFile.mkdir()) {
217
218 String error = "Failed to create directory: " + audioDirectory;
219
220 System.err.println(error);
221 Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
222
223 return false;
224 }
225 }
226
227 // dig out the audio files
228 String[] audio_filenames = { "BeepMorseSonar.wav","Ding.wav","DingExtended.wav" };
229 for (String audio_filename: audio_filenames) {
230 String audio_asset_filename = "audio/" + audio_filename;
231 String audio_exstore_filename = audioDirectory + audio_filename;
232
233 if (!assetToExternalStorage(audio_asset_filename,audio_exstore_filename,debug_mode)) {
234
235
236 String error = "Failed to transfer asset " + audio_asset_filename + " to " + audio_exstore_filename;
237
238 System.err.println(error);
239 Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
240
241 return false;
242 }
243 }
244
245 String loc_asset_filename = "geodata/" + DEFAULT_LOC_FILE + ".loc";
246 String loc_exstore_filename = geodataDirectory + DEFAULT_LOC_FILE + ".loc";
247
248 if (!assetToExternalStorage(loc_asset_filename,loc_exstore_filename,debug_mode)) {
249
250 String error = "Failed to transfer asset " + loc_asset_filename + " to " + loc_exstore_filename;
251
252 System.err.println(error);
253 Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
254
255 return false;
256 }
257
258 return true;
259 }
260
261 protected boolean initTippleStore()
262 {
263 return initTippleStore(null);
264 }
265
266
267 protected TextView createLongLatView()
268 {
269 // Lat,Long line at top
270 TextView longlat_view = new TextView(this);
271 longlat_view.setText("(Long,Lat): ");
272
273 LayoutParams longlat_view_layout
274 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 35, 0);
275 longlat_view.setLayoutParams(longlat_view_layout);
276
277 return longlat_view;
278 }
279
280
281
282
283 protected MapView createMapView(String map_filename)
284 {
285
286 // Map view in middle
287 MapView map_view = new MapView(this);
288 map_view.setClickable(true);
289 map_view.setBuiltInZoomControls(true);
290 String full_map_filename = geodataDirectory + map_filename;
291 //String full_map_filename = geodataDirectory + "mymap.map";
292
293 map_view.setMapFile(new File(full_map_filename));
294
295 LayoutParams map_view_layout
296 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
297 ViewGroup.LayoutParams.WRAP_CONTENT, 1);
298
299 map_view.setLayoutParams(map_view_layout);
300
301
302 return map_view;
303 }
304
305 protected MapView createMapViewFromAsset(String map_filename)
306 {
307 // Transfer asset to file in dataDirectory if it does not already exist
308
309 String asset_filename = "geodata" + File.separator + map_filename;
310 String external_storage_filename = geodataDirectory + map_filename;
311
312 assetToExternalStorage(asset_filename,external_storage_filename,debug_mode);
313
314 return createMapView(map_filename);
315
316 }
317
318 protected TextView createTextView()
319 {
320 // Text view in middle (ultimately on top of map view)
321 TextView text_view = new TextView(this);
322
323 LayoutParams text_view_layout
324 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
325 ViewGroup.LayoutParams.WRAP_CONTENT, 1);
326 text_view.setLayoutParams(text_view_layout);
327
328 text_view.setGravity(Gravity.BOTTOM|Gravity.FILL_HORIZONTAL);
329
330 text_view.setPadding(12, 12, 12, 12);
331 text_view.setTextColor(0xffffffff);
332 text_view.setBackgroundColor(0xCC000000);
333 text_view.setTextSize(20.0f);
334
335 text_view.setVerticalScrollBarEnabled(true);
336 text_view.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
337
338 text_view.setMovementMethod(new ScrollingMovementMethod());
339
340 text_view.setVisibility(View.INVISIBLE);
341
342 return text_view;
343 }
344
345 protected FrameLayout compositTextViewOnMapView(MapView map_view, TextView text_view)
346 {
347 FrameLayout frame_layout = new FrameLayout(this);
348
349 LayoutParams view_layout
350 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
351 ViewGroup.LayoutParams.WRAP_CONTENT, 1);
352
353 frame_layout.setLayoutParams(view_layout);
354 frame_layout.addView(map_view);
355 frame_layout.addView(text_view);
356
357 return frame_layout;
358 }
359
360 protected AudioPlayerView createAudioPlayerView()
361 {
362 // Player at the bottom
363 AudioPlayerView audio_player_view = new AudioPlayerView(this, text_view_);
364
365 LayoutParams audio_player_layout
366 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT, 0);
367 audio_player_view.setLayoutParams(audio_player_layout);
368
369 return audio_player_view;
370 }
371
372
373 protected void refreshPreferences()
374 {
375
376
377 // Get the preferences and start storing them in our class's variables
378 sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
379
380 // set the default values, but only if the user hasn't changed the preferences themselves
381 PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
382
383 //shared_preferences_ = getPreferences(MODE_PRIVATE);
384 showScaleBar = sharedPreferences.getBoolean("showScaleBar", false);
385 centreOnGPSLocation = sharedPreferences.getBoolean("centreOnGPSLoc", false);
386
387 showSights = sharedPreferences.getBoolean("showSights", true);
388
389 showUserTrail = sharedPreferences.getBoolean("showUserTrail", false);
390
391 String textAudioModeStr = sharedPreferences.getString("textAudioMode", "AUDIO_PLUS_TEXT");
392 textAudioMode = Enum.valueOf(TextAudioModeEnum.class, textAudioModeStr);
393
394 }
395
396
397 @Override
398 protected void onResume()
399 {
400 System.err.println("*** TippleActivity::onResume()");
401
402 super.onResume();
403
404 refreshPreferences();
405 audio_player_view_.refreshView();
406
407
408 map_view_.getMapScaleBar().setShowMapScaleBar(showScaleBar);
409
410
411 /*
412 * The code below is REALLY bad, but I decided to comment it out instead.
413 * TippleActivity's onResume() gets called many times, and so TipLocationManager
414 * is actually adding lots and lots of tour locations.
415 */
416
417 /*
418
419 if (tip_location_manager_ != null) {
420 if (showSights) {
421 //tip_location_manager_.createTourLocationsOverlay(map_view_,locations_);
422 tip_location_manager_.addTourLocations();
423 }
424 else {
425 tip_location_manager_.removeTourLocations();
426 }
427
428 if (user_trail_filename_ != null) {
429
430 if (showUserTrail) {
431 //tip_location_manager_.createLogRouteLocationsOverlay(map_view_,user_trail_);
432 tip_location_manager_.addLogRouteLocations();
433 }
434 else {
435 tip_location_manager_.removeLogRouteLocations();
436 }
437 }
438 }
439
440 */
441
442 }
443
444 public void buildAlertMessageNoGps()
445 {
446 final AlertDialog.Builder builder = new AlertDialog.Builder(this);
447
448 builder.setMessage("Your GPS doesn't seem to be enabled. Enable it then re-launch Tipple.")
449 .setCancelable(false)
450 .setPositiveButton("Enable in Settings", new DialogInterface.OnClickListener()
451 {
452 @Override
453 public void onClick(DialogInterface dialog, int which)
454 {
455 startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
456 }
457 });
458
459 final AlertDialog alert = builder.create();
460 alert.show();
461 }
462
463 protected void onActivityResult(int requestCode, int resultCode, Intent data)
464 {
465 Log.d("TippleActivity::onActivityResult()", "requestCode: " + requestCode);
466
467 if (requestCode == TTS_DATA_CHECK) {
468 if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
469
470 if(first_time_starting)
471 {
472 // success, create the TTS instance
473 tts_ = new TextToSpeech(this, (TextToSpeech.OnInitListener) this);
474 tts_.setLanguage(Locale.UK);
475
476 // now TTS initialized, init TTS aware place-locations
477 String loc_filename = geodataDirectory + "hamilton.loc";
478 System.err.println("*** loc filename = " + loc_filename);
479
480 tip_location_manager_ = new TipLocationManager(this, audio_player_view_, tts_, map_view_);
481 tip_location_manager_.createTipLocations(DEFAULT_NO_GPS_LONGITUDE, DEFAULT_NO_GPS_LATITUDE);
482 tip_location_manager_.createMapLocations();
483
484 // Set up a callback method for handling GPS location updates connected with our place-locations
485 location_manager_ = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
486 if ( !location_manager_.isProviderEnabled( LocationManager.GPS_PROVIDER ) )
487 {
488 buildAlertMessageNoGps();
489 }
490 else
491 {
492
493 location_listener_
494 = new TippleLocationListener(this,map_view_,longlat_view_,tip_location_manager_);
495
496 Criteria location_criteria_ = new Criteria();
497 location_criteria_.setAccuracy(Criteria.ACCURACY_LOW);
498 location_manager_.requestLocationUpdates(location_manager_.getBestProvider(location_criteria_, true), 0, 0, location_listener_);
499 }
500
501
502 tip_location_manager_.addMapLocations();
503
504 first_time_starting = false;
505
506 }
507
508 } else {
509 // missing data, install it
510 Intent installIntent = new Intent();
511 installIntent.setAction(
512 TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
513 startActivity(installIntent);
514 }
515 }
516
517 else if (requestCode == SELECT_LOG_FILE) {
518
519 if (resultCode == RESULT_OK) {
520 if (data != null && data.getStringExtra("logFile") != null) {
521 if (user_trail_filename_ != null) {
522 // have a previously loaded route => scrub it
523 tip_location_manager_.removeLogRouteLocations();
524 user_trail_filename_ = null;
525 }
526 // set up route overlay
527 user_trail_filename_ = data.getStringExtra("logFile");
528 user_trail_ = tip_location_manager_.loadUserTrail(user_trail_filename_);
529 tip_location_manager_.createLogRouteLocationsOverlay(user_trail_);
530 }
531 } else if (resultCode == RESULT_CANCELED) {
532 // nothing to do
533 }
534
535 }
536
537 }
538
539
540 @Override
541 public void onInit(int status)
542 {
543
544 Log.d("TippleActivity::onInit()", "Status (zero == good) is: " + status);
545
546 if (status == TextToSpeech.SUCCESS)
547 {
548 Log.d("TippleActivity::onInit()","Text-to-speech engine initialized");
549 }
550 else if (status == TextToSpeech.ERROR)
551 {
552 Log.e("TippleActivity::onInit()","Text-to-speech engine failed initialized");
553 }
554
555
556
557 }
558
559 @Override
560 public boolean onCreateOptionsMenu(Menu menu)
561 {
562 getMenuInflater().inflate(R.menu.options_menu, menu);
563 return true;
564 }
565
566 @Override
567 public boolean onOptionsItemSelected(MenuItem item)
568 {
569 int item_id = item.getItemId();
570
571 if (item_id == R.id.menu_info) {
572 startActivity(new Intent(this, InfoView.class));
573
574 return true;
575 }
576 else if (item_id == R.id.menu_preferences) {
577
578 startActivity(new Intent(this, EditPreferences.class));
579 return true;
580 }
581 else if (item_id == R.id.menu_logfile) {
582 startActivityForResult(new Intent(this, TippleLogFileBrowser.class), SELECT_LOG_FILE);
583 return true;
584 }
585 else {
586 return false;
587 }
588 }
589
590
591
592 @Override
593 protected void onStop()
594 {
595 System.err.println("*** TippleActivity::onStop()");
596 super.onStop();
597
598 // Is this needed?
599 // Go through an Editor object to make preference changes stick
600 SharedPreferences.Editor editor = sharedPreferences.edit();
601 editor.commit();
602
603 }
604
605 @Override
606 protected void onPause()
607 {
608 System.err.println("*** TippleActivity::onPause()");
609 super.onPause();
610 }
611
612 @Override
613 protected void onDestroy()
614 {
615 System.err.println("*** TippleActivity::onDestroy()");
616 super.onDestroy();
617
618 tts_.shutdown();
619 tts_ = null;
620
621 // Switch off location updates
622 if (location_manager_ != null) {
623
624 if (location_listener_ != null) {
625
626 location_manager_.removeUpdates(location_listener_);
627 location_listener_ = null;
628 }
629 location_manager_ = null;
630
631 user_trail_filename_ = null;
632 }
633
634
635 // http://stackoverflow.com/questions/3957253/what-is-this-log-when-i-coded-thread-stop
636 TipLocationManager.stopOverlayThreads();
637
638
639 log.optStopLog();
640 }
641
642}
Note: See TracBrowser for help on using the repository browser.