package edu.illinois.geoalarm; import java.io.IOException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; import edu.illinois.geoalarm.parser.XMLParser; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.TimePickerDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.SQLException; import android.graphics.Color; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; import android.os.Handler; import android.speech.RecognizerIntent; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.EditorInfo; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.ImageButton; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.TimePicker; /** * The TripPlanner activity handles planning a bus trip. * This class uses the data input by the user to display possible itineraries, * and then passes the trip data to the RouteMap class so it can start the trip * @author GeoAlarm * */ public class TripPlanner extends Activity { private AutoCompleteTextView lineSearchBar; private AutoCompleteTextView startingLocationSearchBar; private AutoCompleteTextView destinationLocationSearchBar; private Button setAlarmButton; private GeoAlarmDB database; private String selectedLine; private String selectedStartingStation; private String selectedDestinationStation; private String selectedNotification = POP_UP_NOTIFICATION; private String selectedNotificationTime = AT_STOP_CHOICE; private int hourSet = -1; private int minuteSet = -1; private int itineraryNum=0; public static final String AT_STOP_CHOICE = "At Stop"; public static final String STATION_BEFORE_STOP_CHOICE = "Station Before Stop"; public static final String AT_TIME_CHOICE = "At Time"; public static final String RING_NOTIFICATION = "Ring"; public static final String VIBRATE_NOTIFICATION = "Vibrate"; public static final String POP_UP_NOTIFICATION = "PopUp Message"; private static final int ITINERARY_OPTIONS_ID = 0; private static final int ALARM_OPTIONS_ID = 1; private static final int TIME_OPTIONS_ID = 2; private static final int INPUT_TIME_ID = 3; private static final int LEG_ID = 4; XMLParser parser; private int buttonVoice = 0; ArrayList<String> matches; Handler refresh; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.trip_cta_bus); SharedPreferences settings = getSharedPreferences("GeoAlarm", Activity.MODE_PRIVATE); View v = findViewById(R.id.optionsTopLayout); v.setBackgroundResource(settings.getInt("color_value", Color.BLACK)); initializeHandles(); loadDatabase(); populateLineSpinner(); ImageButton speakButton1 = (ImageButton) findViewById(R.id.voice1); ImageButton speakButton2 = (ImageButton) findViewById(R.id.voice2); ImageButton speakButton3 = (ImageButton) findViewById(R.id.voice3); PackageManager pm = getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0); if (activities.size() == 0) { speakButton1.setEnabled(false); speakButton2.setEnabled(false); speakButton3.setEnabled(false); } parser = new XMLParser(); database.close(); refresh = new Handler(); } @Override public void onPause() { database.close(); super.onPause(); } @Override public void onResume() { loadDatabase(); super.onResume(); } /** * This method uses findViewById to initialized the object handles from the View elements */ public void initializeHandles() { setAlarmButton = (Button) findViewById(R.id.setAlarmButton); setAlarmButton.setEnabled(false); lineSearchBar = (AutoCompleteTextView)findViewById(R.id.lineSearchBar); startingLocationSearchBar = (AutoCompleteTextView)findViewById(R.id.startingLocationSearchBar); startingLocationSearchBar.setEnabled(false); destinationLocationSearchBar = (AutoCompleteTextView)findViewById(R.id.destinationLocationSearchBar); destinationLocationSearchBar.setEnabled(false); } /** * This function tries to load the existing SQLite DB */ public void loadDatabase() { database = new GeoAlarmDB(this.getApplicationContext()); // Check the custom SQLite helper functions that load existing DB try { database.createDataBase(); } catch (IOException e) { throw new Error("Unable to create/find database"); } // Open the SQLite database try { database.openDataBase(); } catch (SQLException sql) { throw new Error("Unable to execute sql in: " + sql.toString()); } } /** * This method populates the startingLocationSearchBar and destinationLocationSearchBar with data from the database */ public void populateStartingAndDestination() { if(!database.geoAlarmDB.isOpen()) loadDatabase(); List<String> locationList = database.getLineStops(selectedLine); ArrayAdapter<String> locationAdapter = new ArrayAdapter<String>(this.getBaseContext(), android.R.layout.simple_dropdown_item_1line, locationList); startingLocationSearchBar.setAdapter(locationAdapter); startingLocationSearchBar.setOnEditorActionListener(new OnEditorActionListener() { public boolean onEditorAction(TextView arg0, int arg1, KeyEvent arg2) { List<String> locationList = database.getLineStops(selectedLine); String text = arg0.getText().toString(); if(locationList.contains(text)) { selectedStartingStation = text; } else { selectedStartingStation = locationList.get(0); arg0.setText(locationList.get(0)); } if(!selectedStartingStation.equals(selectedDestinationStation)) { setAlarmButton.setEnabled(true); } else { setAlarmButton.setEnabled(false); } return false; } }); startingLocationSearchBar.setHint("Type start stop"); destinationLocationSearchBar.setAdapter(locationAdapter); destinationLocationSearchBar.setOnEditorActionListener(new OnEditorActionListener() { public boolean onEditorAction(TextView arg0, int arg1, KeyEvent arg2) { List<String> locationList = database.getLineStops(selectedLine); String text = arg0.getText().toString(); if(locationList.contains(text)) { selectedDestinationStation = text; } else { selectedDestinationStation = locationList.get(0); arg0.setText(locationList.get(0)); } if(!selectedDestinationStation.equals(selectedStartingStation)) { setAlarmButton.setEnabled(true); } else { setAlarmButton.setEnabled(false); } return false; } }); destinationLocationSearchBar.setHint("Type destination stop"); } /** * This function populates the lineListSpinner. It gets the list of lines from the database, * and adds them to the Spinner. * @throws Exception */ public void populateLineSpinner() { if(!database.geoAlarmDB.isOpen()) loadDatabase(); ArrayList<String> linesList = database.getBusLines(); lineSearchBar = (AutoCompleteTextView)findViewById(R.id.lineSearchBar); ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(this.getBaseContext(), android.R.layout.simple_dropdown_item_1line, linesList); lineSearchBar.setAdapter(adapter1); lineSearchBar.setOnEditorActionListener(new OnEditorActionListener() { public boolean onEditorAction(TextView arg0, int arg1, KeyEvent arg2) { ArrayList<String> linesList = database.getBusLines(); String text = arg0.getText().toString(); if(linesList.contains(text)) { selectedLine = text; populateStartingAndDestination(); } else { selectedLine = linesList.get(0); arg0.setText(linesList.get(0)); populateStartingAndDestination(); } startingLocationSearchBar.setEnabled(true); destinationLocationSearchBar.setEnabled(true); return false; } }); // Set background message for the search bar lineSearchBar.setHint("Type line"); } /** * This method is used to launch the alarm options dialog. The method is bound to the button using * the onClick XML attribute. */ public void configureAlarmOptions(View view) { this.showDialog(ALARM_OPTIONS_ID); this.showDialog(TIME_OPTIONS_ID); if(isOnline()) { this.showDialog(ITINERARY_OPTIONS_ID); } } @Override public Dialog onCreateDialog(int dialogID) { Dialog dialog = null; switch(dialogID) { case ITINERARY_OPTIONS_ID: dialog = createItineraryOptionsDialog(); break; case LEG_ID: dialog = createLegsDialog(); break; case ALARM_OPTIONS_ID: dialog = createAlarmOptionsDialog(); break; case TIME_OPTIONS_ID: dialog = createTimeOptionsDialog(); break; case INPUT_TIME_ID: dialog = createTimeInputDialog(); break; default: dialog = null; } return dialog; } /** * Checks whether we have a network connection * @return true if connected, false otherwise */ public boolean isOnline() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); if (netInfo != null && netInfo.isConnectedOrConnecting()) { return true; } return false; } /** * This method creates a new dialog for displaying itinerary options * @return The new AlertDialog for itineraries */ private AlertDialog createItineraryOptionsDialog() { DecimalFormat df = new DecimalFormat("#.######"); String startLat = "" + df.format(database.getLatitude(selectedStartingStation)); String startLon = "" + df.format(database.getLongitude(selectedStartingStation)); String endLat = "" + df.format(database.getLatitude(selectedDestinationStation)); String endLon = "" + df.format(database.getLongitude(selectedDestinationStation)); final CharSequence[] items = parser.getItineraryArray(startLat, startLon, endLat, endLon); //final CharSequence[] items = {"blah", "blah2"}; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Select itinerary"); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { itineraryNum = item; showDialog(LEG_ID); } }); return builder.create(); } /** * This method creates a new dialog for displaying leg options * @return The new AlertDialog */ private AlertDialog createLegsDialog() { DecimalFormat df = new DecimalFormat("#.######"); String startLat = "" + df.format(database.getLatitude(selectedStartingStation)); String startLon = "" + df.format(database.getLongitude(selectedStartingStation)); String endLat = "" + df.format(database.getLatitude(selectedDestinationStation)); String endLon = "" + df.format(database.getLongitude(selectedDestinationStation)); final CharSequence[] items = parser.getLegArray(startLat, startLon, endLat, endLon, itineraryNum); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Selected itinerary"); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { //showDialog(TIME_OPTIONS_ID); } }); return builder.create(); } /** * This method creates a AlertDialog that is used to determine what kind of notification the user wants * to receive * @return An AlertDialog ready to be shown */ private AlertDialog createAlarmOptionsDialog() { final CharSequence[] items = {RING_NOTIFICATION, VIBRATE_NOTIFICATION, POP_UP_NOTIFICATION}; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Pick a notification"); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { selectedNotification = items[item].toString(); } }); return builder.create(); } /** * This method creates an AlertDialog that is used to select what time the user wants to receive * a notification * @return An AlertDialog ready to be shown */ private AlertDialog createTimeOptionsDialog() { final CharSequence[] items = {AT_STOP_CHOICE, STATION_BEFORE_STOP_CHOICE, AT_TIME_CHOICE}; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("When do you want to be notified?"); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { if(items[item].equals(AT_TIME_CHOICE)) { selectedNotificationTime = AT_TIME_CHOICE; showDialog(INPUT_TIME_ID); } else { selectedNotificationTime = items[item].toString(); } } }); return builder.create(); } /** * This method creates a TimePickerDialog to enable the user to set the time for notification manually. * @return A TimePickerDialog ready to be shown */ private Dialog createTimeInputDialog() { TimePickerDialog.OnTimeSetListener listener = new TimePickerDialog.OnTimeSetListener() { public void onTimeSet(TimePicker view, int hourOfDay, int minute) { hourSet = hourOfDay; minuteSet = minute; } }; Calendar calendar = GregorianCalendar.getInstance(); TimePickerDialog dialog = new TimePickerDialog(this, listener, calendar.get(Calendar.HOUR), calendar.get(Calendar.MINUTE), false); return dialog; } /** * This method is used to launch the trip setting intent. The method is bound to the button using the * onClick XML attribute. */ public void setAlarm(View view) { database.close(); Intent intent1 = new Intent(view.getContext(), RouteMap.class); intent1.putExtra("edu.illinois.geoalarm.isPlannedTrip", true); intent1.putExtra("edu.illinois.geoalarm.line", selectedLine); intent1.putExtra("edu.illinois.geoalarm.startingStation", selectedStartingStation); intent1.putExtra("edu.illinois.geoalarm.destinationStation", selectedDestinationStation); intent1.putExtra("edu.illinois.geoalarm.selectedNotification", selectedNotification); intent1.putExtra("edu.illinois.geoalarm.selectedNotificationTime", selectedNotificationTime); intent1.putExtra("edu.illinois.geoalarm.selectedNotificationHour", hourSet); intent1.putExtra("edu.illinois.geoalarm.selectedNotificationMinute", minuteSet); startActivityForResult(intent1, 0); } /** * Returns a handle to the active GeoAlarmDB * @return A handle to the active GeoAlarmDB */ public GeoAlarmDB getDatabase() { return database; } /** * Called when the line select voice button is clicked. Sets the button clicked * and starts the voice recognition activity * @param v The button clicked */ public void speakButtonClicked1(View v) { buttonVoice = 1; startVoiceRecognitionActivity(); } /** * Called when the start select voice button is clicked. Sets the button clicked * and starts the voice recognition activity * @param v The button clicked */ public void speakButtonClicked2(View v) { buttonVoice = 2; startVoiceRecognitionActivity(); } /** * Called when the end select voice button is clicked. Sets the button clicked * and starts the voice recognition activity * @param v The button clicked */ public void speakButtonClicked3(View v) { buttonVoice = 3; startVoiceRecognitionActivity(); } /** * Starts the built-in android activity for voice recognition */ private void startVoiceRecognitionActivity() { Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "GeoAlarm"); database.close(); startActivityForResult(intent, 1234); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { loadDatabase(); if (requestCode == 1234 && resultCode == RESULT_OK) { // Populate the wordsList with the String values the recognition engine thought it heard matches = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); if(matches == null) { database.close(); return; } if(matches.size() == 0) { database.close(); return; } if(buttonVoice == 1) { ArrayList<String> linesList = database.getBusLines(); for(int i = linesList.size() - 1 ; i >= 0; i--) { for(int j = matches.size() - 1; j >= 0; j--) { if(linesList.get(i).toLowerCase().equals(matches.get(j).toLowerCase())) { selectedLine = linesList.get(i); lineSearchBar.setText(selectedLine); lineSearchBar.onEditorAction(EditorInfo.IME_ACTION_DONE); } } } } if(buttonVoice == 2) { ArrayList<String> linesList = database.getLineStops(selectedLine); for(int i = linesList.size() - 1 ; i >= 0; i--) { for(int j = matches.size() - 1; j >= 0; j--) { if(matches.get(j).contains("and")) { matches.add(j, matches.get(j).replace("and", "&")); } if(linesList.get(i).toLowerCase().contains(matches.get(j).toLowerCase())) { selectedStartingStation = linesList.get(i); startingLocationSearchBar.setText(selectedStartingStation); startingLocationSearchBar.onEditorAction(EditorInfo.IME_ACTION_DONE); } } } } if(buttonVoice == 3) { ArrayList<String> linesList = database.getLineStops(selectedLine); for(int i = linesList.size() - 1 ; i >= 0; i--) { for(int j = matches.size() - 1; j >= 0; j--) { if(matches.get(j).contains("and")) { matches.add(j, matches.get(j).replace("and", "&")); } if(linesList.get(i).toLowerCase().equals(matches.get(j).toLowerCase())) { selectedDestinationStation = linesList.get(i); destinationLocationSearchBar.setText(selectedDestinationStation); destinationLocationSearchBar.onEditorAction(EditorInfo.IME_ACTION_DONE); } } } } database.close(); } super.onActivityResult(requestCode, resultCode, data); } /** * Returns a handle to the line AutoCompleteTextView * @return the lineSearchBar AutoCompleteTextView */ public AutoCompleteTextView getLineSearchBar() { return lineSearchBar; } /** * Returns a handle to the start AutoCompleteTextView * @return the startingLocationSearchBar AutoCompleteTextView */ public AutoCompleteTextView getStartingLocationSearchBar() { return startingLocationSearchBar; } /** * Returns a handle to the end AutoCompleteTextView * @return the destinationLocationSearchBar AutoCompleteTextView */ public AutoCompleteTextView getDestinationLocationSearchBar() { return destinationLocationSearchBar; } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.options: Intent intent = new Intent(TripPlanner.this, Options.class); startActivityForResult(intent, 0); } return true; } /** * This method can be used to execute a "next" editor action on the start search bar. * User for compatibility issues with the Robotium testing library */ public void makeEditorAction() { refresh.post(new Runnable() { public void run() { startingLocationSearchBar.onEditorAction(EditorInfo.IME_ACTION_NEXT); } }); } }