/**
* Copyright (C) 2009 Anders Aagaard <aagaande@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
*
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.neuron.trafikanten.views.route;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
import com.google.android.AnalyticsUtils;
import com.neuron.trafikanten.R;
import com.neuron.trafikanten.dataSets.RouteSearchData;
import com.neuron.trafikanten.dataSets.StationData;
import com.neuron.trafikanten.tasks.ShowHelpTask;
import com.neuron.trafikanten.views.route.SelectRouteAdapter.GenericRouteEntry;
import com.neuron.trafikanten.views.route.SelectRouteAdapter.SeperatorRouteEntry;
import com.neuron.trafikanten.views.route.SelectRouteAdapter.SimpleTextRouteEntry;
public class SelectRouteView extends ListActivity {
private static final String TAG = "Trafikanten-SelectRouteView";
private static final int ACTIVITY_SELECT_FROM = 1;
private static final int ACTIVITY_SELECT_TO = 2;
private static final int DIALOG_SELECTTIME = 1;
private static final int DIALOG_TRANSPORTTYPES = 2;
private static final int DIALOG_CHANGEMARGIN = 3;
private static final int DIALOG_CHANGEPRIORITY = 4;
private static final int DIALOG_PROPOSALS = 5;
private SelectRouteAdapter listMenu;
private RouteSearchData routeSearch;
/*
* Options menu items:
*/
private final static int RESET_ID = Menu.FIRST;
private final static int HELP_ID = Menu.FIRST + 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.selectroute);
/*
* Analytics
*/
AnalyticsUtils.getInstance(this).trackPageView("/selectRouteView");
listMenu = new SelectRouteAdapter();
/*
* Load instance state
*/
if (savedInstanceState != null) {
routeSearch = savedInstanceState.getParcelable(RouteSearchData.PARCELABLE);
} else {
routeSearch = new RouteSearchData();
}
refreshMenu();
setListAdapter(listMenu);
}
private void resetView() {
routeSearch = new RouteSearchData();
refreshMenu();
}
/*
* This renders the entire menu
*/
public void refreshMenu() {
ArrayList<GenericRouteEntry> items = new ArrayList<GenericRouteEntry>();
/*
* Setup the basic menu:
*/
items.add(new SeperatorRouteEntry(this, R.string.selectStations));
/*
* Setup From
*/
CharSequence travelFromString = null;
if (routeSearch.fromStation.size() == 0) {
travelFromString = getText(R.string.selectWhereToTravelFrom);
} else {
for (StationData station : routeSearch.fromStation) {
if (travelFromString == null) {
travelFromString = getText(R.string.travelingFrom) + " : " + station.stopName;
} else {
travelFromString = travelFromString + ", " + station.stopName;
}
}
}
items.add(new SimpleTextRouteEntry(this, R.string.travelFrom, travelFromString, 0, 0, new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SelectRouteView.this, SelectRouteStationView.class);
startActivityForResult(intent, ACTIVITY_SELECT_FROM);
refreshMenu();
}
}));
/*
* Setup to
*/
CharSequence travelToString = null;
if (routeSearch.toStation.size() == 0) {
travelToString = getText(R.string.selectWhereToTravelTo);
} else {
for (StationData station : routeSearch.toStation) {
if (travelToString == null) {
travelToString = getText(R.string.travelingTo) + " : " + station.stopName;
} else {
travelToString = travelToString + ", " + station.stopName;
}
}
}
items.add(new SimpleTextRouteEntry(this, R.string.travelTo, travelToString, 0, 0, new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SelectRouteView.this, SelectRouteStationView.class);
startActivityForResult(intent, ACTIVITY_SELECT_TO);
refreshMenu();
}
}));
items.add(new SeperatorRouteEntry(this, R.string.changeSettings));
/*
* Setup when
*/
final SimpleDateFormat DATEFORMAT = new SimpleDateFormat("EEEEEEE dd-MM-yyyy HH:mm");
String travelTime;
if (routeSearch.arrival == 0) {
/*
* Travel type "travel at"
*/
travelTime = routeSearch.departure == 0 ? getText(R.string.travelAt) + " : " + getText(R.string.now) : getText(R.string.travelAt) + " : " + DATEFORMAT.format(routeSearch.departure);
} else {
/*
* Travel type "arrive before"
*/
travelTime = routeSearch.arrival == 0 ? getText(R.string.arriveBefore) + " : " + getText(R.string.now) : getText(R.string.arriveBefore) + " : " + DATEFORMAT.format(routeSearch.arrival);
}
items.add(new SimpleTextRouteEntry(this, R.string.when, travelTime, 5, 0, new OnClickListener() {
@Override
public void onClick(View v) {
SelectRouteView.this.showDialog(DIALOG_SELECTTIME);
refreshMenu();
}
}));
{
/*
* advanced onClickListener is shared
*/
final OnClickListener advancedOnClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
routeSearch.advancedOptionsEnabled = !routeSearch.advancedOptionsEnabled;
refreshMenu();
}
};
if (routeSearch.advancedOptionsEnabled) {
items.add(new SimpleTextRouteEntry(this, R.string.advancedOptions,getText(R.string.disable) + " " + getText(R.string.advancedOptions).toString().toLowerCase(), 0, 0, advancedOnClickListener));
/*
* Add advanced options
*/
CharSequence prioritize = routeSearch.changePunish > 2 ? getText(R.string.directRoute) : getText(R.string.shortTravel);
items.add(new SimpleTextRouteEntry(this, getText(R.string.prioritize) + " : " + prioritize, getText(R.string.prioritizeHint), 10, 0, new OnClickListener() {
@Override
public void onClick(View v) {
SelectRouteView.this.showDialog(DIALOG_CHANGEPRIORITY);
refreshMenu();
}
}));
/*items.add(new CheckboxRouteEntry(this, R.string.avoidWalking, routeSearch.avoidWalking, 10, new OnClickListener() {
@Override
public void onClick(View v) {
routeSearch.avoidWalking = !routeSearch.avoidWalking;
refreshMenu();
}
}));*/
items.add(new SimpleTextRouteEntry(this, getText(R.string.transportTypes), routeSearch.renderTransportTypes(this, routeSearch.getTransportArray(this)), 10, 0, new OnClickListener() {
@Override
public void onClick(View v) {
SelectRouteView.this.showDialog(DIALOG_TRANSPORTTYPES);
refreshMenu();
}
}));
items.add(new SimpleTextRouteEntry(this, getText(R.string.changeMargin) + " : " + routeSearch.changeMargin + "m", getText(R.string.safeMargin), 10, 0, new OnClickListener() {
@Override
public void onClick(View v) {
SelectRouteView.this.showDialog(DIALOG_CHANGEMARGIN);
refreshMenu();
}
}));
items.add(new SimpleTextRouteEntry(this, getText(R.string.proposals) + " : " + routeSearch.proposals, getText(R.string.maxProposals), 10, 0, new OnClickListener() {
@Override
public void onClick(View v) {
SelectRouteView.this.showDialog(DIALOG_PROPOSALS);
refreshMenu();
}
}));
} else {
items.add(new SimpleTextRouteEntry(this, R.string.advancedOptions, getText(R.string.enable) + " " + getText(R.string.advancedOptions).toString().toLowerCase(), 0, 0, advancedOnClickListener));
}
}
items.add(new SeperatorRouteEntry(this, R.string.searchForRoutes));
items.add(new SimpleTextRouteEntry(this, R.string.search, getText(R.string.calculateRouteResults), 0, R.drawable.ic_btn_search, new OnClickListener() {
@Override
public void onClick(View v) {
onSearch();
}
}));
listMenu.setList(items);
listMenu.notifyDataSetChanged();
}
/*
* Options menu, visible on menu button.
* @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
final MenuItem favorites = menu.add(0, RESET_ID, 0, R.string.reset);
favorites.setIcon(android.R.drawable.ic_menu_revert);
final MenuItem help = menu.add(0, HELP_ID, 0, R.string.help);
help.setIcon(android.R.drawable.ic_menu_help);
return true;
}
/*
* Options menu item selected, options menu visible on menu button.
* @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case RESET_ID:
resetView();
break;
case HELP_ID:
new ShowHelpTask(this);
break;
default:
Log.e(TAG, "onOptionsItemSelected unknown id " + item.getItemId());
}
return super.onOptionsItemSelected(item);
}
/*
* Do actual search
*/
public void onSearch() {
if (routeSearch.fromStation.size() == 0) {
Toast.makeText(this, R.string.pleaseSelectFrom, Toast.LENGTH_SHORT).show();
return;
}
if (routeSearch.toStation.size() == 0) {
Toast.makeText(this, R.string.pleaseSelectTo, Toast.LENGTH_SHORT).show();
return;
}
OverviewRouteView.ShowRoute(this, routeSearch);
}
/*
* Get get results from opened activities
* @see android.app.Activity#onActivityResult(int, int, android.content.Intent)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK) return;
switch(requestCode) {
case ACTIVITY_SELECT_FROM:
case ACTIVITY_SELECT_TO:
if (data.hasExtra(StationData.PARCELABLE)) {
/*
* Return data is a single station
*/
final StationData station = data.getParcelableExtra(StationData.PARCELABLE);
if (requestCode == ACTIVITY_SELECT_FROM) {
routeSearch.fromStation.clear();
routeSearch.fromStation.add(station);
} else {
routeSearch.toStation.clear();
routeSearch.toStation.add(station);
}
} else {
/*
* Return data is a set of stations
*/
final ArrayList<StationData> stationList = data.getParcelableArrayListExtra(SelectRouteStationView.STATIONLIST_PARCELABLE);
if (requestCode == ACTIVITY_SELECT_FROM) {
routeSearch.fromStation = stationList;
} else {
routeSearch.toStation = stationList;
}
}
break;
default:
Log.e(TAG, "Unknown request code " + requestCode);
}
refreshMenu();
}
@Override
protected Dialog onCreateDialog(int id) {
switch(id) {
case DIALOG_SELECTTIME:
/*
* Load previous info into display.
*/
Calendar oldTime = null;
boolean travelAt = true;
if (routeSearch.arrival > 0) {
travelAt = false;
oldTime = Calendar.getInstance();
oldTime.setTimeInMillis(routeSearch.arrival);
} else if (routeSearch.departure > 0) {
oldTime = Calendar.getInstance();
oldTime.setTimeInMillis(routeSearch.departure);
}
/*
* Create a dialog for selecting travel day and time.
*/
final Dialog dialog = new Dialog(this);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.selectroute_dialog_timepick);
/*
* Setup the travel at/arrive before spinner.
*/
final ArrayList<CharSequence> travelAtArriveBeforeItems = new ArrayList<CharSequence>();
travelAtArriveBeforeItems.add(getText(R.string.travelAt));
travelAtArriveBeforeItems.add(getText(R.string.arriveBefore));
final Spinner travelAtArriveBeforeSpinner = (Spinner) dialog.findViewById(R.id.travelAtArriveBefore);
final ArrayAdapter<CharSequence> travelAtArriveBeforeAdapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, travelAtArriveBeforeItems);
travelAtArriveBeforeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
travelAtArriveBeforeSpinner.setAdapter(travelAtArriveBeforeAdapter);
if (!travelAt) {
travelAtArriveBeforeSpinner.setSelection(1);
}
/*
* Setup the time picker
*/
final TimePicker timePicker = (TimePicker) dialog.findViewById(R.id.timePicker);
if (Build.VERSION.SDK_INT >= 11) {
// HACK, ref http://code.google.com/p/android/issues/detail?id=18982
int hour = timePicker.getCurrentHour();
timePicker.setIs24HourView(true);
timePicker.setCurrentHour(hour);
} else {
timePicker.setIs24HourView(true);
}
if (oldTime != null) {
timePicker.setCurrentHour(oldTime.get(Calendar.HOUR_OF_DAY));
timePicker.setCurrentMinute(oldTime.get(Calendar.MINUTE));
}
/*
* Setup list of days in "Select day to travel" dropdown.
*/
final Spinner dayList = (Spinner) dialog.findViewById(R.id.dayList);
final SimpleDateFormat DATEFORMAT = new SimpleDateFormat("EEEEEEE dd-MM-yyyy");
ArrayList<String> dateList = new ArrayList<String>();
Calendar date = Calendar.getInstance();
int positionOfOldTime = 0;
for (int i = 0; i < 14; i++) {
if (oldTime != null && date.get(Calendar.DAY_OF_YEAR) == oldTime.get(Calendar.DAY_OF_YEAR)) {
positionOfOldTime = i;
}
dateList.add(DATEFORMAT.format(date.getTime()));
date.add(Calendar.DAY_OF_YEAR, 1);
}
final ArrayAdapter<String> dayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, dateList);
dayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
dayList.setAdapter(dayAdapter);
dayList.setSelection(positionOfOldTime);
/*
* Setup button
*/
final Button okButton = (Button) dialog.findViewById(R.id.okButton);
okButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
boolean travelAt = travelAtArriveBeforeSpinner.getSelectedItemPosition() == 0;
Date date = DATEFORMAT.parse(dayAdapter.getItem(dayList.getSelectedItemPosition()));
date.setHours(timePicker.getCurrentHour());
date.setMinutes(timePicker.getCurrentMinute());
if (travelAt) {
routeSearch.departure = date.getTime();
routeSearch.arrival = 0;
} else {
routeSearch.departure = 0;
routeSearch.arrival = date.getTime();
}
refreshMenu();
dialog.dismiss();
} catch (ParseException e) {
// This can't happen.
}
}
});
/*
* Setup reset/now button
*/
final Button resetButton = (Button) dialog.findViewById(R.id.resetButton);
resetButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
routeSearch.departure = 0;
routeSearch.arrival = 0;
refreshMenu();
dialog.dismiss();
}
});
return dialog;
case DIALOG_TRANSPORTTYPES:
// ORDER : transportBus, transportTrain, transportTram, transportMetro, transportAirportBus, transportAirportTrain, transportBoat
final CharSequence[] transportItems = routeSearch.getTransportArray(this);
final boolean[] checkedTransportItems = {true, true, true, true, true, true, true};
AlertDialog.Builder transportBuilder = new AlertDialog.Builder(this);
transportBuilder.setTitle(R.string.transportTypes);
transportBuilder.setMultiChoiceItems(transportItems, checkedTransportItems, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which,
boolean isChecked) {
routeSearch.transportTypes[which] = isChecked;
refreshMenu();
}
});
final AlertDialog transportDialog = transportBuilder.create();
transportDialog.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
/*
* Check if any transport types are enabled
*/
int enabled = 0;
for (int i = 0; i < 7; i++) {
if (routeSearch.transportTypes[i]) {
enabled++;
}
}
/*
* Nothing enabled = reset all to enabled.
*/
if (enabled == 0) {
for (int i = 0; i < 7; i++) {
routeSearch.transportTypes[i] = true;
}
}
}
});
return transportDialog;
case DIALOG_CHANGEMARGIN:
final CharSequence[] changeMarginItems = {"1m", "2m", "3m", "4m", "5m", "6m", "7m", "8m", "9m", "10m"};
AlertDialog.Builder changeBuilder = new AlertDialog.Builder(this);
changeBuilder.setTitle(R.string.setChangeMargin);
changeBuilder.setItems(changeMarginItems, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
routeSearch.changeMargin = item + 1;
refreshMenu();
}
});
return changeBuilder.create();
case DIALOG_CHANGEPRIORITY:
final CharSequence[] priorityItems = {getText(R.string.shortTravel), getText(R.string.directRoute)};
AlertDialog.Builder priorityBuilder = new AlertDialog.Builder(this);
priorityBuilder.setTitle(R.string.prioritize);
priorityBuilder.setItems(priorityItems, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if (item == 0) {
routeSearch.changePunish = 2;
} else {
routeSearch.changePunish = 10;
}
refreshMenu();
}
});
return priorityBuilder.create();
case DIALOG_PROPOSALS:
final CharSequence[] proposalItems = {"1", "2", "3", "4", "5", "10", "15"};
AlertDialog.Builder changeProposals = new AlertDialog.Builder(this);
changeProposals.setTitle(R.string.setProposals);
changeProposals.setItems(proposalItems, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch(item) {
case 6:
routeSearch.proposals = 15;
break;
case 5:
routeSearch.proposals = 10;
break;
default:
routeSearch.proposals = item + 1;
}
refreshMenu();
}
});
return changeProposals.create();
}
return super.onCreateDialog(id);
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case DIALOG_SELECTTIME:
/*
* Simply recreate this dialog.
*/
removeDialog(DIALOG_SELECTTIME);
dialog = onCreateDialog(DIALOG_SELECTTIME);
break;
}
super.onPrepareDialog(id, dialog);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
listMenu.click(position);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(RouteSearchData.PARCELABLE, routeSearch);
}
}
class SelectRouteAdapter extends BaseAdapter
{
private ArrayList<GenericRouteEntry> items = new ArrayList<GenericRouteEntry>();
public void setList(ArrayList<GenericRouteEntry> items) {
this.items = items;
}
public void click(int pos) {
items.get(pos).onClick();
}
@Override
public int getCount() {
return items.size();
}
@Override
public GenericRouteEntry getItem(int pos) {
return items.get(pos);
}
@Override
public long getItemId(int pos) {
return pos;
}
@Override
public View getView(int pos, View arg1, ViewGroup arg2) {
return items.get(pos).mView;
}
@Override
public boolean isEnabled(int position) {
return !items.get(position).getClass().equals(SeperatorRouteEntry.class);
}
/*
* Fairly complicated view to render everything in the select route list.
* - Supports Linearlayout with titles and extra text
* - Supports checkboxes
* - Supports expandable, and having a parent to see if it's been expanded.
*/
public static class GenericRouteEntry {
public View mView;
private OnClickListener onClickListener;
public GenericRouteEntry(OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
public void onClick() {
if (onClickListener != null)
onClickListener.onClick(mView);
}
}
public static class SeperatorRouteEntry extends GenericRouteEntry {
private TextView mText;
public SeperatorRouteEntry(Context context, int text) {
super(null);
mText = new TextView(context, null);
mText.setText(text);
mText.setGravity(Gravity.CENTER);
mText.setBackgroundResource(R.drawable.skin_platform);
mText.setTextColor(Color.WHITE);
mView = mText;
}
}
public static class SimpleTextRouteEntry extends GenericRouteEntry {
private TextView mTitle;
private TextView mSubText;
private ImageView mSymbol;
private void setup(Context context, int indent, int logoResource) {
mView = LayoutInflater.from(context).inflate(R.layout.selectroute_textrouteentry, null);
if (indent > 0) {
mView.setPadding(mView.getPaddingLeft() + indent, mView.getPaddingTop(), mView.getPaddingRight(), mView.getPaddingBottom());
}
mTitle = (TextView) mView.findViewById(R.id.title);
mSubText = (TextView) mView.findViewById(R.id.subText);
if (logoResource > 0) {
mSymbol = (ImageView) mView.findViewById(R.id.symbol);
mSymbol.setVisibility(View.VISIBLE);
mSymbol.setImageResource(logoResource);
}
}
public SimpleTextRouteEntry(Context context, int title, CharSequence extra, int indent, int logoResource, OnClickListener onClickListener) {
super(onClickListener);
setup(context, indent, logoResource);
mTitle.setText(title);
mSubText.setText(extra);
}
public SimpleTextRouteEntry(Context context, CharSequence title, CharSequence extra, int indent, int logoResource, OnClickListener onClickListener) {
super(onClickListener);
setup(context, indent, logoResource);
mTitle.setText(title);
mSubText.setText(extra);
}
}
public static class CheckboxRouteEntry extends GenericRouteEntry {
private CheckBox mCheckBox;
/*
* This is a checkbox layout.
*/
public CheckboxRouteEntry(Context context, int title, boolean checked, int paddingLeft, OnClickListener onClickListener) {
super(onClickListener);
LinearLayout linearLayout = new LinearLayout(context);
linearLayout.setPadding(paddingLeft + 2, 2, 2, 2);
linearLayout.setOrientation(LinearLayout.VERTICAL);
mCheckBox = new CheckBox(context);
mCheckBox.setText(title);
mCheckBox.setChecked(checked);
mCheckBox.setTextColor(context.getResources().getColorStateList(R.color.skin_stoppested));
linearLayout.addView(mCheckBox, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
mView = linearLayout;
}
}
}