/* * 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.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 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.net.Uri; import android.preference.PreferenceManager; import android.util.Log; import android.view.MotionEvent; import android.widget.Toast; public class BuildingNumOverlay extends Overlay implements Preferences, OnSharedPreferenceChangeListener { private ArrayList 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 MapActivity context; private float userScale = 1f; private boolean showIdentifiers; public BuildingNumOverlay(MapActivity context, List buildings) throws SQLException { super(context); this.context = context; marker = context.getResources().getDrawable(R.drawable.building); favMarker = context.getResources().getDrawable(R.drawable.building_fav); final SharedPreferences favouritesPrefs = context.getSharedPreferences(FAVOURITES_PREFERENCES, 0); favouritesPrefs.registerOnSharedPreferenceChangeListener(this); showIdentifiers = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SHOW_IDENTIFIERS, SHOW_IDENTIFIERS_ENABLED_BY_DEFAULT); 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) buildings; } /** * Draw a marker on each of our items. populate() must have been called first.
*
* 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.
*
* 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.
*
* The focused item is always drawn last, which puts it visually on top of the other items.
* * @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 || !isEnabled()) { 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); final SharedPreferences favouritesPrefs = context.getSharedPreferences(FAVOURITES_PREFERENCES, 0); /* * Draw in backward cycle, so the items with the least index are on the front. */ for (Iterator buildingIter = buildings.iterator(); buildingIter.hasNext();) { final Building building = buildingIter.next(); // Log.i(TAG, "Looking at drawing stop " + stop.id); pj.toMapPixels(building.point, mCurScreenCoords); if (!pj.getBoundingBox().increaseByScale(1.2f).contains(building.point)) { continue; } // draw it if (favouritesPrefs.getBoolean(building.id, false)) { 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 onSingleTapConfirmed(final MotionEvent event, final MapView mapView) { if (!this.isEnabled()) return false; final Building building = getSelectedItem(event, mapView); if (building == null) { // Log.v(TAG, "No building pressed"); return false; } else { Log.v(TAG, "building Pressed " + building.id); String str = building.name; if (showIdentifiers) { str += " (" + building.id + ")"; } context.makeToast(str, context.getResources().getString(R.string.map_activity_toast_help_message), Toast.LENGTH_SHORT); return true; } } public boolean onDoubleTap(final MotionEvent event, final MapView mapView) { if (!this.isEnabled()) return false; Building building = getSelectedItem(event, mapView); if (building == null) { // Log.v(TAG, "No busStop pressed"); return false; } else { Log.i(TAG, "Pressed " + building.id); Uri uri = Uri.parse("http://id.southampton.ac.uk/building/" + building.id); Log.i(TAG, "Starting a activity for " + uri + " path " + uri.getPath()); Intent buildingIntent = new Intent(Intent.ACTION_VIEW, uri); context.startActivity(buildingIntent); return true; } } @Override public boolean onLongPress(final MotionEvent event, final MapView mapView) { if (!this.isEnabled()) return false; int pointerCount = event.getPointerCount(); if (pointerCount > 1) { Log.v(TAG, "Detected a zoom " + pointerCount); return false; } else { Log.v(TAG, "Zoom not detected " + pointerCount); } final Building building = getSelectedItem(event, mapView); if (building == null) { // Log.v(TAG, "No building pressed"); return false; } else { Log.v(TAG, "building Pressed " + building.id); final SharedPreferences favouritesPrefs = context.getSharedPreferences(FAVOURITES_PREFERENCES, 0); // final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); if (favouritesPrefs.getBoolean(building.id, false)) { favouritesPrefs.edit().remove(building.id).commit(); // if (sharedPrefs.getBoolean(SHOW_IDENTIFIERS, SHOW_IDENTIFIERS_ENABLED_BY_DEFAULT)) { context.makeToast(building.name + " removed from favourites", "(" + building.id + ")", Toast.LENGTH_SHORT); // } else { // context.makeToast(building.name + " removed from favourites", Toast.LENGTH_SHORT); // } } else { // if (sharedPrefs.getBoolean(SHOW_IDENTIFIERS, SHOW_IDENTIFIERS_ENABLED_BY_DEFAULT)) { context.makeToast(building.name + " added to favourites", "(" + building.id + ")", Toast.LENGTH_SHORT); // } else { // context.makeToast(building.name + " added to favourites", Toast.LENGTH_SHORT); // } favouritesPrefs.edit().putBoolean(building.id, true).commit(); } Collections.sort(buildings, new POIFavouriteComparator(context.getSharedPreferences(FAVOURITES_PREFERENCES, 0))); 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 onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { Log.i(TAG, "Got a favourites change in the BuildingNumOverlay for key " + key); Collections.sort(buildings, new POIFavouriteComparator(context.getSharedPreferences(FAVOURITES_PREFERENCES, 0))); } }