diff options
Diffstat (limited to 'src/net/cbaines/suma')
34 files changed, 5707 insertions, 0 deletions
diff --git a/src/net/cbaines/suma/AboutActivity.java b/src/net/cbaines/suma/AboutActivity.java new file mode 100644 index 0000000..b330596 --- /dev/null +++ b/src/net/cbaines/suma/AboutActivity.java @@ -0,0 +1,149 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.BaseExpandableListAdapter; +import android.widget.Button; +import android.widget.ExpandableListView; +import android.widget.TextView; + +public class AboutActivity extends Activity implements OnClickListener { + + static final int DONATE_DIALOG_ID = 0; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.about_dialog); + + ExpandableListView epView = (ExpandableListView) findViewById(R.id.helpExpandableListView); + AboutListAdapter mAdapter = new AboutListAdapter(this); + epView.setAdapter(mAdapter); + + Button donateButton = (Button) findViewById(R.id.donateButton); + donateButton.setOnClickListener(this); + + } + + class AboutListAdapter extends BaseExpandableListAdapter { + private String[] groups = { "Map", "Find", "Preferences", "Find my Location", "View", "About", "Favourites", "Licence", "Credits" }; + private LayoutInflater inflater; + private Context cxt; + + public AboutListAdapter(Context cxt) { + inflater = LayoutInflater.from(cxt); + this.cxt = cxt; + } + + public Object getChild(int groupPos, int childPos) { + if (groupPos == 0) { + return cxt.getResources().getString(R.string.map_help_message); + } else if (groupPos == 1) { + return cxt.getResources().getString(R.string.find_help_message); + } else if (groupPos == 1) { + return cxt.getResources().getString(R.string.preferences_help_message); + } else if (groupPos == 2) { + return cxt.getResources().getString(R.string.findmylocation_help_message); + } else if (groupPos == 3) { + return cxt.getResources().getString(R.string.view_help_message); + } else if (groupPos == 4) { + return cxt.getResources().getString(R.string.about_help_message); + } else if (groupPos == 5) { + return cxt.getResources().getString(R.string.favourites_help_message); + } else if (groupPos == 6) { + return cxt.getResources().getString(R.string.favourites_help_message); + } else if (groupPos == 7) { + return cxt.getResources().getString(R.string.licence_help_message); + } else if (groupPos == 8) { + return cxt.getResources().getString(R.string.credits_help_message); + } + return null; + } + + public long getChildId(int groupPos, int childPos) { + return childPos; + } + + public View getChildView(int groupPos, int childPos, boolean isLastChild, View convertView, ViewGroup parent) { + TextView tv = new TextView(cxt); + tv.setText(getChild(groupPos, childPos).toString()); + return tv; + } + + public int getChildrenCount(int groupPos) { + return 1; + } + + public Object getGroup(int groupPos) { + return groups[groupPos]; + } + + public int getGroupCount() { + return groups.length; + } + + public long getGroupId(int groupPos) { + return groupPos; + } + + public View getGroupView(int groupPos, boolean isExpanded, View convertView, ViewGroup parent) { + View v = null; + if (convertView != null) + v = convertView; + else + v = inflater.inflate(R.layout.view_group_row, parent, false); + String gt = (String) getGroup(groupPos); + TextView colorGroup = (TextView) v.findViewById(R.id.childname); + if (gt != null) + colorGroup.setText(gt); + return v; + } + + public boolean hasStableIds() { + return true; + } + + public boolean isChildSelectable(int groupPos, int childPos) { + return true; + } + + } + + public void onClick(View arg0) { + showDialog(DONATE_DIALOG_ID); + } + + protected Dialog onCreateDialog(int id) { + switch (id) { + case DONATE_DIALOG_ID: + DonateDialog donateDialog = new DonateDialog(this); + return donateDialog; + + } + return null; + } +} diff --git a/src/net/cbaines/suma/Building.java b/src/net/cbaines/suma/Building.java new file mode 100644 index 0000000..df8c303 --- /dev/null +++ b/src/net/cbaines/suma/Building.java @@ -0,0 +1,64 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import org.osmdroid.util.GeoPoint; + +import com.j256.ormlite.field.DataType; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "buildings") +public class Building extends POI { + public static final String NAME_FIELD_NAME = "name"; + public static final String RESIDENTIAL_FIELD_NAME = "residential"; + public static final String OUTLINE_FIELD_NAME = "outline"; + + @DatabaseField(canBeNull = false) + public String name; + @DatabaseField(canBeNull = false) + boolean residential; + @DatabaseField(dataType = DataType.SERIALIZABLE, canBeNull = true) + Polygon outline; + + Building(String id, GeoPoint point, boolean residential, String name, Polygon outline) { + super(id, point); + this.residential = residential; + this.name = name; + this.outline = outline; + this.type = POI.BUILDING; + } + + Building(String id, GeoPoint point, boolean residential, String name) { + this(id, point, residential, name, null); + } + + Building(String id, GeoPoint point, boolean residential) { + this(id, point, residential, ""); + } + + Building() { + this.type = POI.BUILDING; + } + + public String toString() { + return name + " (" + id + ")"; + } +} diff --git a/src/net/cbaines/suma/BuildingNumOverlay.java b/src/net/cbaines/suma/BuildingNumOverlay.java new file mode 100644 index 0000000..080cdc6 --- /dev/null +++ b/src/net/cbaines/suma/BuildingNumOverlay.java @@ -0,0 +1,273 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.osmdroid.views.MapView; +import org.osmdroid.views.MapView.Projection; +import org.osmdroid.views.overlay.Overlay; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.MotionEvent; +import android.widget.Toast; + +import com.j256.ormlite.android.apptools.OpenHelperManager; +import com.j256.ormlite.dao.Dao; + +public class BuildingNumOverlay extends Overlay { + + private ArrayList<Building> buildings; + + private final Point mCurScreenCoords = new Point(); + private final Point mTouchScreenPoint = new Point(); + private final Point mItemPoint = new Point(); + + private final Rect mRect = new Rect(); + + private final Drawable marker; + private final Drawable favMarker; + + private final Paint paint; + + private static final String TAG = "BuildingNumOverlay"; + + private final Context context; + + private Dao<Building, String> buildingDao; + + private float userScale = 1f; + + public BuildingNumOverlay(Context context, List<Building> buildings) throws SQLException { + super(context); + + this.context = context; + + marker = context.getResources().getDrawable(R.drawable.building); + favMarker = context.getResources().getDrawable(R.drawable.building_fav); + + DatabaseHelper helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + buildingDao = helper.getBuildingDao(); + + paint = new Paint(); + paint.setColor(Color.BLACK); + paint.setAntiAlias(true); + paint.setStyle(Style.FILL); + paint.setAlpha(120); + paint.setStrokeWidth(6); + paint.setTextAlign(Paint.Align.CENTER); + + this.buildings = (ArrayList<Building>) buildings; + } + + /** + * Draw a marker on each of our items. populate() must have been called first.<br/> + * <br/> + * The marker will be drawn twice for each Item in the Overlay--once in the shadow phase, skewed and darkened, then again in the non-shadow phase. The + * bottom-center of the marker will be aligned with the geographical coordinates of the Item.<br/> + * <br/> + * The order of drawing may be changed by overriding the getIndexToDraw(int) method. An item may provide an alternate marker via its + * OverlayItem.getMarker(int) method. If that method returns null, the default marker is used.<br/> + * <br/> + * The focused item is always drawn last, which puts it visually on top of the other items.<br/> + * + * @param canvas + * the Canvas upon which to draw. Note that this may already have a transformation applied, so be sure to leave it the way you found it + * @param mapView + * the MapView that requested the draw. Use MapView.getProjection() to convert between on-screen pixels and latitude/longitude pairs + * @param shadow + * if true, draw the shadow layer. If false, draw the overlay contents. + */ + @Override + public void draw(final Canvas canvas, final MapView mapView, final boolean shadow) { + + if (shadow) { + return; + } + + float scale = mScale * userScale; + + final Projection pj = mapView.getProjection(); + + final int markerWidth = (int) (marker.getIntrinsicWidth() * userScale); + final int markerHeight = (int) (marker.getIntrinsicHeight() * userScale); + + mRect.set(0, 0, 0 + markerWidth, 0 + markerHeight); + mRect.offset(-markerWidth / 2, -markerHeight); + marker.setBounds(mRect); + favMarker.setBounds(mRect); + + /* Draw in backward cycle, so the items with the least index are on the front. */ + for (Iterator<Building> buildingIter = buildings.iterator(); buildingIter.hasNext();) { + final Building building = buildingIter.next(); + + // Log.i(TAG, "Looking at drawing stop " + stop.id); + + pj.toMapPixels(building.point, mCurScreenCoords); + + // draw it + if (building.favourite) { + Overlay.drawAt(canvas, favMarker, mCurScreenCoords.x, mCurScreenCoords.y, false); + } else { + Overlay.drawAt(canvas, marker, mCurScreenCoords.x, mCurScreenCoords.y, false); + } + + String idString = String.valueOf(building.id); + + int yOfset = 10; + switch (idString.length()) { + case 1: + paint.setTextSize(25 * scale); + yOfset = 18; + break; + case 2: + paint.setTextSize(24 * scale); + yOfset = 18; + break; + case 3: + paint.setTextSize(17 * scale); + yOfset = 20; + break; + case 4: + paint.setTextSize(14 * scale); + yOfset = 23; + break; + case 5: + paint.setTextSize(10 * scale); + yOfset = 20; + break; + case 6: + paint.setTextSize(9 * scale); + yOfset = 24; + break; + default: + Log.w(TAG, "Reverting to default text size for length " + idString.length()); + paint.setTextSize(15 * scale); + break; + } + canvas.drawText(idString, mCurScreenCoords.x, mCurScreenCoords.y - (yOfset * scale), paint); + } + } + + @Override + public boolean onSingleTapUp(final MotionEvent event, final MapView mapView) { + if (!this.isEnabled()) + return false; + + final Building building = getSelectedItem(event, mapView); + + if (building == null) { + Log.i(TAG, "No building pressed"); + return false; + } else { + Log.i(TAG, "building Pressed " + building.id); + + Toast.makeText(context, building.name + " (" + building.id + ")", Toast.LENGTH_SHORT).show(); + return true; + } + + } + + @Override + public boolean onLongPress(final MotionEvent event, final MapView mapView) { + if (!this.isEnabled()) + return false; + + final Building building = getSelectedItem(event, mapView); + + if (building == null) { + Log.i(TAG, "No building pressed"); + return false; + } else { + Log.i(TAG, "building Pressed " + building.id); + + if (building.favourite) { + building.favourite = false; + + Toast.makeText(context, building.id + " removed from favourites", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(context, building.id + " made a favourite", Toast.LENGTH_SHORT).show(); + + building.favourite = true; + } + + try { + buildingDao.update(building); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + Collections.sort(buildings, new POIFavouriteComparator()); + + mapView.invalidate(); + + return true; + } + + } + + private Building getSelectedItem(final MotionEvent event, final MapView mapView) { + final Projection pj = mapView.getProjection(); + final int eventX = (int) event.getX(); + final int eventY = (int) event.getY(); + + /* These objects are created to avoid construct new ones every cycle. */ + pj.fromMapPixels(eventX, eventY, mTouchScreenPoint); + + // Iterate back through the array to properly deal with overlap + for (int i = buildings.size() - 1; i > 0; i--) { + final Building building = buildings.get(i); + + pj.toPixels(building.point, mItemPoint); + + if (marker.getBounds().contains(mTouchScreenPoint.x - mItemPoint.x, mTouchScreenPoint.y - mItemPoint.y)) { + return building; + } + } + return null; + } + + public void refresh() { + for (int i = 0; i < buildings.size(); i++) { + refresh(buildings.get(i)); + } + } + + public void refresh(Building building) { + if (building.favourite) { + buildings.remove(building); + buildings.add(building); + } + } + +} diff --git a/src/net/cbaines/suma/Bus.java b/src/net/cbaines/suma/Bus.java new file mode 100644 index 0000000..c73d6d7 --- /dev/null +++ b/src/net/cbaines/suma/Bus.java @@ -0,0 +1,57 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "buses") +public class Bus { + + @DatabaseField(id = true) + int id; + + @DatabaseField(canBeNull = true, foreign = true) + Stop lastKnownStop; + + @DatabaseField(canBeNull = true, foreign = true) + Stop firstKnownStop; + + @DatabaseField(canBeNull = false, foreign = true) + BusRoute lastKnownRoute; + + Bus() { + } + + Bus(int id, BusRoute lastKnownRoute, Stop lastKnownStop) { + this.id = id; + this.lastKnownRoute = lastKnownRoute; + this.lastKnownStop = lastKnownStop; + } + + Bus(int id, BusRoute lastKnownRoute) { + this(id, lastKnownRoute, null); + } + + public String toString() { + return String.valueOf(id); + } + +} diff --git a/src/net/cbaines/suma/BusRoute.java b/src/net/cbaines/suma/BusRoute.java new file mode 100644 index 0000000..016f3bd --- /dev/null +++ b/src/net/cbaines/suma/BusRoute.java @@ -0,0 +1,186 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.sql.SQLException; +import java.util.List; + +import android.content.Context; +import android.util.Log; + +import com.j256.ormlite.android.apptools.OpenHelperManager; +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.stmt.PreparedQuery; +import com.j256.ormlite.stmt.QueryBuilder; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "busroutes") +/** + * This class represents a bus route (U1, U1N, ..). + * + * @author Christopher Baines <cbaines8@gmail.com> + * + */ +public class BusRoute { + + final static String ID_FIELD_NAME = "id"; + final static String CODE_FIELD_NAME = "code"; + final static String LABEL_FIELD_NAME = "label"; + + private static final String TAG = "BusRoute"; + + @DatabaseField(id = true) + int id; + + @DatabaseField + String code; + + @DatabaseField + String label; + + BusRoute() { + } + + BusRoute(Integer id, String code, String label) { + this.id = id.intValue(); + this.code = code; + this.label = label; + } + + public String toString() { + return code; + } + + /** + * Untested? + * + * @param context + * @param stop + * @return + */ + BusStop getBusStopBefore(Context context, Stop stop) { + return moveInRoute(context, stop, -1); + } + + /** + * Untested? + * + * @param context + * @param stop + * @return + */ + BusStop getStopAfter(Context context, Stop stop) { + return moveInRoute(context, stop, 1); + } + + /** + * Untested? + * + * @param context + * @param stop + * @param moveAmount + * @return + */ + BusStop moveInRoute(Context context, Stop stop, int moveAmount) { + DatabaseHelper helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + + try { + Dao<RouteStops, Integer> routeStopsDao = helper.getRouteStopsDao(); + // Dao<Stop, Integer> stopDao = helper.getStopDao(); + Dao<BusStop, String> busStopDao = helper.getBusStopDao(); + + int stopSeq; + int beforeStopSeq = -1; + + QueryBuilder<RouteStops, Integer> routeStopsQueryBuilder = routeStopsDao.queryBuilder(); + routeStopsQueryBuilder.where().eq(RouteStops.ROUTE_ID_FIELD_NAME, this.id).and().eq(RouteStops.STOP_ID_FIELD_NAME, stop.busStop.id); + PreparedQuery<RouteStops> routeStopsPreparedQuery = routeStopsQueryBuilder.prepare(); + + List<RouteStops> stopsFound = routeStopsDao.query(routeStopsPreparedQuery); + if (stopsFound.size() != 0) { + Log.e(TAG, "Wierd, found more than one stop"); + return null; + } + + long maxSeq = 0; + + routeStopsQueryBuilder = routeStopsDao.queryBuilder(); + routeStopsQueryBuilder.where().eq(RouteStops.ROUTE_ID_FIELD_NAME, this.id); + routeStopsQueryBuilder.setCountOf(true); + routeStopsPreparedQuery = routeStopsQueryBuilder.prepare(); + + maxSeq = routeStopsDao.countOf(routeStopsPreparedQuery); + + if (maxSeq == 0) { + Log.e(TAG, "Something wierd has gone on, maxSeq equals 0"); + return null; + } + + if (id == 326) { // U1 + + if (stop.name.equals("U1C")) {// Seq 0 = End of route + + stopSeq = routeStopsDao.query(routeStopsPreparedQuery).get(0).sequence; + + beforeStopSeq = stopSeq + moveAmount; + + } else if (stop.name.equals("U1A")) { // seq 88 == end of route + + stopSeq = routeStopsDao.query(routeStopsPreparedQuery).get(0).sequence; + + beforeStopSeq = stopSeq - moveAmount; + + } else { + Log.e(TAG, "In route U1 but " + stop.name + " does not match U1A or U1C"); + return null; + } + } else { + Log.e(TAG, "Route id not recognised " + id); + return null; + } + + if (beforeStopSeq == -1) { + Log.e(TAG, "Something wierd has gone on, beforeStopSeq equals -1"); + return null; + } + + routeStopsQueryBuilder = routeStopsDao.queryBuilder(); + routeStopsQueryBuilder.where().eq(RouteStops.ROUTE_ID_FIELD_NAME, this.id).and().eq(RouteStops.SEQUENCE_ID_FIELD_NAME, beforeStopSeq); + + routeStopsPreparedQuery = routeStopsQueryBuilder.prepare(); + + List<RouteStops> beforeStopsFound = routeStopsDao.query(routeStopsPreparedQuery); + if (stopsFound.size() != 0) { + Log.e(TAG, "Wierd, found more than one before stop"); + return null; + } + + busStopDao.refresh(beforeStopsFound.get(0).stop); + + return beforeStopsFound.get(0).stop; + + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/src/net/cbaines/suma/BusStop.java b/src/net/cbaines/suma/BusStop.java new file mode 100644 index 0000000..8e13f27 --- /dev/null +++ b/src/net/cbaines/suma/BusStop.java @@ -0,0 +1,57 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import org.osmdroid.util.GeoPoint; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "busstops") +public class BusStop extends POI { + public static final String DESCRIPTION_FIELD_NAME = "description"; + public static final String BAY_FIELD_NAME = "bay"; + public static final String ROUTES_FIELD_NAME = "bay"; + + @DatabaseField(canBeNull = true) + public String description; + @DatabaseField(canBeNull = true) + public String bay; + + // Used to speed up accessing the relevent uni link routes for a bus stop, if == 0, this is not a uni link stop + @DatabaseField(canBeNull = false) + byte routes; + + BusStop(String location, String description, String bay, GeoPoint point) { + this.id = location; + this.description = description; + this.bay = bay; + this.point = point; + this.type = POI.BUS_STOP; + } + + BusStop() { + this.type = POI.BUS_STOP; + } + + public String toString() { + return description + " (" + id + ")"; + } +} diff --git a/src/net/cbaines/suma/BusStopOverlay.java b/src/net/cbaines/suma/BusStopOverlay.java new file mode 100644 index 0000000..69687b6 --- /dev/null +++ b/src/net/cbaines/suma/BusStopOverlay.java @@ -0,0 +1,309 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; + +import org.osmdroid.views.MapView; +import org.osmdroid.views.MapView.Projection; +import org.osmdroid.views.overlay.Overlay; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.MotionEvent; +import android.widget.Toast; + +import com.j256.ormlite.android.apptools.OpenHelperManager; +import com.j256.ormlite.dao.Dao; + +public class BusStopOverlay extends Overlay implements RouteColorConstants { + + private ArrayList<BusStop> busStops; + + private final Point mCurScreenCoords = new Point(); + private final Point mTouchScreenPoint = new Point(); + private final Point mItemPoint = new Point(); + + private final Rect mRect = new Rect(); + + private final Drawable marker; + private final Drawable favMarker; + + private final Paint paint; + + private static final String TAG = "BusStopOverlay"; + + private final Context context; + + private Dao<BusStop, String> busStopDao; + + private float userScale = 1f; + + private boolean[] routes = new boolean[5]; + + public BusStopOverlay(Context context) throws SQLException { + super(context); + + this.context = context; + + marker = context.getResources().getDrawable(R.drawable.busstop); + favMarker = context.getResources().getDrawable(R.drawable.busstop_fav); + + DatabaseHelper helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + busStopDao = helper.getBusStopDao(); + + paint = new Paint(); + paint.setStyle(Style.FILL); + paint.setStrokeWidth(6); + + busStops = new ArrayList<BusStop>((int) busStopDao.countOf()); + busStops.addAll(busStopDao.queryForAll()); + } + + void setRoutes(int route, boolean visible) { + routes[route] = visible; + } + + @Override + public void draw(final Canvas canvas, final MapView mapView, final boolean shadow) { + + if (shadow) { + return; + } + + float scale = mScale * userScale; + + final Projection pj = mapView.getProjection(); + + final int markerWidth = (int) (marker.getIntrinsicWidth() * userScale); + final int markerHeight = (int) (marker.getIntrinsicHeight() * userScale); + + mRect.set(0, 0, 0 + markerWidth, 0 + markerHeight); + mRect.offset(-markerWidth / 2, -markerHeight); + marker.setBounds(mRect); + favMarker.setBounds(mRect); + + /* Draw in backward cycle, so the items with the least index are on the front. */ + + for (int stopNum = 0; stopNum < busStops.size(); stopNum++) { + BusStop stop = busStops.get(stopNum); + + final byte stopRoutes = stop.routes; + byte routeNum = 0; + boolean drawing = false; + + for (int i = 0; i < 5; i++) { + if ((stopRoutes & (1 << i)) != 0) { + routeNum++; + if (routes[i]) { + drawing = true; + } + } + } + + if (!drawing) + continue; + + int yOfsetPerMarker = (int) (10 * scale); + int markerYSize = (int) (8 * scale); + + pj.toMapPixels(stop.point, mCurScreenCoords); + + if (stop.favourite) { + Overlay.drawAt(canvas, favMarker, mCurScreenCoords.x, mCurScreenCoords.y, false); + } else { + Overlay.drawAt(canvas, marker, mCurScreenCoords.x, mCurScreenCoords.y, false); + } + // Log.i(TAG, "Got " + routes.size() + " routes " + routes); + + int makersPlaced = 0; + + float rectLeft = mCurScreenCoords.x + (8.8f * scale); + float rectRight = rectLeft + markerYSize; + + if (routeNum == 5) { + markerYSize = (int) (5 * scale); + yOfsetPerMarker = (int) (7 * scale); + } else if (routeNum == 4) { + markerYSize = (int) (6.5f * scale); + yOfsetPerMarker = (int) (8 * scale); + } + + for (int i = 0; i < 5; i++) { + if ((stopRoutes & (1 << i)) != 0) { + + // Log.i(TAG, "Route " + route + " is " + routes.get(route)); + + // Log.i(TAG, "Index is " + busRoutes.indexOf(route) + " busRoutes " + busRoutes); + + if (i == 0) { + paint.setColor(U1); + } else if (i == 1) { + paint.setColor(U1N); + } else if (i == 2) { + paint.setColor(U2); + } else if (i == 3) { + paint.setColor(U6); + } else if (i == 4) { + paint.setColor(U9); + } else { + Log.e(TAG, "Unknown route code"); + } + + canvas.drawRect(rectLeft, mCurScreenCoords.y + ((yOfsetPerMarker * makersPlaced) - (45 * scale)), rectRight, mCurScreenCoords.y + + (yOfsetPerMarker * makersPlaced) - ((45 * scale) - markerYSize), paint); + + makersPlaced++; + } + } + + } + } + + @Override + public boolean onSingleTapUp(final MotionEvent event, final MapView mapView) { + + BusStop busStop = getSelectedItem(event, mapView); + + if (busStop == null) { + Log.i(TAG, "No busStop pressed"); + + return false; + } else { + Log.i(TAG, "Pressed " + busStop.id); + + Intent i = new Intent(context, BusTimeActivity.class); + i.putExtra("busStopID", busStop.id); + i.putExtra("busStopName", busStop.description); + ((Activity) context).startActivityForResult(i, 0); + + return true; + } + + } + + @Override + public boolean onLongPress(final MotionEvent event, final MapView mapView) { + BusStop busStop = getSelectedItem(event, mapView); + + if (busStop == null) { + Log.i(TAG, "No busStop pressed"); + return false; + } else { + Log.i(TAG, "Pressed " + busStop.id); + + if (busStop.favourite) { + busStop.favourite = false; + + Toast.makeText(context, busStop.id + " removed from favourites", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(context, busStop.id + " made a favourite", Toast.LENGTH_SHORT).show(); + + busStop.favourite = true; + } + + try { + busStopDao.update(busStop); + } catch (SQLException e) { + e.printStackTrace(); + } + + Collections.sort(busStops, new POIFavouriteComparator()); + + mapView.invalidate(); + + return true; + } + + } + + public void refresh() { + try { + for (int i = 0; i < busStops.size(); i++) { + BusStop busStop = busStops.get(i); + busStopDao.refresh(busStop); + if (busStop.favourite) { + busStops.remove(i); + busStops.add(busStop); + } else { + busStops.set(i, busStop); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * Replaces any bus stops that equal the argument in the overlay with the argument + * + * @param busStop + */ + public void refresh(BusStop busStop) { + for (int i = 0; i < busStops.size(); i++) { + if (busStop.equals(busStops.get(i))) { + busStops.set(i, busStop); + } + } + } + + private BusStop getSelectedItem(final MotionEvent event, final MapView mapView) { + final Projection pj = mapView.getProjection(); + final int eventX = (int) event.getX(); + final int eventY = (int) event.getY(); + + /* These objects are created to avoid construct new ones every cycle. */ + pj.fromMapPixels(eventX, eventY, mTouchScreenPoint); + + for (int i = busStops.size() - 1; i > 0; i--) { + BusStop busStop = busStops.get(i); + + pj.toPixels(busStop.point, mItemPoint); + + if (marker.getBounds().contains(mTouchScreenPoint.x - mItemPoint.x, mTouchScreenPoint.y - mItemPoint.y)) { + boolean drawing = false; + for (int route = 0; route < 5; route++) { + if ((busStop.routes & (1 << route)) != 0) { + if (routes[route]) { + drawing = true; + break; + } + } + } + if (!drawing) + continue; + + return busStop; + } + } + return null; + } + +} diff --git a/src/net/cbaines/suma/BusStopView.java b/src/net/cbaines/suma/BusStopView.java new file mode 100644 index 0000000..d76198b --- /dev/null +++ b/src/net/cbaines/suma/BusStopView.java @@ -0,0 +1,34 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import android.content.Context; + +public class BusStopView extends POIView { + + public BusStopView(Context context, BusStop busStop) { + this(context, busStop, -1); + } + + public BusStopView(Context context, BusStop busStop, int dist) { + super(context, (POI) busStop, dist); + } + +} diff --git a/src/net/cbaines/suma/BusTimeActivity.java b/src/net/cbaines/suma/BusTimeActivity.java new file mode 100644 index 0000000..be8b14a --- /dev/null +++ b/src/net/cbaines/suma/BusTimeActivity.java @@ -0,0 +1,247 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.sql.SQLException; + +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.j256.ormlite.android.apptools.OrmLiteBaseActivity; +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.stmt.PreparedQuery; +import com.j256.ormlite.stmt.QueryBuilder; + +public class BusTimeActivity extends OrmLiteBaseActivity<DatabaseHelper> implements Runnable, OnCheckedChangeListener { + + final static String TAG = "BusTimeActivity"; + + private boolean dataChanged; + + private ListView busTimeList; + private TextView busName; + private TextView busID; + private CheckBox busFavourite; + private TextView busStopMessage; + private ProgressBar progBar; + private LinearLayout busTimeContentLayout; + + private TimetableAdapter adapter; + + private String busStopID; + private String busStopName; + + private Dao<BusStop, String> busStopDao; + + private BusStop busStop; + + private Thread timetableThread; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.bustimes); + + DatabaseHelper helper = getHelper(); + + busStopID = getIntent().getExtras().getString("busStopID"); + busStopName = getIntent().getExtras().getString("busStopName"); + + TextView U1RouteTextView = (TextView) findViewById(R.id.busStopU1); + TextView U1NRouteTextView = (TextView) findViewById(R.id.busStopU1N); + TextView U2RouteTextView = (TextView) findViewById(R.id.busStopU2); + TextView U6RouteTextView = (TextView) findViewById(R.id.busStopU6); + TextView U9RouteTextView = (TextView) findViewById(R.id.busStopU9); + + try { + Dao<BusRoute, Integer> busRouteDao = helper.getBusRouteDao(); + Dao<RouteStops, Integer> routeStopsDao = helper.getRouteStopsDao(); + + for (BusRoute route : busRouteDao) { + QueryBuilder<RouteStops, Integer> queryBuilder = routeStopsDao.queryBuilder(); + + queryBuilder.where().eq(RouteStops.ROUTE_ID_FIELD_NAME, route.id).and().eq(RouteStops.STOP_ID_FIELD_NAME, busStopID); + queryBuilder.setCountOf(true); + PreparedQuery<RouteStops> preparedQuery = queryBuilder.prepare(); + + long count = routeStopsDao.countOf(preparedQuery); + + if (route.code.equals("U1")) { + if (count != 0) { + U1RouteTextView.setVisibility(View.VISIBLE); + } else { + U1RouteTextView.setVisibility(View.GONE); + } + } else if (route.code.equals("U1N")) { + if (count != 0) { + U1NRouteTextView.setVisibility(View.VISIBLE); + } else { + U1NRouteTextView.setVisibility(View.GONE); + } + } else if (route.code.equals("U2")) { + if (count != 0) { + U2RouteTextView.setVisibility(View.VISIBLE); + } else { + U2RouteTextView.setVisibility(View.GONE); + } + } else if (route.code.equals("U6")) { + if (count != 0) { + U6RouteTextView.setVisibility(View.VISIBLE); + } else { + U6RouteTextView.setVisibility(View.GONE); + } + } else if (route.code.equals("U9")) { + if (count != 0) { + U9RouteTextView.setVisibility(View.VISIBLE); + } else { + U9RouteTextView.setVisibility(View.GONE); + } + } else { + Log.e(TAG, "Error unknown route " + route.code); + } + + } + + busStopDao = helper.getBusStopDao(); + + busStop = busStopDao.queryForId(busStopID); + + busFavourite = (CheckBox) findViewById(R.id.favouriteCheckBox); + busFavourite.setChecked(busStop.favourite); + busFavourite.setOnCheckedChangeListener(this); + + } catch (SQLException e) { + e.printStackTrace(); + } + + busName = (TextView) findViewById(R.id.busStopName); + busID = (TextView) findViewById(R.id.busStopID); + + busStopMessage = (TextView) findViewById(R.id.busStopMessage); + progBar = (ProgressBar) findViewById(R.id.busStopLoadBar); + busTimeList = (ListView) findViewById(R.id.busStopTimes); + busTimeContentLayout = (LinearLayout) findViewById(R.id.busTimeContentLayout); + + Log.i(TAG, "Got busstop id " + busStopID); + + busName.setText(busStopName); + busID.setText(busStopID); + + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + if (sharedPrefs.getBoolean("liveBusTimesEnabled", false)) { + timetableThread = new Thread(this); + timetableThread.start(); + } else { + progBar.setVisibility(View.GONE); + busStopMessage.setText("Live bus times disabled"); + busStopMessage.setVisibility(View.VISIBLE); + } + + } + + public void finish() { + Log.i(TAG, "Stopping BusTimeActivity thread"); + if (timetableThread != null) { // Could happen if live bus times are disabled + timetableThread.interrupt(); + } + + if (dataChanged) { + getIntent().putExtra("busStopChanged", busStopID); + } + + setResult(RESULT_OK, getIntent()); + + super.finish(); + } + + public void run() { + while (true) { + try { + Timetable timetable = DataManager.getTimetable(this, busStopID, true); + + Log.i(TAG, "Got timetable for " + busStopID); + if (timetable == null) { + Log.i(TAG, "Its null"); + busTimeList.post(new Runnable() { + public void run() { + progBar.setVisibility(View.GONE); + busStopMessage.setText("Error fetching bus times"); + busStopMessage.setVisibility(View.VISIBLE); + } + }); + } else { + Log.i(TAG, "It contains " + timetable.size() + " stops"); + + if (timetable.size() == 0) { + busTimeList.post(new Runnable() { + public void run() { + progBar.setVisibility(View.GONE); + busStopMessage.setText("No Busses"); + busStopMessage.setVisibility(View.VISIBLE); + } + }); + } else { + + adapter = new TimetableAdapter(this, timetable); + + busTimeList.post(new Runnable() { + public void run() { + progBar.setVisibility(View.GONE); + busStopMessage.setVisibility(View.GONE); + busTimeList.setAdapter(adapter); + busTimeContentLayout.setGravity(Gravity.TOP); + } + }); + } + } + + } catch (SQLException e1) { + e1.printStackTrace(); + } + + try { + Thread.sleep(20000); + } catch (InterruptedException e) { + Log.i(TAG, "Bus stop activity thread stoped"); + break; + } + } + } + + public void onCheckedChanged(CompoundButton arg0, boolean arg1) { + busStop.favourite = arg1; + try { + busStopDao.update(busStop); + dataChanged = true; + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/src/net/cbaines/suma/DataHandler.java b/src/net/cbaines/suma/DataHandler.java new file mode 100644 index 0000000..f6263da --- /dev/null +++ b/src/net/cbaines/suma/DataHandler.java @@ -0,0 +1,114 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import org.osmdroid.ResourceProxy; +import org.osmdroid.views.overlay.PathOverlay; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import android.util.Log; + +public class DataHandler extends DefaultHandler { + + // this holds the data + private PathOverlay _data; + + private int colour; + private ResourceProxy resProxy; + + public DataHandler(int colour, ResourceProxy resProxy) { + this.colour = colour; + this.resProxy = resProxy; + } + + /** + * Returns the data object + * + * @return + */ + public PathOverlay getData() { + return _data; + } + + /** + * This gets called when the xml document is first opened + * + * @throws SAXException + */ + @Override + public void startDocument() throws SAXException { + _data = new PathOverlay(colour, resProxy); + } + + /** + * Called when it's finished handling the document + * + * @throws SAXException + */ + @Override + public void endDocument() throws SAXException { + + } + + /** + * This gets called at the start of an element. Here we're also setting the booleans to true if it's at that specific tag. (so we know where we are) + * + * @param namespaceURI + * @param localName + * @param qName + * @param atts + * @throws SAXException + */ + @Override + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + if (localName.equals("trkpt")) { + Log.v("DataHandler", "Adding point to route overlay " + atts.getValue("lat") + " " + atts.getValue("lon")); + _data.addPoint(Util.csLatLongToGeoPoint(atts.getValue("lat"), atts.getValue("lon"))); + } + } + + /** + * Called at the end of the element. Setting the booleans to false, so we know that we've just left that tag. + * + * @param namespaceURI + * @param localName + * @param qName + * @throws SAXException + */ + @Override + public void endElement(String namespaceURI, String localName, String qName) throws SAXException { + + } + + /** + * Calling when we're within an element. Here we're checking to see if there is any content in the tags that we're interested in and populating it in the + * Config object. + * + * @param ch + * @param start + * @param length + */ + @Override + public void characters(char ch[], int start, int length) { + + } +} diff --git a/src/net/cbaines/suma/DataManager.java b/src/net/cbaines/suma/DataManager.java new file mode 100644 index 0000000..e910c8e --- /dev/null +++ b/src/net/cbaines/suma/DataManager.java @@ -0,0 +1,726 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.sql.SQLException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Iterator; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.DefaultHttpClient; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.osmdroid.ResourceProxy; +import org.osmdroid.util.GeoPoint; +import org.osmdroid.views.overlay.PathOverlay; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +import android.content.Context; +import android.util.Log; + +import com.j256.ormlite.android.apptools.OpenHelperManager; +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.stmt.PreparedQuery; +import com.j256.ormlite.stmt.QueryBuilder; +import com.j256.ormlite.table.TableUtils; + +public class DataManager { + + final static String TAG = "DataManager"; + + final static String busStopUrl = "http://data.southampton.ac.uk/bus-stop/"; + + private static Context context; + + private static DatabaseHelper helper; + private static Dao<BusRoute, Integer> busRoutes; + private static Dao<Bus, Integer> busDao; + private static Dao<BusStop, String> busStopDao; + private static Dao<Stop, Integer> stopDao; + + public static void loadBuildings(Context context) throws SQLException, IOException { + DatabaseHelper helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + Dao<Building, String> buildingDao = helper.getBuildingDao(); + + TableUtils.clearTable(helper.getConnectionSource(), Building.class); + + Log.i(TAG, "Loading buildings from csv"); + + HashMap<String, GeoPoint> buildingPoints = new HashMap<String, GeoPoint>(); + HashMap<String, Polygon> buildingPolys = new HashMap<String, Polygon>(); + + InputStream inputStream = context.getAssets().open("buildings_points.csv"); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + String strLine; + try { + String def = bufferedReader.readLine(); + // Log.i(TAG, "Reading the definition " + def); + + while ((strLine = bufferedReader.readLine()) != null) { + // Log.i(TAG, "Data: " + strLine); + String[] dataBits = strLine.split(","); + GeoPoint point = Util.csLatLongToGeoPoint(dataBits[2], dataBits[1]); + // Log.i(TAG, "Creating building with id " + dataBits[0] + " and " + point.getLatitudeE6() + " " + point.getLongitudeE6()); + buildingPoints.put(dataBits[0], point); + } + + bufferedReader.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + Log.i(TAG, "Number of building points " + buildingPoints.size()); + + /* + * inputStream = context.getResources().openRawResource(R.raw.buildings_shapes); bufferedReader = new BufferedReader(new + * InputStreamReader(inputStream)); + * + * try { String def = bufferedReader.readLine(); // Log.i(TAG, "Reading the definition " + def); + * + * while ((strLine = bufferedReader.readLine()) != null) { // Log.i(TAG, "Data: " + strLine); String[] dataBits = strLine.split(","); Polygon poly = + * Util.csPolygonToPolygon(strLine.split("\"")[1]); // Log.i(TAG, "Creating building with id " + dataBits[0] + " and " + poly); + * buildingPolys.put(dataBits[0], poly); } + * + * bufferedReader.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } + * + * Log.i(TAG, "Number of polys points " + buildingPolys.size()); + */ + + inputStream = context.getAssets().open("building_estates.csv"); + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + + try { + String def = bufferedReader.readLine(); + // Log.i(TAG, "Reading the definition " + def); + + while ((strLine = bufferedReader.readLine()) != null) { + // Log.i(TAG, "Data: " + strLine); + if (strLine.startsWith("\"")) { + String[] quoteBits = strLine.split("\""); + // Log.i(TAG, "QuoteBits " + quoteBits[0] + " | " + quoteBits[1]); + String[] dataBits = quoteBits[2].split(","); + // Log.i(TAG, "dataBits " + dataBits[0] + " | " + dataBits[1]); + + if (buildingPoints.get(dataBits[1]) == null) { + // Log.w(TAG, "Building " + dataBits[1] + " has a null point"); + continue; + } + + Building bdg = new Building(dataBits[1], buildingPoints.get(dataBits[1]), dataBits[3].equals("R"), quoteBits[0]); + /* + * Polygon poly = buildingPolys.get(dataBits[1]); + * + * if (poly != null) { bdg.outline = poly; // Log.i(TAG, "Adding building " + key + " " + bdg.point.getLatitudeE6() + " " + + * bdg.point.getLongitudeE6() + " " + poly); } else { // Log.i(TAG, "Adding building " + key + " " + bdg.point.getLatitudeE6() + " " + + * bdg.point.getLongitudeE6()); } + */ + + // Log.i(TAG, "Creating building " + bdg.id + " " + bdg.name + " " + bdg.point + " " + bdg.residential + " " + bdg.outline); + + buildingDao.create(bdg); + + } else { + + String[] dataBits = strLine.split(","); + + if (buildingPoints.get(dataBits[1]) == null) { + // Log.w(TAG, "Building " + dataBits[1] + " has a null point"); + continue; + } + + Building bdg = new Building(dataBits[1], buildingPoints.get(dataBits[1]), dataBits[3].equals("R"), dataBits[0]); + /* + * Polygon poly = buildingPolys.get(dataBits[1]); + * + * if (poly != null) { bdg.outline = poly; // Log.i(TAG, "Adding building " + key + " " + bdg.point.getLatitudeE6() + " " + + * bdg.point.getLongitudeE6() + " " + poly); } else { // Log.i(TAG, "Adding building " + key + " " + bdg.point.getLatitudeE6() + " " + + * bdg.point.getLongitudeE6()); } + */ + + // Log.i(TAG, "Creating building " + bdg.id + " " + bdg.name + " " + bdg.point + " " + bdg.residential + " " + bdg.outline); + + buildingDao.create(bdg); + + } + } + + bufferedReader.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + /* + * for (Iterator<String> iter = buildingPoints.keySet().iterator(); iter.hasNext();) { String key = iter.next(); + * + * Building bdg = new Building(key, buildingPoints.get(key), false); Polygon poly = buildingPolys.get(key); + * + * if (poly != null) { bdg.outline = poly; // Log.i(TAG, "Adding building " + key + " " + bdg.point.getLatitudeE6() + " " + bdg.point.getLongitudeE6() + + * " " + poly); } else { // Log.i(TAG, "Adding building " + key + " " + bdg.point.getLatitudeE6() + " " + bdg.point.getLongitudeE6()); } + * + * buildingDao.create(bdg); } + */ + + } + + public static void loadBusData(Context context, boolean onlyUniLink) throws SQLException, IOException { + DatabaseHelper helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + + Dao<BusStop, String> busStopDao = helper.getBusStopDao(); + Dao<BusRoute, Integer> busRouteDao = helper.getBusRouteDao(); + Dao<RouteStops, Integer> routeStopsDao = helper.getRouteStopsDao(); + + TableUtils.clearTable(helper.getConnectionSource(), BusStop.class); + TableUtils.clearTable(helper.getConnectionSource(), BusRoute.class); + TableUtils.clearTable(helper.getConnectionSource(), RouteStops.class); + + Log.i(TAG, "Loading busstops from csv"); + + InputStream inputStream = context.getAssets().open("bus_stops.csv"); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + String strLine = ""; + + try { + String def = bufferedReader.readLine(); + // Log.i(TAG, "Reading the definition " +def ); + + while ((strLine = bufferedReader.readLine()) != null) { + // Log.i(TAG, "Data: " + strLine); + String[] dataBits = strLine.split(","); + + String[] quBitsLat = dataBits[3].substring(1, dataBits[3].length() - 1).split(" "); + String[] quBitsLng = dataBits[4].substring(1, dataBits[4].length() - 1).split(" "); + + // Log.i(TAG, "Whole " + dataBits[3] + " First bit " + quBitsLat[0] + " last bit " + quBitsLat[1]); + double lat = Double.valueOf(quBitsLat[0]) + Double.valueOf(quBitsLat[1].substring(0, quBitsLat[1].length() - 1)) / 60d; // TODO Much hackage + // Log.i(TAG, "Whole " + dataBits[4] + " First bit " + quBitsLng[0] + " last bit " + quBitsLng[1]); + double lng = Double.valueOf(quBitsLng[0]) + Double.valueOf(quBitsLng[1].substring(0, quBitsLng[1].length() - 1)) / 60d; // TODO Much hackage + GeoPoint point = new GeoPoint((int) (lat * 1e6), (int) (lng * -1e6)); + // Log.i(TAG, "Lat " + point.getLatitudeE6() + " lng " + point.getLongitudeE6()); + + busStopDao.create(new BusStop(dataBits[0].replace("\"", ""), dataBits[1].replace("\"", ""), dataBits[2].replace("\"", ""), point)); + + } + + bufferedReader.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + Log.e(TAG, "Line: " + strLine); + e.printStackTrace(); + } + + Log.i(TAG, "Finished loading busstops, now loading routes"); + + inputStream = context.getAssets().open("routes.csv"); + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + + try { + String def = bufferedReader.readLine(); + // Log.i(TAG, "Reading the definition " + def); + + while ((strLine = bufferedReader.readLine()) != null) { + // Log.i(TAG, "Data: " + strLine); + String[] dataBits = strLine.split(","); + + BusRoute route = new BusRoute(Integer.parseInt(dataBits[0]), dataBits[1], dataBits[2].replace("\"", "")); + // Log.i(TAG, "Loaded route " + route.id + " " + route.code + " " + route.label); + busRouteDao.create(route); + + } + + bufferedReader.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + Log.e(TAG, "Line: " + strLine); + e.printStackTrace(); + } + + Log.i(TAG, "Finished loading routes, now loading routestops"); + + inputStream = context.getAssets().open("routestops.csv"); + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + + try { + String def = bufferedReader.readLine(); + // Log.i(TAG, "Reading the definition " + def); + + while ((strLine = bufferedReader.readLine()) != null) { + // Log.i(TAG, "Data: " + strLine); + String[] dataBits = strLine.split(","); + + BusStop stop = busStopDao.queryForId(dataBits[2]); + if (stop != null) { + // Log.i(TAG, "Found stop " + stop.id); + } else { + Log.w(TAG, "No stop found for " + dataBits[2]); + continue; + } + + BusRoute route = busRouteDao.queryForId(Integer.parseInt(dataBits[0])); + if (route != null) { + // Log.i(TAG, "Found route " + route.id); + } else { + Log.w(TAG, "No route found for " + dataBits[0]); + continue; + } + + int sequence = Integer.parseInt(dataBits[1]); + Log.i(TAG, "Creating RouteStop " + stop.id + " " + route.code + " " + sequence); + + routeStopsDao.create(new RouteStops(stop, route, sequence)); + + if (route.id == 326) { // U1 + stop.routes = (byte) (stop.routes | 1); + } else if (route.id == 468) { // U1N + stop.routes = (byte) (stop.routes | (1 << 1)); + } else if (route.id == 329) { // U2 + stop.routes = (byte) (stop.routes | (1 << 2)); + } else if (route.id == 327) { // U6 + stop.routes = (byte) (stop.routes | (1 << 3)); + } else if (route.id == 354) { // U9 + stop.routes = (byte) (stop.routes | (1 << 4)); + } + Log.v(TAG, "Stop routes " + stop.routes); + busStopDao.update(stop); + + } + + bufferedReader.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + Log.e(TAG, "Line: " + strLine); + e.printStackTrace(); + } + + // TODO: Seperate non unilink stuff in to a different table + if (onlyUniLink) { + + long sizeBeforeRemoval = busStopDao.countOf(); + + // Removing busstops not used by unilink busses + for (Iterator<BusStop> busStopIter = busStopDao.iterator(); busStopIter.hasNext();) { + BusStop stop = busStopIter.next(); + // Log.i(TAG, "Looking at stop " + stop.id); + + /* + * QueryBuilder<RouteStops, Integer> routeStopsQueryBuilder = routeStopsDao.queryBuilder(); routeStopsQueryBuilder.where().eq(columnName, value) + * + * DeleteBuilder<BusStop, String> deleteBuilder = busStopDao.deleteBuilder(); // only delete the rows where password is null + * deleteBuilder.where().in(RouteStops.STOP_ID_FIELD_NAME, objects) accountDao.delete(deleteBuilder.prepare()); + */ + + QueryBuilder<RouteStops, Integer> routeStopsQueryBuilder = routeStopsDao.queryBuilder(); + routeStopsQueryBuilder.setCountOf(true); + routeStopsQueryBuilder.where().eq(RouteStops.STOP_ID_FIELD_NAME, stop); + + PreparedQuery<RouteStops> routeStopsPreparedQuery = routeStopsQueryBuilder.prepare(); + long num = routeStopsDao.countOf(routeStopsPreparedQuery); + // long num = routeStopsDao.query(routeStopsPreparedQuery).size(); + // Log.i(TAG, "Number is " + num); + if (num == 0) { + // Log.i(TAG, "Removing " + stop.id); + busStopIter.remove(); + } + } + + long sizeAfterRemoval = busStopDao.countOf(); + + Log.i(TAG, "Removed " + (sizeBeforeRemoval - sizeAfterRemoval) + " stops (from " + sizeBeforeRemoval + ") now have " + sizeAfterRemoval); + + } + + Log.i(TAG, "Finished loading bus data"); + } + + public static void loadSiteData(Context context) throws SQLException, IOException { + Log.i(TAG, "Begining loading site data"); + + DatabaseHelper helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + + TableUtils.clearTable(helper.getConnectionSource(), Site.class); + + Dao<Site, String> siteDao = helper.getSiteDao(); + + InputStream inputStream = context.getAssets().open("sites.csv"); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + String strLine = null; + + try { + String def = bufferedReader.readLine(); + // Log.i(TAG, "Reading the site definition " + def); + + while ((strLine = bufferedReader.readLine()) != null) { + // Log.i(TAG, "Site Data: " + strLine); + String[] dataBits = strLine.split(","); + + GeoPoint point = null; + if (dataBits[2].length() > 1 && dataBits[3].length() > 1) { + point = Util.csLatLongToGeoPoint(dataBits[2], dataBits[3]); + } else { + point = new GeoPoint(0, 0); + } + + Polygon poly = Util.csPolygonToPolygon(strLine.split("\"")[1]); + // Log.i(TAG, "Polygon: " + poly); + + siteDao.create(new Site(dataBits[0], dataBits[1], point, poly)); + } + + bufferedReader.close(); + } catch (Exception e) { + // TODO Auto-generated catch block + Log.e(TAG, "Site Line: " + strLine); + e.printStackTrace(); + } + + Log.i(TAG, "Loaded sites from csv"); + } + + private static Stop getStop(Context context, JSONObject stopObj, BusStop busStop) throws SQLException { + + if (helper == null) + helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + if (busRoutes == null) + busRoutes = helper.getBusRouteDao(); + if (busDao == null) + busDao = helper.getBusDao(); + if (busStopDao == null) + busStopDao = helper.getBusStopDao(); + if (stopDao == null) + stopDao = helper.getStopDao(); + + // Log.i(TAG, "Stop " + stopObj); + + try { + String time = stopObj.getString("time"); + + GregorianCalendar calender = new GregorianCalendar(); + + if (!time.equals("Due")) { + + Log.i(TAG, "Time: " + time + " current time " + calender.getTime()); + + if (time.contains(":")) { + String[] minAndHour = time.split(":"); + calender.set(Calendar.HOUR_OF_DAY, Integer.parseInt(minAndHour[0])); + calender.set(Calendar.MINUTE, Integer.parseInt(minAndHour[1])); + } else { + // Log.i(TAG, "Parsing " + time.substring(0, time.length() - 1) + " for min"); + calender.add(Calendar.MINUTE, Integer.parseInt(time.substring(0, time.length() - 1))); + } + + Log.i(TAG, "Date: " + calender.getTime()); + + } + + Stop stop; + + String name = stopObj.getString("name"); + + BusRoute route; + + if (name.equals("U1N")) { + route = busRoutes.queryForId(468); + } else if (name.startsWith("U1")) { + route = busRoutes.queryForId(326); + } else if (name.startsWith("U2")) { + route = busRoutes.queryForId(329); + } else if (name.startsWith("U6")) { + route = busRoutes.queryForId(327); + } else if (name.startsWith("U9")) { + route = busRoutes.queryForId(354); + } else { + Log.e(TAG, "Error selecting route for " + name); + return null; + } + + String destString = stopObj.getString("dest"); + BusStop destStop; + + if (destString.equals("Central Station")) { + destStop = busStopDao.queryForId("SNA19709"); + } else if (destString.equals("Civic Centre")) { + destStop = busStopDao.queryForId("SN120527"); + } else if (destString.equals("City DG4")) { + destStop = busStopDao.queryForId("HAA13579"); + } else if (destString.equals("Central Station")) { + destStop = busStopDao.queryForId("SN120520"); + } else if (destString.equals("Airport")) { + destStop = busStopDao.queryForId("HA030184"); + } else if (destString.equals("City, Town Quay")) { + destStop = busStopDao.queryForId("SNA13766"); + } else if (destString.equals("Dock Gate 4")) { + destStop = busStopDao.queryForId("MG1031"); + } else if (destString.equals("Eastleigh")) { + destStop = busStopDao.queryForId("HA030212"); + } else if (destString.equals("Crematorium")) { + destStop = busStopDao.queryForId("SN121009"); + } else if (destString.equals("General Hosp")) { + destStop = busStopDao.queryForId("SNA19482"); + } else { + Log.e(TAG, "Unknown end dest " + destString + " for route " + route.code); + return null; + } + + if (stopObj.has("vehicle")) { + + int vehicle = Integer.parseInt(stopObj.getString("vehicle")); + // Log.i(TAG, "Looking at vehicle " + vehicle + " (" + stopObj.getString("vehicle") + ")"); + Bus bus = busDao.queryForId(vehicle); + if (bus == null) { + // Log.i(TAG, "Cant find vehicle, creating"); + + // for (Bus gotBus : busDao) { + // Log.i(TAG, "Currently have bus " + gotBus.id); + // } + + bus = new Bus(vehicle, route); + busDao.create(bus); + } + + Date now = new Date(System.currentTimeMillis()); + + stop = new Stop(stopObj.getString("name"), busStop, destStop, bus, calender.getTime(), now); + + /* + * if (bus.lastKnownStop != null) { stopDao.delete(bus.lastKnownStop); // TODO Crude, might delete useful data + * + * if (bus.lastKnownStop.arivalTime == null) { Log.e(TAG, " bus.lastKnownStop.arivalTime is null"); } else if (stop.arivalTime == null) { + * Log.e(TAG, " stop.arivalTime is null"); } + * + * if (bus.lastKnownStop.arivalTime.before(stop.arivalTime) && bus.lastKnownStop.arivalTime.after(now)) { bus.lastKnownStop = stop; + * + * if (bus.firstKnownStop == null) { bus.firstKnownStop = stop; + * + * } else if (!bus.firstKnownStop.busStop.equals(stop.busStop)) { stopDao.delete(bus.firstKnownStop); // TODO Crude, might delete useful data + * + * bus.firstKnownStop = stop; } + * + * } } + */ + + // busDao.update(bus); + + } else { + + stop = new Stop(stopObj.getString("name"), busStop, destStop, calender.getTime(), new Date(System.currentTimeMillis())); + + } + + return stop; + + } catch (Exception e) { + // TODO Auto-generated catch block + Log.e(TAG, "Error parsing stop " + stopObj, e); + return null; + } + + } + + public static boolean updateStop(BusStop busStop, boolean onlyUniLink) { + String file = getFileFromServer(busStopUrl + busStop.id + ".json"); + + try { + JSONObject data = new JSONObject(file); + + JSONArray stopsArray = data.getJSONArray("stops"); + + // Log.i(TAG, "Number of entries " + data.length()); + + // Log.i(TAG, "Stops: " + data.getJSONArray("stops")); + + for (int stopNum = 0; stopNum < stopsArray.length(); stopNum++) { + JSONObject stopObj = stopsArray.getJSONObject(stopNum); + + if (onlyUniLink && !stopObj.getString("name").startsWith("U")) { + continue; + } + + Stop stop = getStop(context, stopObj, busStop); + + if (stop == null) { + Log.w(TAG, "Null stop, skiping"); + continue; + } + + if (stop.bus != null) { + Log.i(TAG, "Found stop for " + stop.bus.id + " at " + stop.busStop.id + " at " + stop.arivalTime); + } else { + if (stop.name == null) { + Log.e(TAG, "Null name"); + } else if (stop.busStop == null) { + Log.e(TAG, "Null busStop"); + } else if (stop.arivalTime == null) { + Log.e(TAG, "Null arivalTime"); + } + Log.i(TAG, "Found stop for a unidentified " + stop.name + " at " + stop.busStop.id + " at " + stop.arivalTime); + } + + // stopDao.create(stop); + } + } catch (JSONException ex) { + Log.e(TAG, "", ex); + + return false; + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + + return false; + } + + return false; + + } + + public static Timetable getTimetable(Context context, String busStop, boolean onlyUniLink) throws SQLException { + + if (helper == null) + helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + if (busRoutes == null) + busRoutes = helper.getBusRouteDao(); + if (stopDao == null) + stopDao = helper.getStopDao(); + if (busStopDao == null) + busStopDao = helper.getBusStopDao(); + + Timetable timetable = new Timetable(); + + String file = getFileFromServer(busStopUrl + busStop + ".json"); + + try { + JSONObject data = new JSONObject(file); + + JSONArray stopsArray = data.getJSONArray("stops"); + + Log.i(TAG, "Number of entries " + data.length()); + + Log.i(TAG, "Stops: " + data.getJSONArray("stops")); + + for (int stopNum = 0; stopNum < stopsArray.length(); stopNum++) { + JSONObject stopObj = stopsArray.getJSONObject(stopNum); + + if (onlyUniLink && !stopObj.getString("name").startsWith("U")) { + continue; + } + + BusStop busStopObj = busStopDao.queryForId(busStop); + if (busStopObj == null) { + Log.e(TAG, "BusStopObj == null"); + } + + Stop stop = getStop(context, stopObj, busStopObj); + + if (stop == null) { + Log.w(TAG, "Null stop, skiping"); + continue; + } + + if (stop.bus != null) { + Log.i(TAG, "Found stop for " + stop.bus.id + " at " + stop.busStop.id + " at " + stop.arivalTime); + } else { + Log.i(TAG, "Found stop for a unidentified " + stop.name + " at " + stop.busStop.id + " at " + stop.arivalTime); + } + + timetable.add(stop); + } + } catch (JSONException ex) { + Log.e(TAG, "", ex); + Log.e(TAG, "File: " + file); + return null; + } + + return timetable; + } + + static PathOverlay getRoutePath(InputStream routeResource, int colour, ResourceProxy resProxy) { + PathOverlay data = null; + + // sax stuff + try { + SAXParserFactory spf = SAXParserFactory.newInstance(); + SAXParser sp = spf.newSAXParser(); + + XMLReader xr = sp.getXMLReader(); + + DataHandler dataHandler = new DataHandler(colour, resProxy); + xr.setContentHandler(dataHandler); + + xr.parse(new InputSource(routeResource)); + + data = dataHandler.getData(); + + } catch (ParserConfigurationException pce) { + Log.e("SAX XML", "sax parse error", pce); + } catch (SAXException se) { + Log.e("SAX XML", "sax error", se); + } catch (IOException ioe) { + Log.e("SAX XML", "sax parse io error", ioe); + } + + return data; + } + + public static String getFileFromServer(String request) { + StringBuilder builder = new StringBuilder(); + HttpClient client = new DefaultHttpClient(); + HttpGet httpGet = new HttpGet(request); + Log.i("Util.getFileFromServer", "Request used: " + request); + try { + HttpResponse response = client.execute(httpGet); + StatusLine statusLine = response.getStatusLine(); + int statusCode = statusLine.getStatusCode(); + if (statusCode == 200) { + HttpEntity entity = response.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader(content)); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line); + } + } else { + Log.e("", "Failed to download file"); + } + } catch (Exception ex) { + Log.e("Util.getFileFromServer", ex.getClass().toString() + " " + ex.getMessage()); + } + + return builder.toString(); + } +} diff --git a/src/net/cbaines/suma/DatabaseHelper.java b/src/net/cbaines/suma/DatabaseHelper.java new file mode 100644 index 0000000..28ad6a5 --- /dev/null +++ b/src/net/cbaines/suma/DatabaseHelper.java @@ -0,0 +1,279 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.sql.SQLException; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.util.Log; + +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.TableUtils; + +public class DatabaseHelper extends OrmLiteSqliteOpenHelper { + + private static final String DATABASE_PATH = "/data/data/net.cbaines.suma/databases/"; + private static final String DATABASE_NAME = "data.db"; + + private static final int DATABASE_VERSION = 36; + + private static final String TAG = "DatabaseHelper"; + + // the DAO object we use to access the SimpleData table + private Dao<Building, String> buildingDao = null; + private Dao<BusStop, String> busStopDao = null; + private Dao<BusRoute, Integer> busRouteDao = null; + private Dao<RouteStops, Integer> routeStopsDao = null; + private Dao<Site, String> siteDao = null; + private Dao<Bus, Integer> busDao = null; + private Dao<Stop, Integer> stopDao = null; + + private Context context; + + public DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + Log.i(TAG, "Database Helper created"); + this.context = context; + } + + @Override + public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) { + try { + Log.i(DatabaseHelper.class.getName(), "onCreate"); + TableUtils.createTable(connectionSource, Building.class); + TableUtils.createTable(connectionSource, BusStop.class); + TableUtils.createTable(connectionSource, BusRoute.class); + TableUtils.createTable(connectionSource, RouteStops.class); + TableUtils.createTable(connectionSource, Site.class); + TableUtils.createTable(connectionSource, Bus.class); + TableUtils.createTable(connectionSource, Stop.class); + } catch (SQLException e) { + Log.e(DatabaseHelper.class.getName(), "Can't create database", e); + throw new RuntimeException(e); + } + } + + @Override + public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { + try { + Log.i(DatabaseHelper.class.getName(), "onUpgrade"); + TableUtils.dropTable(connectionSource, Building.class, true); + TableUtils.dropTable(connectionSource, BusStop.class, true); + TableUtils.dropTable(connectionSource, BusRoute.class, true); + TableUtils.dropTable(connectionSource, RouteStops.class, true); + TableUtils.dropTable(connectionSource, Site.class, true); + TableUtils.dropTable(connectionSource, Bus.class, true); + TableUtils.dropTable(connectionSource, Stop.class, true); + // after we drop the old databases, we create the new ones + onCreate(database, connectionSource); + } catch (SQLException e) { + Log.e(DatabaseHelper.class.getName(), "Can't drop databases", e); + throw new RuntimeException(e); + } + + } + + /** + * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached value. + */ + public Dao<Building, String> getBuildingDao() throws SQLException { + if (buildingDao == null) { + buildingDao = getDao(Building.class); + } + return buildingDao; + } + + /** + * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached value. + */ + public Dao<BusStop, String> getBusStopDao() throws SQLException { + if (busStopDao == null) { + busStopDao = getDao(BusStop.class); + } + return busStopDao; + } + + /** + * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached value. + */ + public Dao<BusRoute, Integer> getBusRouteDao() throws SQLException { + if (busRouteDao == null) { + busRouteDao = getDao(BusRoute.class); + } + return busRouteDao; + } + + /** + * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached value. + */ + public Dao<RouteStops, Integer> getRouteStopsDao() throws SQLException { + if (routeStopsDao == null) { + routeStopsDao = getDao(RouteStops.class); + } + return routeStopsDao; + } + + /** + * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached value. + */ + public Dao<Site, String> getSiteDao() throws SQLException { + if (siteDao == null) { + siteDao = getDao(Site.class); + } + return siteDao; + } + + /** + * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached value. + */ + public Dao<Bus, Integer> getBusDao() throws SQLException { + if (busDao == null) { + busDao = getDao(Bus.class); + } + return busDao; + } + + /** + * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached value. + */ + public Dao<Stop, Integer> getStopDao() throws SQLException { + if (stopDao == null) { + stopDao = getDao(Stop.class); + } + return stopDao; + } + + /** + * Check if the database already exist to avoid re-copying the file each time you open the application. + * + * @return true if it exists, false if it doesn't + */ + public boolean checkDataBase() { + Log.i(TAG, "Check database"); + + SQLiteDatabase checkDB = null; + + try { + String myPath = DATABASE_PATH + DATABASE_NAME; + checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY); + } catch (SQLiteException e) { + + // database does't exist yet. + + } + + if (checkDB != null) { + + checkDB.close(); + + } + + Log.i(TAG, "Finished checking database"); + return checkDB != null ? true : false; + } + + /** + * Copies your database from your local assets-folder to the just created empty database in the system folder, from where it can be accessed and handled. + * This is done by transfering bytestream. + * */ + public void copyDataBase() throws IOException { + Log.i(TAG, "Begining copy database"); + + // By calling this method and empty database will be created into the default system path + // of your application so we are gonna be able to overwrite that database with our database. + Log.i(TAG, "GetReadableDatabase"); + this.getWritableDatabase().close(); + + InputStream myInput = context.getAssets().open(DATABASE_NAME); + + // Path to the just created empty db + String outFileName = DATABASE_PATH + DATABASE_NAME; + + File database = new File(outFileName); + if (database.exists()) { + database.delete(); + } + + // Open the empty db as the output stream + OutputStream myOutput = new FileOutputStream(outFileName); + + // transfer bytes from the inputfile to the outputfile + byte[] buffer = new byte[1024]; + int length; + while ((length = myInput.read(buffer)) > 0) { + myOutput.write(buffer, 0, length); + } + + // Close the streams + myOutput.flush(); + myOutput.close(); + myInput.close(); + + // getWritableDatabase().close(); + + Log.i(TAG, "Finished copying db"); + + } + + /** + * Creates a empty database on the system and rewrites it with your own database. + * */ + public void createDataBase() throws IOException { + + boolean dbExist = checkDataBase(); + + if (dbExist) { + // do nothing - database already exist + } else { + + try { + Log.i(TAG, "Copy database"); + copyDataBase(); + } catch (IOException e) { + throw new Error("Error copying database"); + } + } + + } + + /** + * Close the database connections and clear any cached DAOs. + */ + @Override + public void close() { + super.close(); + buildingDao = null; + busStopDao = null; + busRouteDao = null; + routeStopsDao = null; + siteDao = null; + busDao = null; + stopDao = null; + } +}
\ No newline at end of file diff --git a/src/net/cbaines/suma/DonateDialog.java b/src/net/cbaines/suma/DonateDialog.java new file mode 100644 index 0000000..0c0c45f --- /dev/null +++ b/src/net/cbaines/suma/DonateDialog.java @@ -0,0 +1,92 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import android.app.Dialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +public class DonateDialog extends Dialog implements Runnable { + + // private static final String TAG = "DonateDialog"; + + private static final String bitcoinAddress = "1LFATViKkmbm6m4u1Ghi9wqrgVy2B6M412"; + + private final Context context; + + private final TextView dialogMessage; + private final ProgressBar progressBar; + + private final LinearLayout errorLayout; + private final TextView donateDialogErrorMessage; + private final TextView donateBitcoinAddress; + + public DonateDialog(Context context) { + super(context); + + this.context = context; + + setContentView(R.layout.donate_dialog); + setTitle("Donate"); + + dialogMessage = (TextView) findViewById(R.id.donateDialogMessage); + progressBar = (ProgressBar) findViewById(R.id.donateDialogProgress); + + errorLayout = (LinearLayout) findViewById(R.id.donateDialogMessageLayout); + donateDialogErrorMessage = (TextView) findViewById(R.id.donateDialogErrorMessage); + donateBitcoinAddress = (TextView) findViewById(R.id.donateBitcoinAddress); + + new Thread(this).start(); + } + + public void run() { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + try { + + Intent donateIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("bitcoin:?" + bitcoinAddress + + "&label=Southampton%20Uni%20Map%20App&message=Donation%20for%20the%20Southampton%20University%20Map%20App")); + context.startActivity(donateIntent); + + } catch (ActivityNotFoundException e) { + errorLayout.post(new Runnable() { + public void run() { + dialogMessage.setText(R.string.donate_dialog_error_title); + progressBar.setVisibility(View.GONE); + errorLayout.setVisibility(View.VISIBLE); + donateDialogErrorMessage.setVisibility(View.VISIBLE); + donateBitcoinAddress.setText(bitcoinAddress); + donateBitcoinAddress.setVisibility(View.VISIBLE); + } + }); + } + } + +} diff --git a/src/net/cbaines/suma/FavouriteDialog.java b/src/net/cbaines/suma/FavouriteDialog.java new file mode 100644 index 0000000..dfd5690 --- /dev/null +++ b/src/net/cbaines/suma/FavouriteDialog.java @@ -0,0 +1,130 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.sql.SQLException; +import java.util.ArrayList; + +import android.app.Dialog; +import android.content.Context; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.ListView; +import android.widget.TextView; + +import com.j256.ormlite.android.apptools.OpenHelperManager; +import com.j256.ormlite.dao.Dao; + +public class FavouriteDialog extends Dialog { + + private static final String TAG = "FavouriteDialog"; + private ListView listItems; + + private final Context context; + + private final TextView message; + + protected POIArrayAdapter adapter; + + private ArrayList<POI> favouriteItems; + + public FavouriteDialog(Context context) { + super(context); + + this.context = context; + + setContentView(R.layout.favourite_dialog); + setTitle("Favourite Items"); + + message = (TextView) findViewById(R.id.favouriteDialogMessage); + + favouriteItems = new ArrayList<POI>(); + + listItems = (ListView) findViewById(R.id.favouriteListItems); + } + + public void refresh() { + + DatabaseHelper helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + + try { + + Dao<Building, String> buildingDao = helper.getBuildingDao(); + Dao<BusStop, String> busStopDao = helper.getBusStopDao(); + + final ArrayList<POI> newFavouriteItems = new ArrayList<POI>(); + + newFavouriteItems.addAll(buildingDao.queryForEq(POI.FAVOURITE_FIELD_NAME, true)); + newFavouriteItems.addAll(busStopDao.queryForEq(POI.FAVOURITE_FIELD_NAME, true)); + + Log.i(TAG, "There are " + newFavouriteItems.size() + " favourites"); + if (newFavouriteItems.size() == 0) { + Log.i(TAG, "Favourite dialog has no favourites, displaying message"); + message.post(new Runnable() { + public void run() { + message.setText("No Favourite's, to make some buildings or bus stops favourite, just tap and hold them on the map"); + message.setVisibility(View.VISIBLE); + } + }); + listItems.post(new Runnable() { + public void run() { + listItems.setVisibility(View.GONE); + adapter = null; + favouriteItems.clear(); + } + }); + + } else { + message.post(new Runnable() { + public void run() { + message.setVisibility(View.GONE); + } + }); + + listItems.post(new Runnable() { + public void run() { + favouriteItems = newFavouriteItems; + adapter = new POIArrayAdapter(context, favouriteItems); + + listItems.setVisibility(View.VISIBLE); + listItems.setAdapter(adapter); + + } + }); + + } + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + void setOnItemClickListener(OnItemClickListener item) { + listItems.setOnItemClickListener(item); + + } + + void setOnItemLongClickListener(OnItemLongClickListener item) { + listItems.setOnItemLongClickListener(item); + } + +} diff --git a/src/net/cbaines/suma/FindActivity.java b/src/net/cbaines/suma/FindActivity.java new file mode 100644 index 0000000..dbbff49 --- /dev/null +++ b/src/net/cbaines/suma/FindActivity.java @@ -0,0 +1,399 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.osmdroid.util.GeoPoint; + +import android.content.Context; +import android.content.Intent; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.ProgressBar; + +import com.j256.ormlite.android.apptools.OrmLiteBaseActivity; +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.stmt.PreparedQuery; +import com.j256.ormlite.stmt.QueryBuilder; + +public class FindActivity extends OrmLiteBaseActivity<DatabaseHelper> implements Runnable, TextWatcher, OnItemClickListener, LocationListener, + OnItemLongClickListener { + + final static String TAG = "FindActivity"; + + private EditText searchBar; + private ListView listItems; + private ProgressBar progBar; + private LinearLayout findContentLayout; + + private String searchTerm = ""; + + private Dao<Building, String> buildingDao; + private Dao<BusStop, String> busStopDao; + private Dao<Site, String> siteDao; + + private POIArrayAdapter adapter; + // private ArrayList<POI> POIsFound = new ArrayList<POI>(); + + private boolean dataChanged; + + private GeoPoint userLocation; + + private Thread searchThread; + + ArrayList<POI> getNearestPOIs(int distance) { + Log.i(TAG, "Getting nearest POI's"); + ArrayList<POI> nearestPOIs = new ArrayList<POI>(); + for (Iterator<Building> buildingDaoIter = buildingDao.iterator(); buildingDaoIter.hasNext();) { + POI poi = buildingDaoIter.next(); + int dist = poi.point.distanceTo(userLocation); + if (dist < distance) { + poi.distTo = dist; + nearestPOIs.add(poi); + } + } + + for (Iterator<BusStop> busStopDaoIter = busStopDao.iterator(); busStopDaoIter.hasNext();) { + POI poi = busStopDaoIter.next(); + int dist = poi.point.distanceTo(userLocation); + if (dist < distance) { + poi.distTo = dist; + nearestPOIs.add(poi); + } + } + + Collections.sort(nearestPOIs, new POIDistanceComparator(userLocation, true)); + + Log.i(TAG, "Got " + nearestPOIs.size() + " nearest POI's"); + return nearestPOIs; + } + + // Search thread + public void run() { + POIArrayAdapter tempAdaptor; + GeoPoint thisUserLocation = userLocation; + + Log.i(TAG, "Search thread started"); + String thisSearchTerm = searchTerm; + + ArrayList<POI> foundPOIsArray = null; + + Log.i(TAG, "Search term length " + thisSearchTerm.length() + " userLocation == null " + (thisUserLocation == null)); + if (thisSearchTerm.length() == 0 && thisUserLocation != null) { + foundPOIsArray = getNearestPOIs(200); + + } + + if (foundPOIsArray != null && foundPOIsArray.size() != 0) { + + tempAdaptor = new POIArrayAdapter(this, foundPOIsArray); + } else { + + try { + + foundPOIsArray = new ArrayList<POI>(); + + if (thisSearchTerm.length() == 0) { + for (Building building : buildingDao) { + foundPOIsArray.add(building); + } + + if (!thisSearchTerm.equals(searchTerm)) + return; + + for (BusStop busStop : busStopDao) { + foundPOIsArray.add(busStop); + } + + if (!thisSearchTerm.equals(searchTerm)) + return; + + for (Site site : siteDao) { + foundPOIsArray.add(site); + } + + } else { + + QueryBuilder<Building, String> buildingQueryBuilder = buildingDao.queryBuilder(); + buildingQueryBuilder.where().like(Building.ID_FIELD_NAME, "%" + thisSearchTerm + "%").or() + .like(Building.NAME_FIELD_NAME, "%" + thisSearchTerm + "%"); + PreparedQuery<Building> buildingPreparedQuery = buildingQueryBuilder.prepare(); + List<Building> buildings = buildingDao.query(buildingPreparedQuery); + for (Building building : buildings) { + foundPOIsArray.add(building); + } + buildings = null; + + if (!thisSearchTerm.equals(searchTerm)) + return; + + if (thisSearchTerm.contains("site")) { + for (Site site : siteDao) { + foundPOIsArray.add(site); + } + } else { + QueryBuilder<Site, String> siteQueryBuilder = siteDao.queryBuilder(); + siteQueryBuilder.where().like(Site.ID_FIELD_NAME, "%" + thisSearchTerm + "%").or() + .like(Site.NAME_FIELD_NAME, "%" + thisSearchTerm + "%"); + PreparedQuery<Site> sitePreparedQuery = siteQueryBuilder.prepare(); + List<Site> sites = siteDao.query(sitePreparedQuery); + for (Site site : sites) { + foundPOIsArray.add(site); + } + sites = null; + } + + if (!thisSearchTerm.equals(searchTerm)) + return; + + // if (thisSearchTerm.contains("bus")) { + // for (BusStop busStop : busStopDao) { + // foundPOIsArray.add(busStop); + // } + // } else { + QueryBuilder<BusStop, String> busStopQueryBuilder = busStopDao.queryBuilder(); + busStopQueryBuilder.where().like(BusStop.ID_FIELD_NAME, "%" + thisSearchTerm + "%").or() + .like(BusStop.DESCRIPTION_FIELD_NAME, "%" + thisSearchTerm + "%"); + PreparedQuery<BusStop> busStopPreparedQuery = busStopQueryBuilder.prepare(); + List<BusStop> busStops = busStopDao.query(busStopPreparedQuery); + for (BusStop busStop : busStops) { + foundPOIsArray.add(busStop); + } + busStops = null; + // } + + if (!thisSearchTerm.equals(searchTerm)) + return; + + Log.i(TAG, "Found " + foundPOIsArray.size() + " pois"); + + if (thisUserLocation != null) { + Collections.sort(foundPOIsArray, new POIDistanceComparator(userLocation)); + } else { + Collections.sort(foundPOIsArray, new StringDistanceComparator(thisSearchTerm)); + } + + } + + if (!thisSearchTerm.equals(searchTerm)) + return; + + tempAdaptor = new POIArrayAdapter(this, foundPOIsArray); + + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return; + } + } + + if (thisSearchTerm.equals(searchTerm)) { + Log.i(TAG, "Search terms still equal, starting post"); + adapter = tempAdaptor; + listItems.post(new Runnable() { + public void run() { + listItems.setAdapter(adapter); + if (progBar.getVisibility() != View.GONE) { + progBar.setVisibility(View.GONE); + findContentLayout.setGravity(Gravity.TOP); + } + } + }); + } else { + Log.i(TAG, "Search terms no longer equal, exiting"); + } + } + + /** Called when the activity is first created. */ + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.find); + + searchBar = (EditText) findViewById(R.id.searchBar); + searchBar.addTextChangedListener(this); + + listItems = (ListView) findViewById(R.id.findListItems); + listItems.setOnItemClickListener(this); + listItems.setOnItemLongClickListener(this); + + progBar = (ProgressBar) findViewById(R.id.findLoadBar); + findContentLayout = (LinearLayout) findViewById(R.id.findContentLayout); + + try { + buildingDao = getHelper().getBuildingDao(); + busStopDao = getHelper().getBusStopDao(); + siteDao = getHelper().getSiteDao(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + // Acquire a reference to the system Location Manager + LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); + Location lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); + if (lastKnownLocation != null) { + userLocation = Util.locationToGeoPoint(lastKnownLocation); + } else { + lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + if (lastKnownLocation != null) { + userLocation = Util.locationToGeoPoint(lastKnownLocation); + + } + } + // Register the listener with the Location Manager to receive location updates + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 10, this); + locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 10, this); + + // SotonBusData.getTimetable("SN120128"); + + // Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("bitcoin:1F8fHWmWhqUJGhXUvY57mUof41wMUaeKH7?amount=1X8&label=SUC")); + // startActivity(browserIntent); + // 1F8fHWmWhqUJGhXUvY57mUof41wMUaeKH7 + + searchThread = new Thread(this); + searchThread.start(); + + } + + public void afterTextChanged(Editable s) { + searchTerm = s.toString(); + Log.i(TAG, "Text changed " + searchTerm + " starting search thread"); + new Thread(this).start(); + } + + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // TODO Auto-generated method stub + + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + // TODO Auto-generated method stub + + } + + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + Log.i(TAG, "OnItemClick pos " + position + " id " + id); + + String poiId = adapter.getItemStringId(position); + + Log.i(TAG, "POI " + poiId + " selected"); + + // Intent i = new Intent(FindActivity.this, SouthamptonUniversityMapActivity.class); + getIntent().putExtra("poi", poiId); + // startActivity(i); + + setResult(RESULT_OK, getIntent()); + finish(); + } + + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + + POI poi = adapter.getPOIItem(position); + + Log.i(TAG, "Long Click Event ID: " + poi.id); + + if (poi.type.equals(POI.BUS_STOP)) { + Log.i(TAG, "Its a bus stop"); + + BusStop busStop = (BusStop) poi; + + Intent i = new Intent(FindActivity.this, BusTimeActivity.class); + i.putExtra("busStopID", busStop.id); + i.putExtra("busStopName", busStop.description); + startActivityForResult(i, 0); + } + + return false; + } + + public void finish() { + getIntent().putExtra("dataChanged", dataChanged); + // startActivity(i); + + setResult(RESULT_OK, getIntent()); + + super.finish(); + } + + public void onLocationChanged(Location location) { + Log.i(TAG, "Got location update for FindActivity"); + userLocation = Util.locationToGeoPoint(location); + if (!searchThread.isAlive()) { + searchThread = new Thread(this); + searchThread.start(); + } + } + + public void onProviderDisabled(String arg0) { + // TODO Auto-generated method stub + + } + + public void onProviderEnabled(String provider) { + // TODO Auto-generated method stub + + } + + public void onStatusChanged(String provider, int status, Bundle extras) { + // TODO Auto-generated method stub + + } + + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.i(TAG, "Got activity result"); + if (resultCode == RESULT_OK) { + // A contact was picked. Here we will just display it + // to the user. + + boolean dataChangedInBusTimeActivity = false; + + Bundle bundle = data.getExtras(); + if (bundle == null) { + Log.i(TAG, "Bundle is null"); + } else { + dataChangedInBusTimeActivity = bundle.getBoolean("dataChanged"); + } + + if (dataChangedInBusTimeActivity == true) { + dataChanged = true; + } + + } + + } +}
\ No newline at end of file diff --git a/src/net/cbaines/suma/POI.java b/src/net/cbaines/suma/POI.java new file mode 100644 index 0000000..37d6aaf --- /dev/null +++ b/src/net/cbaines/suma/POI.java @@ -0,0 +1,94 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import org.osmdroid.util.GeoPoint; + +import com.j256.ormlite.field.DataType; +import com.j256.ormlite.field.DatabaseField; + +public abstract class POI { + public static final String BUS_STOP = "busstop"; + public static final String BUILDING = "building"; + public static final String WAYPOINT = "waypoint"; + public static final String SITE = "site"; + + public static final String ID_FIELD_NAME = "id"; + public static final String POINT_FIELD_NAME = "point"; + public static final String FAVOURITE_FIELD_NAME = "favourite"; + + POI() { + } + + public POI(String id, GeoPoint point) { + this.id = id; + this.point = point; + } + + @DatabaseField(dataType = DataType.SERIALIZABLE, canBeNull = false) + public GeoPoint point; + + @DatabaseField(id = true) + public String id; + + @DatabaseField(canBeNull = false) + public boolean favourite; // This field is not assessed by equals + + int distTo = -1; // Used by the comparator to store distances, then later by the gui to display them. + + public String type; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((point == null) ? 0 : point.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + POI other = (POI) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (point == null) { + if (other.point != null) + return false; + } else if (!point.equals(other.point)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } +} diff --git a/src/net/cbaines/suma/POIArrayAdapter.java b/src/net/cbaines/suma/POIArrayAdapter.java new file mode 100644 index 0000000..83ee517 --- /dev/null +++ b/src/net/cbaines/suma/POIArrayAdapter.java @@ -0,0 +1,78 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.util.List; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +public class POIArrayAdapter extends BaseAdapter { + + private final Context context; + private final List<POI> POIs; + + public POIArrayAdapter(Context context, List<POI> pois) { + this.context = context; + this.POIs = pois; + } + + public View getView(int position, View convertView, ViewGroup parent) { + POIView poiView; + if (convertView == null) { + if (POIs.get(position).distTo == -1) { + poiView = new POIView(context, POIs.get(position)); + } else { + poiView = new POIView(context, POIs.get(position), POIs.get(position).distTo); + } + } else { + poiView = (POIView) convertView; + if (POIs.get(position).distTo == -1) { + poiView = new POIView(context, POIs.get(position)); + } else { + poiView = new POIView(context, POIs.get(position), POIs.get(position).distTo); + } + } + + return poiView; + } + + public int getCount() { + return POIs.size(); + } + + public Object getItem(int position) { + return position; + } + + public POI getPOIItem(int position) { + return POIs.get(position); + } + + public long getItemId(int position) { + return position; + } + + public String getItemStringId(int position) { + return POIs.get(position).id; + } +}
\ No newline at end of file diff --git a/src/net/cbaines/suma/POIDistanceComparator.java b/src/net/cbaines/suma/POIDistanceComparator.java new file mode 100644 index 0000000..ef35112 --- /dev/null +++ b/src/net/cbaines/suma/POIDistanceComparator.java @@ -0,0 +1,50 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.util.Comparator; + +import org.osmdroid.util.GeoPoint; + +public class POIDistanceComparator implements Comparator<POI> { + private final GeoPoint userLocation; + private final boolean useExistingData; + + public POIDistanceComparator(GeoPoint userLocation) { + this(userLocation, false); + } + + public POIDistanceComparator(GeoPoint userLocation, boolean useData) { + super(); + this.userLocation = userLocation; + this.useExistingData = useData; + } + + public int compare(POI poi1, POI poi2) { + if (poi1.distTo == -1 || !useExistingData) { + poi1.distTo = userLocation.distanceTo(poi1.point); + } + if (poi2.distTo == -1 || !useExistingData) { + poi2.distTo = userLocation.distanceTo(poi2.point); + } + return poi1.distTo - poi2.distTo; + } + +} diff --git a/src/net/cbaines/suma/POIFavouriteComparator.java b/src/net/cbaines/suma/POIFavouriteComparator.java new file mode 100644 index 0000000..94148d4 --- /dev/null +++ b/src/net/cbaines/suma/POIFavouriteComparator.java @@ -0,0 +1,40 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.util.Comparator; + +public class POIFavouriteComparator implements Comparator<POI> { + + public int compare(POI poi1, POI poi2) { + if (poi1.favourite) { + if (poi2.favourite) { + return 0; + } else { + return 1; + } + } else if (poi2.favourite) { + return -1; + } else { + return 0; + } + } + +} diff --git a/src/net/cbaines/suma/POIView.java b/src/net/cbaines/suma/POIView.java new file mode 100644 index 0000000..733cd0f --- /dev/null +++ b/src/net/cbaines/suma/POIView.java @@ -0,0 +1,116 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import android.content.Context; +import android.util.Log; +import android.view.Display; +import android.view.Gravity; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class POIView extends LinearLayout { + + private final static String TAG = "POIView"; + + private final TextView name; + private final TextView dist; + + private LayoutParams textLayoutParams; + + final int width; + + public POIView(Context context, POI poi) { + this(context, poi, -1); + } + + public POIView(Context context, POI poi, int distInM) { + super(context); + + Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + width = display.getWidth(); + // int height = display.getHeight(); + + this.setOrientation(HORIZONTAL); + + name = new TextView(context); + name.setTextSize(22f); + name.setGravity(Gravity.LEFT); + + dist = new TextView(context); + dist.setTextSize(22f); + dist.setGravity(Gravity.RIGHT); + + textLayoutParams = new LayoutParams(width - (width / 4), LayoutParams.WRAP_CONTENT); + LayoutParams distLayoutParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); + + setPOIAndDist(poi, distInM); + + addView(name, textLayoutParams); + addView(dist, distLayoutParams); + } + + public void setPOI(POI poi) { + setPOIAndDist(poi, -1); + } + + public void setPOIAndDist(POI poi, int distInM) { + + // Log.i(TAG, "Looking at poi " + poi.id); + + if (poi.type == POI.BUILDING) { + Building building = (Building) poi; + // Log.i(TAG, "Its a building of name " + building.name); + + name.setText(building.name + " (" + building.id + ")"); + } else if (poi.type == POI.BUS_STOP) { + + BusStop busStop = (BusStop) poi; + // Log.i(TAG, "Its a bus stop of description " + busStop.description); + + name.setText(busStop.description + " (" + busStop.id + ")"); + } else if (poi.type == POI.SITE) { + + Site site = (Site) poi; + // Log.i(TAG, "Its a site of name " + site.name); + + name.setText(site.name + " (" + site.id + ")"); + } else { + Log.w(TAG, "Cant identify " + poi.type); + + name.setText(poi.id); + } + + textLayoutParams = new LayoutParams(width - (width / 4), LayoutParams.WRAP_CONTENT); + + if (distInM != -1) { + textLayoutParams.width = width - (width / 4); + name.requestLayout(); + dist.setText(String.valueOf(distInM) + "m"); + } else { + textLayoutParams.width = LayoutParams.FILL_PARENT; + name.requestLayout(); + dist.setText(""); + // Log.w("POIView", "No dist avalible for S" + poi.id); + } + } + +}
\ No newline at end of file diff --git a/src/net/cbaines/suma/Polygon.java b/src/net/cbaines/suma/Polygon.java new file mode 100644 index 0000000..5020a33 --- /dev/null +++ b/src/net/cbaines/suma/Polygon.java @@ -0,0 +1,53 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.io.Serializable; + +import org.osmdroid.util.GeoPoint; + +public class Polygon implements Serializable { + /** + * + */ + private static final long serialVersionUID = 3029139596630651715L; + + final GeoPoint[] points; + + Polygon() { + points = null; + } + + Polygon(GeoPoint[] points) { + this.points = points; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Polygon "); + + for (GeoPoint point : points) { + builder.append(point.getLatitudeE6() + " " + point.getLongitudeE6()); + } + + return builder.toString(); + } +} diff --git a/src/net/cbaines/suma/PreferencesActivity.java b/src/net/cbaines/suma/PreferencesActivity.java new file mode 100644 index 0000000..b7c1d89 --- /dev/null +++ b/src/net/cbaines/suma/PreferencesActivity.java @@ -0,0 +1,39 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import android.os.Bundle; +import android.preference.PreferenceActivity; + +public class PreferencesActivity extends PreferenceActivity { + boolean CheckboxPreference; + String ListPreference; + String editTextPreference; + String ringtonePreference; + String secondEditTextPreference; + String customPref; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.preferences); + } + +}
\ No newline at end of file diff --git a/src/net/cbaines/suma/RouteColorConstants.java b/src/net/cbaines/suma/RouteColorConstants.java new file mode 100644 index 0000000..ba4fa36 --- /dev/null +++ b/src/net/cbaines/suma/RouteColorConstants.java @@ -0,0 +1,30 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import android.graphics.Color; + +public interface RouteColorConstants { + public final int U1 = Color.rgb(0, 139, 208); + public final int U1N = Color.rgb(0, 47, 107); // Dark blue + public final int U2 = Color.rgb(226, 0, 26); + public final int U6 = Color.rgb(247, 168, 0); + public final int U9 = Color.rgb(231, 82, 148); // Pink +} diff --git a/src/net/cbaines/suma/RouteOverlayItem.java b/src/net/cbaines/suma/RouteOverlayItem.java new file mode 100644 index 0000000..77df6dc --- /dev/null +++ b/src/net/cbaines/suma/RouteOverlayItem.java @@ -0,0 +1,37 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.util.ArrayList; + +import org.osmdroid.util.GeoPoint; +import org.osmdroid.views.overlay.OverlayItem; + +public class RouteOverlayItem extends OverlayItem { + + ArrayList<? extends POI> route; + + public RouteOverlayItem(String aTitle, String aDescription, ArrayList<? extends POI> route) { + super(aTitle, aDescription, new GeoPoint(0, 0)); + this.route = route; + + } + +} diff --git a/src/net/cbaines/suma/RouteStops.java b/src/net/cbaines/suma/RouteStops.java new file mode 100644 index 0000000..8375785 --- /dev/null +++ b/src/net/cbaines/suma/RouteStops.java @@ -0,0 +1,58 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "routestops") +public class RouteStops { + public final static String STOP_ID_FIELD_NAME = "stop_id"; + public final static String ROUTE_ID_FIELD_NAME = "route_id"; + public final static String SEQUENCE_ID_FIELD_NAME = "sequence"; + + /** + * This id is generated by the database and set on the object when it is passed to the create method. An id is needed in case we need to update or delete + * this object in the future. + */ + @DatabaseField(generatedId = true) + int id; + + @DatabaseField + int sequence; + + // This is a foreign object which just stores the id from the User object in this table. + @DatabaseField(foreign = true, columnName = STOP_ID_FIELD_NAME, indexName = "routestops_routestop_idx") + BusStop stop; + + // This is a foreign object which just stores the id from the Post object in this table. + @DatabaseField(foreign = true, columnName = ROUTE_ID_FIELD_NAME, indexName = "routestops_routestop_idx") + BusRoute route; + + RouteStops() { + // for ormlite + } + + public RouteStops(BusStop stop, BusRoute route, int sequence) { + this.stop = stop; + this.route = route; + this.sequence = sequence; + } +} diff --git a/src/net/cbaines/suma/Site.java b/src/net/cbaines/suma/Site.java new file mode 100644 index 0000000..0447d8a --- /dev/null +++ b/src/net/cbaines/suma/Site.java @@ -0,0 +1,55 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import org.osmdroid.util.GeoPoint; + +import com.j256.ormlite.field.DataType; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "sites") +public class Site extends POI { + + public static final String NAME_FIELD_NAME = "name"; + public static final String OUTLINE_FIELD_NAME = "outline"; + + @DatabaseField(canBeNull = false) + String name; + + @DatabaseField(dataType = DataType.SERIALIZABLE, canBeNull = true) + Polygon outline; + + Site(String id, String name, GeoPoint point, Polygon outline) { + super(id, point); + this.name = name; + this.outline = outline; + this.type = POI.SITE; + } + + Site() { + this.type = POI.SITE; + } + + public String toString() { + return name; + } + +} diff --git a/src/net/cbaines/suma/SouthamptonUniversityMapActivity.java b/src/net/cbaines/suma/SouthamptonUniversityMapActivity.java new file mode 100644 index 0000000..430bd4f --- /dev/null +++ b/src/net/cbaines/suma/SouthamptonUniversityMapActivity.java @@ -0,0 +1,1405 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; + +import org.osmdroid.DefaultResourceProxyImpl; +import org.osmdroid.ResourceProxy; +import org.osmdroid.tileprovider.tilesource.TileSourceFactory; +import org.osmdroid.util.GeoPoint; +import org.osmdroid.views.MapController; +import org.osmdroid.views.MapView; +import org.osmdroid.views.overlay.MyLocationOverlay; +import org.osmdroid.views.overlay.Overlay; +import org.osmdroid.views.overlay.PathOverlay; +import org.osmdroid.views.overlay.ScaleBarOverlay; +import org.osmdroid.views.util.constants.MapViewConstants; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.BaseExpandableListAdapter; +import android.widget.CheckBox; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.OnChildClickListener; +import android.widget.TextView; + +import com.j256.ormlite.android.apptools.OrmLiteBaseActivity; +import com.j256.ormlite.dao.Dao; + +public class SouthamptonUniversityMapActivity extends OrmLiteBaseActivity<DatabaseHelper> implements MapViewConstants, Runnable, RouteColorConstants, + OnChildClickListener, OnItemClickListener, OnItemLongClickListener, OnSharedPreferenceChangeListener { + + private boolean useBundledDatabase = true; + + private MapView mapView; + private MapController mapController; + private ResourceProxy mResourceProxy; + + private long startTime; + + static final int VIEW_DIALOG_ID = 0; + static final int FAVOURITE_DIALOG_ID = 1; + + private HashMap<String, Overlay> overlays = new HashMap<String, Overlay>(); + private HashMap<String, Overlay> pastOverlays; + + private ScaleBarOverlay scaleBarOverlay; + private MyLocationOverlay myLocationOverlay; + private BuildingNumOverlay residentialBuildingOverlay; + private BuildingNumOverlay nonResidentialBuildingOverlay; + private BusStopOverlay busStopOverlay; + private HashMap<Site, PathOverlay> siteOverlays; + private HashMap<BusRoute, PathOverlay> routeOverlays; + + private String[] busRoutes; + private String[] buildingTypes; + private String[] other; + private String[] groupHeadings; + private String[] siteNames; + + private FavouriteDialog favDialog; + + private SouthamptonUniversityMapActivity instance; + + private static final String TAG = "SUM"; + + @SuppressWarnings("unchecked") + public void onCreate(Bundle savedInstanceState) { + startTime = System.currentTimeMillis(); + + super.onCreate(savedInstanceState); + + instance = this; + + final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + final SharedPreferences activityPrefs = getPreferences(0); + + if (!sharedPrefs.contains("GPSEnabled")) { + sharedPrefs.edit().putBoolean("GPSEnabled", true).commit(); + } + if (!sharedPrefs.contains("liveBusTimesEnabled")) { + sharedPrefs.edit().putBoolean("liveBusTimesEnabled", true).commit(); + } + + Log.i(TAG, "GPS Enabled " + sharedPrefs.getBoolean("GPSEnabled", false)); + Log.i(TAG, "Live Bus Times Enabled " + sharedPrefs.getBoolean("liveBusTimesEnabled", false)); + + setContentView(R.layout.main); + + Log.i(TAG, "Finished setting content view " + (System.currentTimeMillis() - startTime)); + + busRoutes = getResources().getStringArray(R.array.uniLinkBusRoutes); + buildingTypes = getResources().getStringArray(R.array.buildingTypes); + other = getResources().getStringArray(R.array.utilityOverlays); + groupHeadings = getResources().getStringArray(R.array.preferencesHeadings); + + mapView = (MapView) this.findViewById(R.id.mapview); + mapView.setTileSource(TileSourceFactory.MAPNIK); + mapView.setBuiltInZoomControls(true); + mapView.setMultiTouchControls(true); + + pastOverlays = (HashMap<String, Overlay>) getLastNonConfigurationInstance(); + + Log.i(TAG, "Instantiating myLocationOverlay"); + // SensorManager mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); This code in the following constructor causes problems in + // some emulators, disable there sensors to fix. + myLocationOverlay = new MyLocationOverlay(instance, mapView); + Log.i(TAG, "Finished instantiating myLocationOverlay"); + + mapController = mapView.getController(); + mResourceProxy = new DefaultResourceProxyImpl(getApplicationContext()); + + GeoPoint userLocation = myLocationOverlay.getMyLocation(); + if (userLocation == null) { + userLocation = new GeoPoint(50935551, -1393488); // ECS + } + + mapController.setZoom(15); + mapController.setCenter(userLocation); + + Editor editor = activityPrefs.edit(); + editor.putBoolean("first_run", false); + editor.commit(); + + new Thread(this).start(); + } + + public void onResume() { + super.onResume(); + Log.i(TAG, "OnResume"); + if (myLocationOverlay != null) { + final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + final SharedPreferences activityPrefs = getPreferences(0); + + if (activityPrefs.getBoolean("Other:Compass", false)) { + myLocationOverlay.enableCompass(); + } else { + myLocationOverlay.disableCompass(); + } + + if (activityPrefs.getBoolean("Other:My Location", false) && sharedPrefs.getBoolean("GPSEnabled", false)) { + myLocationOverlay.enableMyLocation(); + } else { + myLocationOverlay.disableMyLocation(); + } + } + } + + public void onPause() { + super.onResume(); + Log.i(TAG, "OnPause"); + if (myLocationOverlay != null) { + myLocationOverlay.disableMyLocation(); + myLocationOverlay.disableCompass(); + } + } + + public void finish() { + super.finish(); + } + + @Override + public Object onRetainNonConfigurationInstance() { + return overlays; + } + + public void run() { + Log.i(TAG, "Begining loading the map overlay stuff " + (System.currentTimeMillis() - startTime)); + + Log.i(TAG, "Begining loading databases " + (System.currentTimeMillis() - startTime)); + + DatabaseHelper helper = getHelper(); + Log.i(TAG, "Got the helper"); + + boolean dbExist = helper.checkDataBase(); + + if (dbExist) { + // do nothing - database already exist + } else { + + if (useBundledDatabase) { + try { + helper.copyDataBase(); + Log.i(TAG, "Out of copy database"); + } catch (IOException ioe) { + throw new Error("Unable to create database"); + } + } else { + Thread buildingThread = null; + Thread busStopThread = null; + Thread siteThread = null; + + Log.i(TAG, "Begining loading databases " + (System.currentTimeMillis() - startTime)); + try { + Dao<Building, String> buildingDao; + + buildingDao = helper.getBuildingDao(); + + long buildingCount = buildingDao.countOf(); + Log.i(TAG, "Building count " + buildingCount); + if (buildingCount < 260) { + buildingThread = new Thread(new Runnable() { + public void run() { + try { + DataManager.loadBuildings(instance); + Log.i(TAG, "Loaded building database " + (System.currentTimeMillis() - startTime)); + } catch (SQLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + + buildingThread.start(); + } + + Dao<BusStop, String> busStopDao = helper.getBusStopDao(); + Dao<BusRoute, Integer> busRouteDao = helper.getBusRouteDao(); + Dao<RouteStops, Integer> routeStopsDao = helper.getRouteStopsDao(); + + long busStopCount = busStopDao.countOf(); + long busRouteCount = busRouteDao.countOf(); + long routeStopsCount = routeStopsDao.countOf(); + + Log.i(TAG, "BusStop count " + busStopCount); + Log.i(TAG, "BusRoute count " + busRouteCount); + Log.i(TAG, "RouteStops count " + routeStopsCount); + if (busStopCount < 217 || busRouteCount < 5 || routeStopsCount < 327) { + busStopThread = new Thread(new Runnable() { + public void run() { + try { + DataManager.loadBusData(instance, true); + Log.i(TAG, "Loaded bus stop database " + (System.currentTimeMillis() - startTime)); + } catch (SQLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + + busStopThread.start(); + } + + Dao<Site, String> siteDao = helper.getSiteDao(); + + long siteCount = siteDao.countOf(); + Log.i(TAG, "Sites count " + siteCount); + if (siteCount < 21) { + siteThread = new Thread(new Runnable() { + public void run() { + try { + DataManager.loadSiteData(instance); + Log.i(TAG, "Loaded site database " + (System.currentTimeMillis() - startTime)); + } catch (SQLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + + siteThread.start(); + } + + while (true) { + if ((buildingThread == null || !buildingThread.isAlive()) && (busStopThread == null || !busStopThread.isAlive()) + && (siteThread == null || !siteThread.isAlive())) + break; + + Thread.yield(); + } + + try { + siteNames = new String[(int) siteDao.countOf()]; + + int i = 0; + for (Site site : siteDao) { + siteNames[i] = site.name; + i++; + } + } catch (SQLException e) { + e.printStackTrace(); + } + + SharedPreferences mainPrefs = getPreferences(0); + if (mainPrefs.getBoolean("first_run", true)) { + Log.i(TAG, "Changing button in intro"); + } + + Log.i(TAG, "Finished loading databases " + (System.currentTimeMillis() - startTime)); + + } catch (SQLException e1) { + e1.printStackTrace(); + } + } + + } + + try { + setupActivityPrefs(); + } catch (SQLException e) { + e.printStackTrace(); + } + + createOverlays(); + Log.i(TAG, "Finished seting in motion the creation of the overlays " + (System.currentTimeMillis() - startTime)); + + } + + private void setupActivityPrefs() throws SQLException { + Log.i(TAG, "Begining setting up preferences"); + + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + + int size = (int) getHelper().getSiteDao().countOf(); + ArrayList<Site> sites = new ArrayList<Site>(size); + + try { + sites.addAll(getHelper().getSiteDao().queryForAll()); + } catch (SQLException e) { + e.printStackTrace(); + } + siteNames = new String[size]; + for (int i = 0; i < size; i++) { + siteNames[i] = sites.get(i).name; + } + + SharedPreferences activityPrefs = getPreferences(0); + Editor editor = activityPrefs.edit(); + + for (int heading = 0; heading < groupHeadings.length; heading++) { + if (heading == 0 || heading == 1) { + for (int child = 0; child < busRoutes.length; child++) { + if (!activityPrefs.contains(groupHeadings[heading] + ":" + busRoutes[child])) { + editor.putBoolean(groupHeadings[heading] + ":" + busRoutes[child], true); + } + } + } else if (heading == 2) { + for (int child = 0; child < buildingTypes.length; child++) { + if (!activityPrefs.contains(groupHeadings[heading] + ":" + buildingTypes[child])) { + editor.putBoolean(groupHeadings[heading] + ":" + buildingTypes[child], true); + } + } + } else if (heading == 3) { + for (int child = 0; child < sites.size(); child++) { + if (!activityPrefs.contains(groupHeadings[heading] + ":" + sites.get(child))) { + editor.putBoolean(groupHeadings[heading] + ":" + sites.get(child), true); + } + } + } else if (heading == 4) { + for (int child = 0; child < other.length; child++) { + if (!activityPrefs.contains(groupHeadings[heading] + ":" + other[child])) { + editor.putBoolean(groupHeadings[heading] + ":" + other[child], true); + } + } + } + } + + editor.commit(); + + activityPrefs.registerOnSharedPreferenceChangeListener(this); + sharedPrefs.registerOnSharedPreferenceChangeListener(this); + + Log.i(TAG, "Finished setting up preferences"); + } + + private void createOverlays() { + Log.i(TAG, "Began creating overlays at " + (System.currentTimeMillis() - startTime)); + + if (pastOverlays != null) { + Log.i(TAG, "Able to recover some/all of the overlays from a previous activity"); + } else { + Log.i(TAG, "Unable to recover overlays"); + } + + final OverlayRankComparator comparator = new OverlayRankComparator(getPreferences(0)); + final SharedPreferences activityPrefs = getPreferences(0); + + Thread utilityOverlayCreation = new Thread(new Runnable() { + public void run() { + Log.i(TAG, "Begining the creation of the utility overlays"); + + if (pastOverlays != null) { + scaleBarOverlay = (ScaleBarOverlay) pastOverlays.get("Other:Scale Bar"); + + if (scaleBarOverlay != null && myLocationOverlay != null) { + overlays.put("Other:Scale Bar", scaleBarOverlay); + Log.i(TAG, "Finished restoring utility overlays " + (System.currentTimeMillis() - startTime)); + return; + } + } + + scaleBarOverlay = new ScaleBarOverlay(instance); + scaleBarOverlay.setEnabled(activityPrefs.getBoolean("Other:Scale Bar", true)); + + overlays.put("Other:Scale Bar", scaleBarOverlay); + + Log.i(TAG, "Finished creating utility overlays " + (System.currentTimeMillis() - startTime)); + + } + }); + + utilityOverlayCreation.start(); + + Runnable utilityOverlayApplication = new Runnable() { + public void run() { + Log.i(TAG, "Begining the application of the utility overlays"); + + mapView.getOverlays().add(scaleBarOverlay); + + mapView.getOverlays().add(myLocationOverlay); + + Log.v(TAG, "Applyed the utility overlays, now sorting them"); + + Collections.sort(mapView.getOverlays(), comparator); + + Log.v(TAG, "Finished sorting the utility overlays them, now applying them"); + + mapView.invalidate(); + + Log.i(TAG, "Finished loading utility overlays " + (System.currentTimeMillis() - startTime)); + + } + }; + + Thread routeOverlayCreation = new Thread(new Runnable() { + public void run() { + + try { + Log.i(TAG, "Begining to create the route overlays"); + + SharedPreferences mainPrefs = getPreferences(0); + + routeOverlays = new HashMap<BusRoute, PathOverlay>(5); + + Dao<BusRoute, Integer> busRouteDao = getHelper().getBusRouteDao(); + + for (Iterator<BusRoute> routeIter = busRouteDao.iterator(); routeIter.hasNext();) { + BusRoute route = routeIter.next(); + + Log.v(TAG, "Looking at route " + route.code); + + if (pastOverlays != null) { + PathOverlay routeOverlay = (PathOverlay) pastOverlays.get("Bus Routes:" + route.code); + if (routeOverlay != null) { + Log.i(TAG, "Restored " + route.code + " route overlay"); + routeOverlays.put(route, routeOverlay); + overlays.put("Bus Routes:" + route.code, routeOverlay); + continue; + } + } + + InputStream resource = null; + int colour = 0; + if (route.code.equals("U1")) { + resource = getResources().openRawResource(R.raw.u1); + colour = U1; + + // TODO Is this a route like U1N or, something else, this hack works somewhat for now? + PathOverlay routeOverlayU1E = DataManager.getRoutePath(getResources().openRawResource(R.raw.u1e), colour, mResourceProxy); + routeOverlayU1E.getPaint().setAntiAlias(true); + routeOverlayU1E.getPaint().setAlpha(145); + routeOverlayU1E.getPaint().setStrokeWidth(12); + routeOverlayU1E.getPaint().setPathEffect(new DashPathEffect(new float[] { 20, 16 }, 0)); + routeOverlayU1E.setEnabled(mainPrefs.getBoolean("Bus Routes:" + route.code, true)); + + routeOverlays.put(new BusRoute(1000, "U1E", "U1e Route Label"), routeOverlayU1E); + overlays.put("Bus Routes:" + route.code + "E", routeOverlayU1E); + } else if (route.code.equals("U1N")) { + resource = getResources().openRawResource(R.raw.u1n); + colour = U1N; + } else if (route.code.equals("U2")) { + resource = getResources().openRawResource(R.raw.u2); + colour = U2; + } else if (route.code.equals("U6")) { + resource = getResources().openRawResource(R.raw.u6); + colour = U6; + } else if (route.code.equals("U9")) { + resource = getResources().openRawResource(R.raw.u9); + colour = U9; + } else { + continue; + } + + PathOverlay routeOverlay = DataManager.getRoutePath(resource, colour, mResourceProxy); + + Log.i(TAG, "Path overlay has " + routeOverlay.getNumberOfPoints() + " points"); + + routeOverlay.getPaint().setAntiAlias(true); + routeOverlay.getPaint().setAlpha(145); + routeOverlay.getPaint().setStrokeWidth(12); + routeOverlay.setEnabled(mainPrefs.getBoolean("Bus Routes:" + route.code, true)); + + routeOverlays.put(route, routeOverlay); + overlays.put("Bus Routes:" + route.code, routeOverlay); + + } + + Log.i(TAG, "Finished loading routes " + (System.currentTimeMillis() - startTime)); + + } catch (SQLException e) { + e.printStackTrace(); + } + } + }); + + routeOverlayCreation.start(); + + Runnable routeOverlayApplication = new Runnable() { + public void run() { + Log.i(TAG, "Begining applying the route overlays, number of route overlays = " + routeOverlays.size()); + + for (PathOverlay routeOverlay : routeOverlays.values()) { + Log.v(TAG, "Added route overlay"); + mapView.getOverlays().add(routeOverlay); + } + + Log.v(TAG, "Added the route overlays, now sorting them"); + + Collections.sort(mapView.getOverlays(), comparator); + + Log.v(TAG, "Finished sorting the route overlays them, now applying them"); + + mapView.invalidate(); + + Log.i(TAG, "Finished loading route overlays " + (System.currentTimeMillis() - startTime)); + } + }; + + Thread siteOverlayCreation = new Thread(new Runnable() { + public void run() { + Log.i(TAG, "Begining the creation of the site overlays"); + + SharedPreferences mainPrefs = getPreferences(0); + + try { + + Dao<Site, String> siteDao = getHelper().getSiteDao(); + siteOverlays = new HashMap<Site, PathOverlay>((int) siteDao.countOf()); + + for (Site site : siteDao) { + + if (pastOverlays != null) { + PathOverlay overlay = (PathOverlay) pastOverlays.get("Site Outlines:" + site.name); + if (overlay != null) { + Log.i(TAG, "Restored " + site.name + " site overlay"); + siteOverlays.put(site, overlay); + overlays.put("Site Outlines:" + site.name, overlay); + continue; + } + } + + PathOverlay overlay = new PathOverlay(Color.BLUE, instance); + Paint paint = overlay.getPaint(); + paint.setAntiAlias(true); + paint.setStrokeWidth(1.5f); + for (int i = 0; i < site.outline.points.length; i++) { + overlay.addPoint(site.outline.points[i]); + } + overlay.addPoint(site.outline.points[0]); + + overlay.setEnabled(mainPrefs.getBoolean("Site Outlines:" + site.name, true)); + + siteOverlays.put(site, overlay); + overlays.put("Site Outlines:" + site.name, overlay); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + Log.i(TAG, "Finished creating site overlays " + (System.currentTimeMillis() - startTime)); + + } + }); + + siteOverlayCreation.start(); + + Runnable siteOverlayApplication = new Runnable() { + public void run() { + Log.i(TAG, "Begining applying the site overlays, number of site overlays = " + siteOverlays.size()); + + for (PathOverlay siteOverlay : siteOverlays.values()) { + Log.d(TAG, "Added site overlay"); + mapView.getOverlays().add(siteOverlay); + } + + Log.v(TAG, "Added the site overlays, now sorting them"); + + Collections.sort(mapView.getOverlays(), comparator); + + Log.v(TAG, "Finished sorting the site overlays them, now applying them"); + + mapView.invalidate(); + + Log.i(TAG, "Finished loading site overlays " + (System.currentTimeMillis() - startTime)); + } + }; + + Thread buildingOverlayCreation = new Thread(new Runnable() { + public void run() { + Log.i(TAG, "Begining the creation of the building overlays"); + try { + if (pastOverlays != null) { + residentialBuildingOverlay = (BuildingNumOverlay) pastOverlays.get("Buildings:Residential"); + nonResidentialBuildingOverlay = (BuildingNumOverlay) pastOverlays.get("Buildings:Non-Residential"); + if (residentialBuildingOverlay != null && nonResidentialBuildingOverlay != null) { + overlays.put("Buildings:" + buildingTypes[0], residentialBuildingOverlay); + overlays.put("Buildings:" + buildingTypes[1], nonResidentialBuildingOverlay); + + Log.i(TAG, "Restored building overlays"); + return; + } + } + + SharedPreferences mainPrefs = getPreferences(0); + + ArrayList<Building> residentialBuildings = new ArrayList<Building>(); + ArrayList<Building> nonResidentialBuildings = new ArrayList<Building>(); + + Dao<Building, String> buildingDao = getHelper().getBuildingDao(); + + for (Building building : buildingDao) { + // Log.v(TAG, "Looking at building " + building.id); + if (building.residential == true) { + // Log.v(TAG, "Its residential"); + if (building.favourite) { + // Log.v(TAG, "Its residential and a favourite"); + residentialBuildings.add(building); + } else { + // Log.v(TAG, "Its residential and not a favourite"); + residentialBuildings.add(0, building); + } + } else { + if (building.favourite) { + // Log.v(TAG, "Its not residential and a favourite"); + nonResidentialBuildings.add(building); + } else { + // Log.v(TAG, "Its not residential and not a favourite"); + nonResidentialBuildings.add(0, building); + } + } + } + + residentialBuildingOverlay = new BuildingNumOverlay(instance, residentialBuildings); + nonResidentialBuildingOverlay = new BuildingNumOverlay(instance, nonResidentialBuildings); + + residentialBuildingOverlay.setEnabled(mainPrefs.getBoolean("Buildings:Residential", false)); + nonResidentialBuildingOverlay.setEnabled(mainPrefs.getBoolean("Buildings:Non-Residential", false)); + + overlays.put("Buildings:" + buildingTypes[0], residentialBuildingOverlay); + overlays.put("Buildings:" + buildingTypes[1], nonResidentialBuildingOverlay); + + Log.i(TAG, "Finished creating building overlays " + (System.currentTimeMillis() - startTime)); + } catch (SQLException e) { + e.printStackTrace(); + } + + } + }); + + buildingOverlayCreation.start(); + + Runnable buildingOverlayApplication = new Runnable() { + public void run() { + Log.i(TAG, "Begining applying the building overlays"); + + mapView.getOverlays().add(residentialBuildingOverlay); + mapView.getOverlays().add(nonResidentialBuildingOverlay); + + Log.v(TAG, "Added the building overlays, now sorting them"); + + Collections.sort(mapView.getOverlays(), comparator); + + Log.v(TAG, "Finished sorting the building overlays, now applying them"); + + mapView.invalidate(); + + Log.i(TAG, "Finished loading building overlays " + (System.currentTimeMillis() - startTime)); + } + }; + + Thread busStopOverlayCreation = new Thread(new Runnable() { + + public void run() { + Log.i(TAG, "Begining the creation of the bus stop overlay"); + + if (pastOverlays != null) { + busStopOverlay = (BusStopOverlay) pastOverlays.get("BusStops"); + if (busStopOverlay != null) { + overlays.put("BusStops", busStopOverlay); + + Log.i(TAG, "Restored bus stop overlays"); + return; + } + } + + try { + SharedPreferences mainPrefs = getPreferences(0); + + busStopOverlay = new BusStopOverlay(instance); + busStopOverlay.setRoutes(0, mainPrefs.getBoolean("Bus Stops:U1", false)); + busStopOverlay.setRoutes(1, mainPrefs.getBoolean("Bus Stops:U1N", false)); + busStopOverlay.setRoutes(2, mainPrefs.getBoolean("Bus Stops:U2", false)); + busStopOverlay.setRoutes(3, mainPrefs.getBoolean("Bus Stops:U6", false)); + busStopOverlay.setRoutes(4, mainPrefs.getBoolean("Bus Stops:U9", false)); + + overlays.put("BusStops", busStopOverlay); + } catch (SQLException e) { + e.printStackTrace(); + } + + Log.i(TAG, "Finished creating the bus stops overlay " + (System.currentTimeMillis() - startTime)); + } + }); + + busStopOverlayCreation.start(); + + Runnable busStopOverlayApplication = new Runnable() { + public void run() { + Log.i(TAG, "Begining applying the bus stop overlay"); + + mapView.getOverlays().add(busStopOverlay); + + Log.v(TAG, "Added the bus stop overlay, now sorting them"); + + Collections.sort(mapView.getOverlays(), comparator); + + Log.v(TAG, "Finished sorting the bus stop overlay, now applying them"); + + mapView.invalidate(); + + Log.i(TAG, "Finished loading bus stop overlay " + (System.currentTimeMillis() - startTime)); + } + }; + + while (utilityOverlayCreation != null || routeOverlayCreation != null || siteOverlayCreation != null || buildingOverlayCreation != null + || busStopOverlayCreation != null) { + if (utilityOverlayCreation != null && !utilityOverlayCreation.isAlive()) { + mapView.post(utilityOverlayApplication); + utilityOverlayCreation = null; + } + + if (routeOverlayCreation != null && !routeOverlayCreation.isAlive()) { + mapView.post(routeOverlayApplication); + routeOverlayCreation = null; + } + + if (siteOverlayCreation != null && !siteOverlayCreation.isAlive()) { + mapView.post(siteOverlayApplication); + siteOverlayCreation = null; + } + + if (buildingOverlayCreation != null && !buildingOverlayCreation.isAlive()) { + mapView.post(buildingOverlayApplication); + buildingOverlayCreation = null; + } + + if (busStopOverlayCreation != null && !busStopOverlayCreation.isAlive()) { + mapView.post(busStopOverlayApplication); + busStopOverlayCreation = null; + } + + Thread.yield(); + } + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.map_menu, menu); + return true; + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection + switch (item.getItemId()) { + case R.id.menu_find: + Intent i = new Intent(SouthamptonUniversityMapActivity.this, FindActivity.class); + startActivityForResult(i, 0); + return true; + case R.id.menu_preferences: + Intent settingsActivity = new Intent(getBaseContext(), PreferencesActivity.class); + startActivity(settingsActivity); + return true; + case R.id.menu_find_my_location: + final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + if (sharedPrefs.getBoolean("GPSEnabled", false)) { + GeoPoint userLocation = myLocationOverlay.getMyLocation(); + if (userLocation != null) { + Log.i(TAG, "Found user location, scrolling to " + userLocation); + mapController.animateTo(userLocation); + myLocationOverlay.enableFollowLocation(); + } + } else { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + Editor editor = sharedPrefs.edit(); + editor.putBoolean("GPSEnabled", true); + editor.commit(); + break; + + case DialogInterface.BUTTON_NEGATIVE: + // No button clicked + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage("GPS is not enabled, do you wish to enable it?").setPositiveButton("Yes", dialogClickListener) + .setNegativeButton("No", dialogClickListener).show(); + } + + return true; + case R.id.menu_view: + Log.i(TAG, "Showing view dialog"); + showDialog(VIEW_DIALOG_ID); + return false; + case R.id.menu_favourites: + Log.i(TAG, "Showing favourite dialog"); + showDialog(FAVOURITE_DIALOG_ID); + if (favDialog != null) { + favDialog.refresh(); + } else { + Log.e(TAG, "Very wierd, just tried to launch the favourite's dialog, but its null?"); + } + return false; + case R.id.menu_about: + Intent aboutIntent = new Intent(SouthamptonUniversityMapActivity.this, AboutActivity.class); + startActivityForResult(aboutIntent, 0); + return true; + default: + Log.e(TAG, "No known menu option selected"); + return super.onOptionsItemSelected(item); + } + } + + @Override + public boolean onSearchRequested() { + Intent i = new Intent(SouthamptonUniversityMapActivity.this, FindActivity.class); + startActivityForResult(i, 0); + return false; + } + + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.i(TAG, "Got activity result"); + if (resultCode == RESULT_OK) { + + POI poi = null; + Bundle bundle = data.getExtras(); + if (bundle == null) { + Log.i(TAG, "Bundle is null"); + } else { + String poiId = (String) bundle.get("poi"); + if (poiId != null) { + Log.i(TAG, "Got id " + poiId); + try { + poi = getHelper().getBuildingDao().queryForId(poiId); + if (poi == null) { + poi = getHelper().getBusStopDao().queryForId(poiId); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + if (poi == null) { + Log.e(TAG, "Could not find poi " + poiId + " in onActivityResult"); + } else { + if (myLocationOverlay != null) { + // It could be null if it has not been enabled + myLocationOverlay.disableFollowLocation(); + } + mapController.setZoom(20); + mapController.setCenter(poi.point); + + } + } else { + Log.i(TAG, "Got null poi id"); + + // mapController.setZoom(15); + // mapController.setCenter(new GeoPoint(50935551, -1393488)); + } + + // This handles the possible change in favourite state caused by the user within the BusTimeActivity + try { + String busStopID = bundle.getString("busStopChanged"); + if (busStopID != null && busStopID.length() != 0) { + Log.v(TAG, "Got a busStop id back from the BusTimeActivity " + busStopID); + BusStop busStop = getHelper().getBusStopDao().queryForId(busStopID); + + busStopOverlay.refresh(busStop); // This does not invalidate the map, but it seems to make the changes appear + } + } catch (SQLException e) { + e.printStackTrace(); + } + + if (favDialog != null) { + favDialog.refresh(); + } + } + } + + } + + public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { + + mapView.post(new Runnable() { + public void run() { + // updateEnabledOverlays(); TODO Fix whatever this did? + mapView.invalidate(); + } + }); + + return true; + } + + protected Dialog onCreateDialog(int id) { + switch (id) { + case VIEW_DIALOG_ID: + ViewDialog viewDialog = new ViewDialog(instance); + viewDialog.setOnItemClickListener(this); + return viewDialog; + case FAVOURITE_DIALOG_ID: + favDialog = new FavouriteDialog(instance); + favDialog.setOnItemClickListener(this); + favDialog.setOnItemLongClickListener(this); + return favDialog; + + } + return null; + } + + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + Log.i(TAG, "OnItemClick pos " + position + " id " + id); + + String poiId = favDialog.adapter.getItemStringId(position); + + Log.i(TAG, "POI " + poiId + " selected"); + + POI poi = null; + + if (poiId != null) { + Log.i(TAG, "Got id " + poiId); + try { + poi = getHelper().getBuildingDao().queryForId(poiId); + if (poi == null) { + poi = getHelper().getBusStopDao().queryForId(poiId); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + if (poi == null) { + Log.e(TAG, "Could not find poi " + poiId + " in onActivityResult"); + } else { + if (myLocationOverlay != null) { + myLocationOverlay.disableFollowLocation(); + } + mapController.setZoom(20); + mapController.setCenter(poi.point); + + favDialog.dismiss(); + + } + } else { + Log.i(TAG, "Got null poi id"); + + // mapController.setZoom(15); + // mapController.setCenter(new GeoPoint(50935551, -1393488)); + } + + } + + /** + * Long click on a item in the favourites menu + */ + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + + Log.i(TAG, "OnItemClick pos " + position + " id " + id); + + String poiId = favDialog.adapter.getItemStringId(position); + + Log.i(TAG, "POI " + poiId + " selected"); + + POI poi = null; + + if (poiId != null) { + Log.i(TAG, "Got id " + poiId); + try { + poi = getHelper().getBuildingDao().queryForId(poiId); + if (poi == null) { + poi = getHelper().getBusStopDao().queryForId(poiId); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + if (poi == null) { + Log.e(TAG, "Could not find poi " + poiId + " in onActivityResult"); + } else { + if (poi.type == POI.BUS_STOP) { + BusStop busStop = (BusStop) poi; + + Log.i(TAG, "Pressed " + busStop.id); + + Intent i = new Intent(this, BusTimeActivity.class); + i.putExtra("busStopID", busStop.id); + i.putExtra("busStopName", busStop.description); + startActivityForResult(i, 0); + + return true; + + } else { + + myLocationOverlay.disableFollowLocation(); + mapController.setZoom(20); + mapController.setCenter(poi.point); + + favDialog.dismiss(); + favDialog = null; + } + } + } else { + Log.i(TAG, "Got null poi id"); + + // mapController.setZoom(15); + // mapController.setCenter(new GeoPoint(50935551, -1393488)); + } + + return true; + } + + private class OverlayRankComparator implements Comparator<Overlay> { + private final SharedPreferences prefs; + + OverlayRankComparator(SharedPreferences prefs) { + this.prefs = prefs; + } + + public int compare(Overlay arg0, Overlay arg1) { + return getRank(arg1) - getRank(arg0); + } + + private final int getRank(Overlay arg0) { // TODO: Dont hardcode the rank values + if (arg0 == scaleBarOverlay) { + return prefs.getInt("mScaleBarOverlay", 1); + } else if (arg0 == myLocationOverlay) { + return prefs.getInt("myLocationOverlay", 0); + } else if (arg0 == busStopOverlay) { + return prefs.getInt("busStopOverlay", 2); + } else if (arg0 == residentialBuildingOverlay) { + return prefs.getInt("residentialBuildingOverlay", 4); + } else if (arg0 == nonResidentialBuildingOverlay) { + return prefs.getInt("nonResidentialBuildingOverlay", 3); + } else if (siteOverlays != null && siteOverlays.values().contains(arg0)) { + return prefs.getInt("siteOverlays", 6); + } else if (routeOverlays != null && routeOverlays.values().contains(arg0)) { + return prefs.getInt("routeOverlays", 5); + } else { + return -1; + } + } + } + + /** + * Handles all changes in preferences + */ + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + Log.v(TAG, "Got shared prefs changed event for key " + key); + + if (key.equals("GPSEnabled")) { + final SharedPreferences activityPrefs = getPreferences(0); + + if (activityPrefs.getBoolean("Other:Compass", false) && prefs.getBoolean("GPSEnabled", false)) { + myLocationOverlay.enableMyLocation(); + } else { + myLocationOverlay.disableMyLocation(); + } + } else if (key.equals("liveBusTimesEnabled")) { + // Noting to do here atm + } else if (key.contains("Bus Stops")) { + busStopOverlay.setRoutes(0, prefs.getBoolean("Bus Stops:U1", false)); + busStopOverlay.setRoutes(1, prefs.getBoolean("Bus Stops:U1N", false)); + busStopOverlay.setRoutes(2, prefs.getBoolean("Bus Stops:U2", false)); + busStopOverlay.setRoutes(3, prefs.getBoolean("Bus Stops:U6", false)); + busStopOverlay.setRoutes(4, prefs.getBoolean("Bus Stops:U9", false)); + } else if (key.contains("Bus Routes")) { + for (BusRoute route : routeOverlays.keySet()) { + Log.v(TAG, route.code + " " + key.split(":")[1]); + if (route.code.equals(key.split(":")[1])) { + routeOverlays.get(route).setEnabled(prefs.getBoolean(key, false)); + if (route.code.equals("U1")) { + overlays.get("Bus Routes:" + route.code + "E").setEnabled(prefs.getBoolean(key, false)); + } + } + } + } else if (key.contains("Buildings")) { + if (key.equals("Buildings:Non-Residential")) { + nonResidentialBuildingOverlay.setEnabled(prefs.getBoolean(key, false)); + } else if (key.equals("Buildings:Residential")) { + residentialBuildingOverlay.setEnabled(prefs.getBoolean(key, false)); + } else { + Log.e(TAG, "Wierd building preferences key " + key); + } + } else if (key.contains("Site Outlines")) { + for (Site site : siteOverlays.keySet()) { + if (site.name.equals(key.split(":")[1])) { + siteOverlays.get(site).setEnabled(prefs.getBoolean(key, false)); + } + } + } else if (key.contains("Other")) { + if (key.contains("Scale Bar")) { + scaleBarOverlay.setEnabled(prefs.getBoolean("Other:Scale Bar", false)); + } else if (key.contains("Compass")) { + if (prefs.getBoolean("Other:Compass", false)) { + myLocationOverlay.enableCompass(); + } else { + myLocationOverlay.disableCompass(); + } + } else if (key.contains("Other:My Location")) { + final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + + if (prefs.getBoolean("Other:Compass", false) && sharedPrefs.getBoolean("GPSEnabled", false)) { + myLocationOverlay.enableMyLocation(); + } else { + myLocationOverlay.disableMyLocation(); + } + } else { + Log.e(TAG, "Unhandled preference key " + key); + } + } else { + Log.e(TAG, "Unhandled preference key " + key); + } + } + + class ViewDialog extends Dialog implements OnChildClickListener { + + private final ExpandableListView epView; + + private static final String TAG = "ViewDialog"; + + private final MyExpandableListAdapter mAdapter; + + private OnChildClickListener listener; + + public ViewDialog(Context context) { + super(context); + + setContentView(R.layout.view_dialog); + setTitle("Select the map elements to display"); + + WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); + lp.copyFrom(this.getWindow().getAttributes()); + lp.width = WindowManager.LayoutParams.FILL_PARENT; + lp.height = WindowManager.LayoutParams.FILL_PARENT; + + this.getWindow().setAttributes(lp); + + epView = (ExpandableListView) findViewById(R.id.view_list); + mAdapter = new MyExpandableListAdapter(context); + epView.setAdapter(mAdapter); + epView.setOnChildClickListener(this); + + } + + public void setOnItemClickListener(OnChildClickListener onChildClickListener) { + Log.i(TAG, "Listener set for dialog"); + listener = onChildClickListener; + } + + class MyExpandableListAdapter extends BaseExpandableListAdapter { + + private LayoutInflater inflater; + + private static final String TAG = "MyExpandableListAdapter"; + + // Bus Stops + // |_ U1 + // |_ U1N + // |_ U2 + // |_ U6 + // |_ U9 + // Bus Routes + // |_ U1 + // |_ U1N + // |_ U2 + // |_ U6 + // |_ U9 + // Buildings + // |_ Residential + // |_ Non-Residential + // Site Outlines + // |_ Highfield Campus + // |_ Boldrewood Campus + // |_ Avenue Campus + // |_ Winchester School of Art + // |_ The University of Southampton Science Park + // |_ National Oceanography Centre Campus + // |_ Boat House + // |_ Southampton General Hospital + // |_ Royal South Hants Hospital + // |_ Belgrave Industrial Site + // |_ Highfield Hall + // |_ Glen Eyre Hall + // |_ South Hill Hall + // |_ Chamberlain Hall + // |_ Hartley Grove + // |_ Bencraft Hall + // |_ Connaught Hall + // |_ Montefiore Hall + // |_ Stoneham Hall + // |_ Erasmus Park + // Other + // |_ Scale Bar + // |_ Compass + // |_ My Location + + MyExpandableListAdapter(Context context) { + inflater = LayoutInflater.from(context); + } + + public Object getChild(int groupPosition, int childPosition) { + if (groupPosition == 0 || groupPosition == 1) { + return busRoutes[childPosition]; + } else if (groupPosition == 2) { + return buildingTypes[childPosition]; + } else if (groupPosition == 3) { + return siteNames[childPosition]; + } else if (groupPosition == 4) { + return other[childPosition]; + } else { + Log.e(TAG, "Unrecognised groupPosition " + groupPosition); + return null; + } + } + + public long getChildId(int groupPosition, int childPosition) { + return groupPosition * 50 + childPosition; + } + + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { + View v = null; + if (convertView != null) + v = convertView; + else + v = inflater.inflate(R.layout.view_child_row, parent, false); + String c = (String) getChild(groupPosition, childPosition); + TextView childName = (TextView) v.findViewById(R.id.childname); + if (childName != null) + childName.setText(c); + CheckBox cb = (CheckBox) v.findViewById(R.id.check1); + cb.setClickable(false); + cb.setFocusable(false); + SharedPreferences activityPrefs = getPreferences(0); + + if (groupPosition == 0 || groupPosition == 1) { + cb.setChecked(activityPrefs.getBoolean(groupHeadings[groupPosition] + ":" + busRoutes[childPosition], true)); + } else if (groupPosition == 2) { + cb.setChecked(activityPrefs.getBoolean(groupHeadings[groupPosition] + ":" + buildingTypes[childPosition], true)); + } else if (groupPosition == 3) { + cb.setChecked(activityPrefs.getBoolean(groupHeadings[groupPosition] + ":" + siteNames[childPosition], true)); + } else if (groupPosition == 4) { + cb.setChecked(activityPrefs.getBoolean(groupHeadings[groupPosition] + ":" + other[childPosition], true)); + } + return v; + } + + public int getChildrenCount(int groupPosition) { + if (groupPosition == 0 || groupPosition == 1) { + return busRoutes.length; + } else if (groupPosition == 2) { + return buildingTypes.length; + } else if (groupPosition == 3) { + return siteNames.length; + } else if (groupPosition == 4) { + return other.length; + } + return 0; + } + + public Object getGroup(int groupPosition) { + return groupHeadings[groupPosition]; + } + + public int getGroupCount() { + return groupHeadings.length; + } + + public long getGroupId(int groupPosition) { + return groupPosition * 5; + } + + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { + View v = null; + if (convertView != null) + v = convertView; + else + v = inflater.inflate(R.layout.view_group_row, parent, false); + String gt = (String) getGroup(groupPosition); + TextView colorGroup = (TextView) v.findViewById(R.id.childname); + if (gt != null) + colorGroup.setText(gt); + return v; + } + + public boolean hasStableIds() { + return true; + } + + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + + } + + public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { + Log.i(TAG, "Got view dialog click at " + groupPosition + ":" + childPosition); + + SharedPreferences activityPrefs = getPreferences(0); + + Editor editor = activityPrefs.edit(); + + CheckBox cb = (CheckBox) v.findViewById(R.id.check1); + + if (groupPosition == 0 || groupPosition == 1) { + Log.i(TAG, "Setting value of " + groupHeadings[groupPosition] + ":" + busRoutes[childPosition] + " to " + !cb.isChecked()); + editor.putBoolean(groupHeadings[groupPosition] + ":" + busRoutes[childPosition], !cb.isChecked()); + + } else if (groupPosition == 2) { + Log.i(TAG, "Setting value of " + groupHeadings[groupPosition] + ":" + buildingTypes[childPosition] + " to " + !cb.isChecked()); + editor.putBoolean(groupHeadings[groupPosition] + ":" + buildingTypes[childPosition], !cb.isChecked()); + + } else if (groupPosition == 3) { + Log.i(TAG, "Setting value of " + groupHeadings[groupPosition] + ":" + siteNames[childPosition] + " to " + !cb.isChecked()); + editor.putBoolean(groupHeadings[groupPosition] + ":" + siteNames[childPosition], !cb.isChecked()); + + } else if (groupPosition == 4) { + Log.i(TAG, "Setting value of " + groupHeadings[groupPosition] + ":" + other[childPosition] + " to " + !cb.isChecked()); + editor.putBoolean(groupHeadings[groupPosition] + ":" + other[childPosition], !cb.isChecked()); + } + + editor.commit(); + + mAdapter.notifyDataSetInvalidated(); + + listener.onChildClick(parent, v, groupPosition, childPosition, id); + + return true; + } + + } +} diff --git a/src/net/cbaines/suma/Stop.java b/src/net/cbaines/suma/Stop.java new file mode 100644 index 0000000..6e6bd39 --- /dev/null +++ b/src/net/cbaines/suma/Stop.java @@ -0,0 +1,78 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.util.Date; + +import android.text.format.DateUtils; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "stops") +public class Stop { + + @DatabaseField(generatedId = true) + int id; + + @DatabaseField(canBeNull = false) + String name; + + @DatabaseField(canBeNull = false, foreign = true) + BusStop destStop; + + @DatabaseField(canBeNull = true, foreign = true) + Bus bus; + + @DatabaseField(canBeNull = false, foreign = true) + BusStop busStop; + + @DatabaseField(canBeNull = false) + Date arivalTime; + + @DatabaseField(canBeNull = false) + Date timeOfFetch; + + Stop() { + + } + + Stop(String name, BusStop busStop, BusStop dest, Bus bus, Date arivalTime, Date timeOfFetch) { + this.name = name; + this.busStop = busStop; + this.destStop = dest; + this.bus = bus; + this.arivalTime = arivalTime; + this.timeOfFetch = timeOfFetch; + } + + Stop(String name, BusStop busStop, BusStop dest, Date arivalTime, Date timeOfFetch) { + this(name, busStop, dest, null, arivalTime, timeOfFetch); + } + + public String getTimeToArival() { + + if (arivalTime.getTime() - System.currentTimeMillis() <= 60000) { + return "Due"; + } else { + return (String) DateUtils.getRelativeTimeSpanString(arivalTime.getTime(), System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS); + } + } +} diff --git a/src/net/cbaines/suma/StopView.java b/src/net/cbaines/suma/StopView.java new file mode 100644 index 0000000..9e58eb4 --- /dev/null +++ b/src/net/cbaines/suma/StopView.java @@ -0,0 +1,98 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.sql.SQLException; + +import android.content.Context; +import android.view.Gravity; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.j256.ormlite.android.apptools.OpenHelperManager; +import com.j256.ormlite.dao.Dao; + +public class StopView extends LinearLayout implements OnClickListener { + + // private final ImageView icon; + + // private static final String TAG = "StopView"; + + private final TextView name; + private final TextView time; + private String onClickMessage = ""; + private final Context context; + + public StopView(Context context, Stop stop) { + super(context); + + this.context = context; + + this.setOrientation(HORIZONTAL); + + name = new TextView(context); + name.setTextSize(22f); + + time = new TextView(context); + time.setTextSize(22f); + time.setGravity(Gravity.RIGHT); + + setStop(stop); + + addView(name, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + addView(time, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); + } + + public void setStop(Stop stop) { + + // Log.i(TAG, "Time of arival " + stop.arivalTime); + + name.setText(stop.name); + time.setText(stop.getTimeToArival()); + + DatabaseHelper helper = OpenHelperManager.getHelper(context, DatabaseHelper.class); + + try { + Dao<Bus, Integer> busDao = helper.getBusDao(); + + busDao.refresh(stop.bus); + + if (stop.bus != null) { + onClickMessage = "Bus " + stop.bus.toString(); + } else { + onClickMessage = "Unidentified bus"; + } + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + this.setOnClickListener(this); + } + + public void onClick(View v) { + Toast.makeText(context, onClickMessage, Toast.LENGTH_SHORT).show(); + + } + +} diff --git a/src/net/cbaines/suma/StringDistanceComparator.java b/src/net/cbaines/suma/StringDistanceComparator.java new file mode 100644 index 0000000..0d19f53 --- /dev/null +++ b/src/net/cbaines/suma/StringDistanceComparator.java @@ -0,0 +1,132 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.util.Comparator; + +public class StringDistanceComparator implements Comparator<POI> { + private String userString; + + // private static final String TAG = "StringDistanceComparator"; + + public StringDistanceComparator(String userString) { + super(); + this.userString = userString; + } + + public int compare(POI poi1, POI poi2) { + int distTo1 = LD(userString, poi1.toString()); + // Log.i(TAG, "Comparing " + userString + " and " + poi1.toString() + " got dist " + distTo1); + int distTo2 = LD(userString, poi2.toString()); + // Log.i(TAG, "Comparing " + userString + " and " + poi2.toString() + " got dist " + distTo2); + return distTo1 - distTo2; + } + + // Below is public domain code from http://www.merriampark.com/ld.htm + + // **************************** + // Get minimum of three values + // **************************** + + private int Minimum(int a, int b, int c) { + int mi; + + mi = a; + if (b < mi) { + mi = b; + } + if (c < mi) { + mi = c; + } + return mi; + + } + + // ***************************** + // Compute Levenshtein distance + // ***************************** + + public int LD(String s, String t) { + int d[][]; // matrix + int n; // length of s + int m; // length of t + int i; // iterates through s + int j; // iterates through t + char s_i; // ith character of s + char t_j; // jth character of t + int cost; // cost + + // Step 1 + + n = s.length(); + m = t.length(); + if (n == 0) { + return m; + } + if (m == 0) { + return n; + } + d = new int[n + 1][m + 1]; + + // Step 2 + + for (i = 0; i <= n; i++) { + d[i][0] = i; + } + + for (j = 0; j <= m; j++) { + d[0][j] = j; + } + + // Step 3 + + for (i = 1; i <= n; i++) { + + s_i = s.charAt(i - 1); + + // Step 4 + + for (j = 1; j <= m; j++) { + + t_j = t.charAt(j - 1); + + // Step 5 + + if (s_i == t_j) { + cost = 0; + } else { + cost = 1; + } + + // Step 6 + + d[i][j] = Minimum(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost); + + } + + } + + // Step 7 + + return d[n][m]; + + } + +} diff --git a/src/net/cbaines/suma/Timetable.java b/src/net/cbaines/suma/Timetable.java new file mode 100644 index 0000000..466bdfe --- /dev/null +++ b/src/net/cbaines/suma/Timetable.java @@ -0,0 +1,31 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.util.ArrayList; + +public class Timetable extends ArrayList<Stop> { + + /** + * + */ + private static final long serialVersionUID = -9021303378059511643L; + +} diff --git a/src/net/cbaines/suma/TimetableAdapter.java b/src/net/cbaines/suma/TimetableAdapter.java new file mode 100644 index 0000000..8326856 --- /dev/null +++ b/src/net/cbaines/suma/TimetableAdapter.java @@ -0,0 +1,60 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +public class TimetableAdapter extends BaseAdapter { + + private final Context context; + private final Timetable timetable; + + public TimetableAdapter(Context context, Timetable timetable) { + this.context = context; + this.timetable = timetable; + } + + public View getView(int position, View convertView, ViewGroup parent) { + StopView stopView; + if (convertView == null) { + stopView = new StopView(context, timetable.get(position)); + } else { + stopView = (StopView) convertView; + stopView.setStop(timetable.get(position)); + } + + return stopView; + } + + public int getCount() { + return timetable.size(); + } + + public Object getItem(int position) { + return position; + } + + public long getItemId(int position) { + return position; + } +} diff --git a/src/net/cbaines/suma/Util.java b/src/net/cbaines/suma/Util.java new file mode 100644 index 0000000..7efbcbf --- /dev/null +++ b/src/net/cbaines/suma/Util.java @@ -0,0 +1,95 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import java.util.ArrayList; + +import org.osmdroid.util.GeoPoint; + +import android.location.Location; +import android.util.Log; + +public class Util { + + private static final String TAG = "Util"; + + public static GeoPoint csLatLongToGeoPoint(String lat, String lng) { + try { + double dLat = Double.valueOf(lat).doubleValue(); + double dLng = Double.valueOf(lng).doubleValue(); + int iLat = (int) (dLat * 1e6); + int iLng = (int) (dLng * 1e6); + return new GeoPoint(iLat, iLng); + } catch (NumberFormatException e) { + Log.e(TAG, "Error formating " + lat + " " + lng, e); + throw e; + } + } + + public static GeoPoint ssLatLongToGeoPoint(String lat, String lng) { + try { + + double dLat = Double.valueOf(lat).doubleValue(); + double dLng = Double.valueOf(lng).doubleValue(); + int iLat = (int) (dLat * 1e6); + int iLng = (int) (dLng * 1e6); + return new GeoPoint(iLat, iLng); + } catch (NumberFormatException e) { + Log.e(TAG, "Error formating " + lat + " " + lng, e); + throw e; + } + } + + public static Polygon csPolygonToPolygon(String str) { + // Log.i(TAG, "Getting poly from " + str); + ArrayList<GeoPoint> geoPoints = new ArrayList<GeoPoint>(); + + String[] latLongPoints = str.split(","); + for (int point = 0; point < latLongPoints.length; point++) { + // Log.i(TAG, "LatLong point " + point + " " + latLongPoints[point]); + + String[] latLongs = latLongPoints[point].split(" "); + GeoPoint geoPoint = ssLatLongToGeoPoint(latLongs[1], latLongs[0]); + geoPoints.add(geoPoint); + } + + return new Polygon(geoPoints.toArray(new GeoPoint[0])); + } + + public static int doubleToIntE6(double dub) { + return (int) (dub * 1e6); + } + + public static double E6IntToDouble(int integer) { + return (double) (integer / 1e6); + } + + public static GeoPoint locationToGeoPoint(Location loc) { + return new GeoPoint(doubleToIntE6(loc.getLatitude()), doubleToIntE6(loc.getLongitude())); + } + + public static Location geoPointToLocation(GeoPoint point) { + Location loc = new Location(""); + loc.setLatitude(E6IntToDouble(point.getLatitudeE6())); + loc.setLongitude(E6IntToDouble(point.getLongitudeE6())); + return loc; + } + +} diff --git a/src/net/cbaines/suma/Waypoint.java b/src/net/cbaines/suma/Waypoint.java new file mode 100644 index 0000000..d56c05d --- /dev/null +++ b/src/net/cbaines/suma/Waypoint.java @@ -0,0 +1,42 @@ +/* + * Southampton University Map App + * Copyright (C) 2011 Christopher Baines + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.cbaines.suma; + +import org.osmdroid.util.GeoPoint; + +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "routeWaypoints") +public class Waypoint extends POI { + + Waypoint() { + } + + int sequence; + + String route; + + public Waypoint(int sequence, GeoPoint point) { + super(point.toDoubleString() + ":" + sequence, point); + this.type = POI.WAYPOINT; + this.sequence = sequence; + } + +} |