/**
* 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.util.ArrayList;
import android.app.Activity;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import com.google.android.AnalyticsUtils;
import com.neuron.trafikanten.HelperFunctions;
import com.neuron.trafikanten.R;
import com.neuron.trafikanten.dataProviders.IGenericProviderHandler;
import com.neuron.trafikanten.dataProviders.trafikanten.TrafikantenRoute;
import com.neuron.trafikanten.dataSets.DeviData;
import com.neuron.trafikanten.dataSets.RouteData;
import com.neuron.trafikanten.dataSets.RouteDeviData;
import com.neuron.trafikanten.dataSets.RouteProposal;
import com.neuron.trafikanten.dataSets.RouteSearchData;
import com.neuron.trafikanten.tasks.NotificationTask;
import com.neuron.trafikanten.tasks.SelectDeviTask;
import com.neuron.trafikanten.views.map.GenericMap;
/*
* This class shows a route selector list, when multiple travelproposals are sent.
*/
public class OverviewRouteView extends ListActivity {
private final static String TAG = "Trafikanten-OverviewRouteView";
private OverviewRouteAdapter routeList;
/*
* Context menu:
*/
private static final int NOTIFY_ID = Menu.FIRST;
private static final int MAP_ID = Menu.FIRST + 1;
private static final int DEVI_ID = Menu.FIRST + 2;
/*
* Dialogs
*/
private int selectedId = 0;
/*
* Wanted Route, this is used as a base for the search.
*/
private RouteSearchData routeSearch;
private TrafikantenRoute routeProvider;
private RouteDeviLoader routeDeviLoader;
public RouteDeviData deviList = new RouteDeviData();
/*
* UI
*/
private TextView infoText;
public static void ShowRoute(Activity activity, RouteSearchData routeSearch) {
Intent intent = new Intent(activity, OverviewRouteView.class);
intent.putExtra(RouteSearchData.PARCELABLE, routeSearch);
activity.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
/*
* Analytics
*/
AnalyticsUtils.getInstance(this).trackPageView("/overviewRouteView");
/*
* Setup the view
*/
setContentView(R.layout.route_overview);
routeList = new OverviewRouteAdapter(this);
infoText = (TextView) findViewById(R.id.emptyText);
/*
* Load instance state
*/
if (savedInstanceState == null) {
routeSearch = getIntent().getParcelableExtra(RouteSearchData.PARCELABLE);
load();
} else {
routeSearch = savedInstanceState.getParcelable(RouteSearchData.PARCELABLE);
final ArrayList<RouteProposal> list = savedInstanceState.getParcelableArrayList(OverviewRouteAdapter.KEY_ROUTELIST);
deviList = savedInstanceState.getParcelable(RouteDeviData.PARCELABLE);
routeList.setList(list);
infoText.setVisibility(routeList.getCount() > 0 ? View.GONE : View.VISIBLE);
loadDevi(); // continue loading devi incase we stopped in the middle of a load.
}
registerForContextMenu(getListView());
setListAdapter(routeList);
}
/*
* Load station data
*/
private void load() {
if (routeProvider != null)
routeProvider.kill();
routeList.getList().clear();
routeList.notifyDataSetChanged();
AnalyticsUtils.getInstance(this).trackEvent("Data", "Route", "Data", 0);
routeProvider = new TrafikantenRoute(this, routeSearch, new IGenericProviderHandler<RouteProposal>() {
@Override
public void onExtra(int what, Object obj) {
/* Class has no extra data */
}
@Override
public void onData(RouteProposal data) {
routeList.addItem(data);
routeList.notifyDataSetChanged();
infoText.setVisibility(routeList.getCount() > 0 ? View.GONE : View.VISIBLE);
}
@Override
public void onPostExecute(Exception exception) {
setProgressBarIndeterminateVisibility(false);
routeProvider = null;
if (exception != null) {
routeList.getList().clear();
routeList.notifyDataSetChanged();
Log.w(TAG,"onException " + exception);
infoText.setVisibility(View.VISIBLE);
if (exception.getClass().getSimpleName().equals("ParseException")) {
infoText.setText(R.string.trafikantenErrorParse);
} else {
infoText.setText(R.string.trafikantenErrorOther);
}
setProgressBarIndeterminateVisibility(false);
} else {
infoText.setText(R.string.noRoutesFound);
}
loadDevi();
}
@Override
public void onPreExecute() {
setProgressBarIndeterminateVisibility(true);
}
});
}
/*
* Load devi
*/
private void loadDevi() {
routeDeviLoader = new RouteDeviLoader(this, deviList, new IGenericProviderHandler<Void>() {
@Override
public void onData(Void data) {}
@Override
public void onExtra(int i, Object data) {}
@Override
public void onPostExecute(Exception e) {
infoText.setText(R.string.trafikantenErrorOther);
routeDeviLoader = null;
routeList.notifyDataSetChanged();
loadDevi();
}
@Override
public void onPreExecute() {}
});
if (routeDeviLoader.load(routeList.getList())) {
setProgressBarIndeterminateVisibility(true);
} else {
setProgressBarIndeterminateVisibility(false);
}
}
/*
* Click on a list item
* @see android.app.ListActivity#onListItemClick(android.widget.ListView, android.view.View, int, long)
*/
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
DetailedRouteView.ShowRoute(this, routeList.getList(), deviList, position);
}
/*
* onCreate - Context menu is a popup from a longpress on a list item.
* @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)
*/
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, MAP_ID, 0, R.string.map);
if (routeList.getDevi(((AdapterView.AdapterContextMenuInfo) menuInfo).position, true).size() > 0) {
menu.add(0, DEVI_ID, 0, R.string.warnings);
}
menu.add(0, NOTIFY_ID, 0, R.string.alarm);
}
/*
* onSelected - Context menu is a popup from a longpress on a list item.
* @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
*/
@Override
public boolean onContextItemSelected(MenuItem item) {
final AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
selectedId = info.position;
switch(item.getItemId()) {
case NOTIFY_ID:
final RouteData notifyRouteData = routeList.getItem(selectedId).travelStageList.get(0);
final String notifyWith = notifyRouteData.lineName.equals(notifyRouteData.destination) ? notifyRouteData.lineName : notifyRouteData.lineName + " " + notifyRouteData.destination;
new NotificationTask(this, routeList.getList(), selectedId, deviList, notifyRouteData.departure, notifyWith);
return true;
case DEVI_ID:
new SelectDeviTask(this, routeList.getDevi(info.position, false));
return true;
case MAP_ID:
GenericMap.Show(this, routeList.getList().get(selectedId).travelStageList, true, 0);
return true;
}
return super.onContextItemSelected(item);
}
@Override
protected void onStop() {
if (routeProvider != null) {
routeProvider.kill();
}
if (routeDeviLoader != null) {
routeDeviLoader.kill();
}
super.onStop();
}
/*
* saveInstanceState saves all variables needed for onCreate
* @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(RouteSearchData.PARCELABLE, routeSearch);
outState.putParcelableArrayList(OverviewRouteAdapter.KEY_ROUTELIST, routeList.getList());
outState.putParcelable(RouteDeviData.PARCELABLE, deviList);
}
}
class OverviewRouteAdapter extends BaseAdapter {
public static final String KEY_ROUTELIST = "routelist";
private LayoutInflater inflater;
private ArrayList<RouteProposal> items = new ArrayList<RouteProposal>();
final private OverviewRouteView parent;
public OverviewRouteAdapter(OverviewRouteView parent) {
inflater = LayoutInflater.from(parent);
this.parent = parent;
}
/*
* Simple functions dealing with adding/setting items.
*/
public ArrayList<RouteProposal> getList() { return items; }
public void setList(ArrayList<RouteProposal> items) { this.items = items; }
public void clear() { items.clear(); }
/*
* Standard android.widget.Adapter items, self explanatory.
*/
@Override
public int getCount() { return items.size(); }
@Override
public RouteProposal getItem(int pos) { return items.get(pos); }
@Override
public long getItemId(int pos) { return pos; }
public void addItem(RouteProposal item) { items.add(item); }
public ArrayList<DeviData> getDevi(int pos, boolean checkOnly) {
final ArrayList<DeviData> retList = new ArrayList<DeviData>();
for(RouteData routeData : items.get(pos).travelStageList) {
/*
* TODO, come up with a better way of id'ing the different values, using a string for this is dumb.
* - this is also in routeDeviLoader
*/
final String deviKey = parent.deviList.getDeviKey(routeData.fromStation.stationId, routeData.lineId);
ArrayList<DeviData> deviList = parent.deviList.items.get(deviKey);
if (deviList != null) {
for(DeviData devi : deviList) {
retList.add(devi);
if (checkOnly)
return retList;
}
}
}
return retList;
}
/*
* Setup the view
* @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
*/
@Override
public View getView(int pos, View convertView, ViewGroup arg2) {
/*
* Setup holder, for performance and readability.
*/
ViewHolder holder;
if (convertView == null) {
/*
* New view, inflate and setup holder.
*/
convertView = inflater.inflate(R.layout.route_overview_list, null);
holder = new ViewHolder();
holder.travelTypes = (LinearLayout) convertView.findViewById(R.id.travelTypes);
holder.departureTime = (TextView) convertView.findViewById(R.id.departureTime);
holder.arrivalTime = (TextView) convertView.findViewById(R.id.arrivalTime);
holder.travelTime = (TextView) convertView.findViewById(R.id.travelTime);
holder.deviSymbol = (TextView) convertView.findViewById(R.id.deviSymbol);
convertView.setTag(holder);
} else {
/*
* Old view found, we can reuse that instead of inflating.
*/
holder = (ViewHolder) convertView.getTag();
}
/*
* Render data to view.
*/
final RouteProposal routeProposal = items.get(pos);
long departure = 0;
long arrival = 0;
holder.travelTypes.removeAllViews();
for(RouteData routeData : routeProposal.travelStageList) {
/*
* Grab the first departure and last arrival to calculate total time
*/
if (departure == 0) {
departure = routeData.departure;
}
arrival = routeData.arrival;
/*
* Add Icon to travelTypes
*/
{
final int symbolImage = routeData.transportType;
if (symbolImage > 0) {
final LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.route_overview_traveltype, null);
final TextView line = (TextView) layout.findViewById(R.id.line);
final ImageView icon = (ImageView) layout.findViewById(R.id.icon);
icon.setImageResource(symbolImage);
if (routeData.lineName.length() > 0) {
line.setText(routeData.lineName);
line.setVisibility(View.VISIBLE);
} else {
line.setVisibility(View.GONE);
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.leftMargin = 1;
params.rightMargin = 1;
holder.travelTypes.addView(layout, params);
}
}
}
if (getDevi(pos, true).size() > 0) {
holder.deviSymbol.setVisibility(View.VISIBLE);
} else {
holder.deviSymbol.setVisibility(View.GONE);
}
{
/*
* Setup the basic data
*/
holder.departureTime.setText(HelperFunctions.hourFormater.format(departure));
holder.arrivalTime.setText(HelperFunctions.hourFormater.format(arrival));
long diff = arrival - departure;
if (diff < HelperFunctions.HOUR) {
holder.travelTime.setText(parent.getText(R.string.travelTime) + " : " + (diff / HelperFunctions.MINUTE) + " min");
} else {
diff = diff - HelperFunctions.HOUR; // no idea why this is needed, possibly timezone related when diff'ing two long's.
holder.travelTime.setText(parent.getText(R.string.travelTime) + " : " + HelperFunctions.hourFormater.format(diff));
}
}
return convertView;
}
/*
* Class for caching the view.
*/
static class ViewHolder {
LinearLayout travelTypes;
TextView travelTime;
TextView departureTime;
TextView arrivalTime;
TextView deviSymbol;
}
}