From 18b453e02c5805e28cba9c1154c3a80731c9255a Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Fri, 23 Mar 2012 16:07:30 +0100 Subject: [PATCH] google: Add maps view for larger screens. --- oeffi/AndroidManifest.xml | 4 +- oeffi/build.gradle | 1 + oeffi/libs/maps.jar | Bin 0 -> 26704 bytes oeffi/res/layout/directions_content.xml | 15 + .../directions_trip_details_content.xml | 15 + oeffi/res/layout/network_picker_content.xml | 15 + oeffi/res/layout/stations_content.xml | 15 + .../stations_station_details_content.xml | 15 + oeffi/res/values-large/dimens.xml | 2 + oeffi/res/values-w420dp/dimens.xml | 8 + oeffi/res/values-xlarge/dimens.xml | 2 + oeffi/res/values/dimens.xml | 3 + oeffi/res/values/maps_api_key.xml | 7 + oeffi/src/de/schildbach/oeffi/Constants.java | 2 + .../de/schildbach/oeffi/OeffiActivity.java | 35 +- .../src/de/schildbach/oeffi/OeffiMapView.java | 662 ++++++++++++++++++ .../oeffi/directions/DirectionsActivity.java | 106 +++ .../oeffi/directions/TripDetailsActivity.java | 51 ++ .../oeffi/network/NetworkPickerActivity.java | 80 ++- .../stations/StationDetailsActivity.java | 23 + .../oeffi/stations/StationsActivity.java | 40 ++ 21 files changed, 1096 insertions(+), 5 deletions(-) create mode 100644 oeffi/libs/maps.jar create mode 100644 oeffi/res/values-w420dp/dimens.xml create mode 100644 oeffi/res/values/maps_api_key.xml create mode 100644 oeffi/src/de/schildbach/oeffi/OeffiMapView.java diff --git a/oeffi/AndroidManifest.xml b/oeffi/AndroidManifest.xml index 930a731..961fb59 100644 --- a/oeffi/AndroidManifest.xml +++ b/oeffi/AndroidManifest.xml @@ -379,6 +379,8 @@ android:name=".plans.PlanContentProvider" android:authorities="de.schildbach.oeffi.plans" android:exported="false" /> + + - + \ No newline at end of file diff --git a/oeffi/build.gradle b/oeffi/build.gradle index 7203d71..f551105 100644 --- a/oeffi/build.gradle +++ b/oeffi/build.gradle @@ -23,6 +23,7 @@ dependencies { compile 'com.github.tony19:logback-android:1.1.1-8' compile 'com.google.code.findbugs:jsr305:3.0.0' testCompile 'junit:junit:4.12' + provided files('libs/maps.jar') } android { diff --git a/oeffi/libs/maps.jar b/oeffi/libs/maps.jar new file mode 100644 index 0000000000000000000000000000000000000000..cef0185a4eb9ed0b908fb5c7998b0aabbd2a982a GIT binary patch literal 26704 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 literal 0 HcmV?d00001 diff --git a/oeffi/res/layout/directions_content.xml b/oeffi/res/layout/directions_content.xml index 137b81c..7276242 100644 --- a/oeffi/res/layout/directions_content.xml +++ b/oeffi/res/layout/directions_content.xml @@ -88,4 +88,19 @@ + + + + + \ No newline at end of file diff --git a/oeffi/res/layout/directions_trip_details_content.xml b/oeffi/res/layout/directions_trip_details_content.xml index 82b066a..00db547 100644 --- a/oeffi/res/layout/directions_trip_details_content.xml +++ b/oeffi/res/layout/directions_trip_details_content.xml @@ -134,4 +134,19 @@ + + + + + \ No newline at end of file diff --git a/oeffi/res/layout/network_picker_content.xml b/oeffi/res/layout/network_picker_content.xml index adf5ed9..e15c2d5 100644 --- a/oeffi/res/layout/network_picker_content.xml +++ b/oeffi/res/layout/network_picker_content.xml @@ -65,4 +65,19 @@ + + + + + \ No newline at end of file diff --git a/oeffi/res/layout/stations_content.xml b/oeffi/res/layout/stations_content.xml index 080515d..54af5be 100644 --- a/oeffi/res/layout/stations_content.xml +++ b/oeffi/res/layout/stations_content.xml @@ -398,4 +398,19 @@ + + + + + \ No newline at end of file diff --git a/oeffi/res/layout/stations_station_details_content.xml b/oeffi/res/layout/stations_station_details_content.xml index 2f21297..8dcbb24 100644 --- a/oeffi/res/layout/stations_station_details_content.xml +++ b/oeffi/res/layout/stations_station_details_content.xml @@ -74,4 +74,19 @@ + + + + + \ No newline at end of file diff --git a/oeffi/res/values-large/dimens.xml b/oeffi/res/values-large/dimens.xml index cbc827a..0d9b52e 100644 --- a/oeffi/res/values-large/dimens.xml +++ b/oeffi/res/values-large/dimens.xml @@ -25,6 +25,8 @@ 75% + 390dp + 6 \ No newline at end of file diff --git a/oeffi/res/values-w420dp/dimens.xml b/oeffi/res/values-w420dp/dimens.xml new file mode 100644 index 0000000..c7b5152 --- /dev/null +++ b/oeffi/res/values-w420dp/dimens.xml @@ -0,0 +1,8 @@ + + + + @dimen/layout_list_width + + true + + \ No newline at end of file diff --git a/oeffi/res/values-xlarge/dimens.xml b/oeffi/res/values-xlarge/dimens.xml index a72a1f5..e95eba0 100644 --- a/oeffi/res/values-xlarge/dimens.xml +++ b/oeffi/res/values-xlarge/dimens.xml @@ -27,4 +27,6 @@ 70% 8sp + 420dp + \ No newline at end of file diff --git a/oeffi/res/values/dimens.xml b/oeffi/res/values/dimens.xml index 5f32abb..75f2621 100644 --- a/oeffi/res/values/dimens.xml +++ b/oeffi/res/values/dimens.xml @@ -42,7 +42,10 @@ 12dp 24dp 300dp + 360dp + true + false false 5 diff --git a/oeffi/res/values/maps_api_key.xml b/oeffi/res/values/maps_api_key.xml new file mode 100644 index 0000000..e10fec3 --- /dev/null +++ b/oeffi/res/values/maps_api_key.xml @@ -0,0 +1,7 @@ + + + + + 0_5QxtRHdv6pmRUua3pONjL2g8sntEaJMU_e69w + + diff --git a/oeffi/src/de/schildbach/oeffi/Constants.java b/oeffi/src/de/schildbach/oeffi/Constants.java index 4ee1e4b..72237a8 100644 --- a/oeffi/src/de/schildbach/oeffi/Constants.java +++ b/oeffi/src/de/schildbach/oeffi/Constants.java @@ -47,6 +47,8 @@ public class Constants { public static final int MAX_NUMBER_OF_STOPS = 150; public static final int MAX_HISTORY_ENTRIES = 50; public static final float BEARING_ACCURACY_THRESHOLD = 0.5f; + public static final int INITIAL_MAP_ZOOM_LEVEL_NETWORK = 12; + public static final int INITIAL_MAP_ZOOM_LEVEL = 17; public static final int MAX_TRIES_ON_IO_PROBLEM = 2; public static final Locale DEFAULT_LOCALE = Locale.GERMAN; diff --git a/oeffi/src/de/schildbach/oeffi/OeffiActivity.java b/oeffi/src/de/schildbach/oeffi/OeffiActivity.java index 95988e3..04c4493 100644 --- a/oeffi/src/de/schildbach/oeffi/OeffiActivity.java +++ b/oeffi/src/de/schildbach/oeffi/OeffiActivity.java @@ -20,23 +20,28 @@ package de.schildbach.oeffi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.android.maps.MapActivity; + import de.schildbach.oeffi.network.NetworkResources; import de.schildbach.oeffi.util.ErrorReporter; import de.schildbach.pte.NetworkId; import de.schildbach.pte.dto.ResultHeader; import android.annotation.TargetApi; -import android.app.Activity; import android.app.ActivityManager.TaskDescription; import android.content.SharedPreferences; +import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.format.DateUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; import android.widget.TextView; -public abstract class OeffiActivity extends Activity { +public abstract class OeffiActivity extends MapActivity { protected SharedPreferences prefs; private static final Logger log = LoggerFactory.getLogger(OeffiActivity.class); @@ -50,6 +55,27 @@ public abstract class OeffiActivity extends Activity { prefs = PreferenceManager.getDefaultSharedPreferences(this); } + protected void updateFragments(final int listFrameResId, final int mapFrameResId) { + final Resources res = getResources(); + + final View listFrame = findViewById(listFrameResId); + final boolean listShow = res.getBoolean(R.bool.layout_list_show); + listFrame.setVisibility(isInMultiWindowMode() || listShow ? View.VISIBLE : View.GONE); + + final View mapFrame = findViewById(mapFrameResId); + final boolean mapShow = res.getBoolean(R.bool.layout_map_show); + mapFrame.setVisibility(!isInMultiWindowMode() && mapShow ? View.VISIBLE : View.GONE); + + listFrame.getLayoutParams().width = listShow && mapShow ? res.getDimensionPixelSize(R.dimen.layout_list_width) + : LinearLayout.LayoutParams.MATCH_PARENT; + + final ViewGroup navigationDrawer = (ViewGroup) findViewById(R.id.navigation_drawer_layout); + if (navigationDrawer != null) { + final View child = navigationDrawer.getChildAt(1); + child.getLayoutParams().width = res.getDimensionPixelSize(R.dimen.layout_navigation_drawer_width); + } + } + protected String prefsGetNetwork() { return prefs.getString(Constants.PREFS_KEY_NETWORK_PROVIDER, null); } @@ -83,6 +109,11 @@ public abstract class OeffiActivity extends Activity { return ((Application) getApplication()).packageInfo().firstInstallTime; } + @Override + protected final boolean isRouteDisplayed() { + return false; + } + protected final MyActionBar getMyActionBar() { return (MyActionBar) findViewById(R.id.action_bar); } diff --git a/oeffi/src/de/schildbach/oeffi/OeffiMapView.java b/oeffi/src/de/schildbach/oeffi/OeffiMapView.java new file mode 100644 index 0000000..7d06527 --- /dev/null +++ b/oeffi/src/de/schildbach/oeffi/OeffiMapView.java @@ -0,0 +1,662 @@ +package de.schildbach.oeffi; + +import java.util.ArrayList; +import java.util.List; + +import com.google.android.maps.GeoPoint; +import com.google.android.maps.MapController; +import com.google.android.maps.MapView; +import com.google.android.maps.Overlay; +import com.google.android.maps.Projection; + +import de.schildbach.oeffi.stations.LineView; +import de.schildbach.oeffi.stations.Station; +import de.schildbach.pte.dto.Location; +import de.schildbach.pte.dto.Point; +import de.schildbach.pte.dto.Product; +import de.schildbach.pte.dto.Trip; +import de.schildbach.pte.dto.Trip.Leg; +import de.schildbach.pte.dto.Trip.Public; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Path.FillType; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.widget.TextView; + +public class OeffiMapView extends MapView { + private FromViaToAware fromViaToAware = null; + private TripAware tripAware = null; + private StationsAware stationsAware = null; + private LocationAware locationAware = null; + private AreaAware areaAware = null; + private boolean firstLocation = true; + private boolean zoomLocked = true; + + private final int AREA_FILL_COLOR = Color.parseColor("#22000000"); + + public OeffiMapView(final Context context) { + this(context, null, 0); + } + + public OeffiMapView(final Context context, final AttributeSet attrs) { + this(context, attrs, 0); + } + + public OeffiMapView(final Context context, final AttributeSet attrs, final int defStyle) { + super(context, attrs, defStyle); + + final Resources res = context.getResources(); + final LayoutInflater inflater = LayoutInflater.from(context); + + final float stationFontSize = res.getDimension(R.dimen.font_size_normal); + final float tripStrokeWidth = res.getDimension(R.dimen.map_trip_stroke_width); + final float tripStrokeWidthSelected = res.getDimension(R.dimen.map_trip_stroke_width_selected); + final float tripStrokeWidthSelectedGlow = res.getDimension(R.dimen.map_trip_stroke_width_selected_glow); + + final Drawable startIcon = drawablePointer(R.drawable.ic_maps_indicator_startpoint_list, 2); + final Drawable pointIcon = drawableCenter(R.drawable.ic_maps_product_default, 2); + final Drawable endIcon = drawablePointer(R.drawable.ic_maps_indicator_endpoint_list, 2); + + final Drawable deviceLocationIcon = drawableCenter(R.drawable.location_on, 2); + final Drawable referenceLocationIcon = drawablePointer(R.drawable.da_marker_red, 2); + + final Drawable glowIcon = drawableCenter(R.drawable.station_glow, 2); + final Drawable stationDefaultIcon = drawableCenter(R.drawable.ic_maps_product_default, 2); + final Drawable stationHighspeedIcon = drawableCenter(R.drawable.product_highspeed_color_22dp, 2); + final Drawable stationTrainIcon = drawableCenter(R.drawable.product_train_color_22dp, 2); + final Drawable stationSuburbanIcon = drawableCenter(R.drawable.product_suburban_color_22dp, 2); + final Drawable stationSubwayIcon = drawableCenter(R.drawable.product_subway_color_22dp, 2); + final Drawable stationTramIcon = drawableCenter(R.drawable.product_tram_color_22dp, 2); + final Drawable stationBusIcon = drawableCenter(R.drawable.product_bus_color_22dp, 2); + final Drawable stationFerryIcon = drawableCenter(R.drawable.product_ferry_color_22dp, 2); + final Drawable stationCablecarIcon = drawableCenter(R.drawable.product_cablecar_color_22dp, 2); + final Drawable stationCallIcon = drawableCenter(R.drawable.product_call_color_22dp, 2); + + getController().setZoom(Constants.INITIAL_MAP_ZOOM_LEVEL); + + getOverlays().add(new Overlay() { + @Override + public void draw(final Canvas canvas, final MapView mapView, final boolean shadow) { + if (!shadow) { + final Projection projection = mapView.getProjection(); + final android.graphics.Point point = new android.graphics.Point(); + + if (areaAware != null) { + final Point[] area = areaAware.getArea(); + if (area != null) { + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.FILL); + paint.setColor(AREA_FILL_COLOR); + + final Path path = pointsToPath(projection, area); + path.close(); + + path.setFillType(FillType.INVERSE_WINDING); + + canvas.drawPath(path, paint); + } + } + + if (fromViaToAware != null) { + final List path = new ArrayList<>(3); + final Point from = fromViaToAware.getFrom(); + if (from != null) + path.add(from); + final Point via = fromViaToAware.getVia(); + if (via != null) + path.add(via); + final Point to = fromViaToAware.getTo(); + if (to != null) + path.add(to); + + if (path.size() >= 2) { + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setStrokeCap(Paint.Cap.ROUND); + + paint.setColor(Color.DKGRAY); + paint.setAlpha(92); + paint.setStrokeWidth(tripStrokeWidth); + canvas.drawPath(pointsToPath(projection, path), paint); + } + + if (from != null) { + projection.toPixels(new GeoPoint(from.lat, from.lon), point); + drawAt(canvas, startIcon, point.x, point.y, false); + } + + if (to != null) { + projection.toPixels(new GeoPoint(to.lat, to.lon), point); + drawAt(canvas, endIcon, point.x, point.y, false); + } + } + + if (tripAware != null) { + final Trip trip = tripAware.getTrip(); + + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setStrokeCap(Paint.Cap.ROUND); + + // first paint all unselected legs + for (final Trip.Leg leg : trip.legs) { + if (!tripAware.isSelectedLeg(leg)) { + final Path path = pointsToPath(projection, leg.path); + + paint.setColor(leg instanceof Public ? Color.RED : Color.DKGRAY); + paint.setAlpha(92); + paint.setStrokeWidth(tripStrokeWidth); + canvas.drawPath(path, paint); + } + } + + // then paint selected legs + for (final Trip.Leg leg : trip.legs) { + if (tripAware.isSelectedLeg(leg)) { + final List points = leg.path; + final Path path = pointsToPath(projection, points); + + paint.setColor(Color.GREEN); + paint.setAlpha(92); + paint.setStrokeWidth(tripStrokeWidthSelectedGlow); + canvas.drawPath(path, paint); + + paint.setColor(leg instanceof Public ? Color.RED : Color.DKGRAY); + paint.setAlpha(128); + paint.setStrokeWidth(tripStrokeWidthSelected); + canvas.drawPath(path, paint); + + if (leg instanceof Public && !points.isEmpty()) { + final Public publicLeg = (Public) leg; + + final int lat; + final int lon; + + final int size = points.size(); + if (size >= 2) { + final int pivot = size / 2; + final Point p1 = points.get(pivot - 1); + final Point p2 = points.get(pivot); + lat = (p1.lat + p2.lat) / 2; + lon = (p1.lon + p2.lon) / 2; + } else if (size == 1) { + final Point p = points.get(0); + lat = p.lat; + lon = p.lon; + } else { + lat = 0; + lon = 0; + } + projection.toPixels(new GeoPoint(lat, lon), point); + + final LineView lineView = (LineView) inflater.inflate(R.layout.map_trip_line, null); + lineView.setLine(publicLeg.line); + lineView.measure(MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0), + MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0)); + final int width = lineView.getMeasuredWidth(); + final int height = lineView.getMeasuredHeight(); + lineView.layout(point.x - width / 2, point.y - height / 2, point.x + width / 2, + point.y + height / 2); + // since point.x is ignored in layout (why?), we need to translate canvas + // ourselves + canvas.save(); + canvas.translate(point.x - width / 2, point.y - height / 2); + lineView.draw(canvas); + canvas.restore(); + } + } + } + + // then paint decorators + final Leg firstLeg = trip.legs.get(0); + final Leg lastLeg = trip.legs.get(trip.legs.size() - 1); + + for (final Trip.Leg leg : trip.legs) { + if (!leg.path.isEmpty()) { + final Point firstPoint = leg.path.get(0); + final Point lastPoint = leg.path.get(leg.path.size() - 1); + + if (firstPoint == lastPoint) { + projection.toPixels(new GeoPoint(firstPoint.lat, firstPoint.lon), point); + drawAt(canvas, startIcon, point.x, point.y, false); + } else if (leg == firstLeg || leg == lastLeg) { + if (leg == firstLeg) { + projection.toPixels(new GeoPoint(firstPoint.lat, firstPoint.lon), point); + drawAt(canvas, startIcon, point.x, point.y, false); + } + + if (leg == lastLeg) { + projection.toPixels(new GeoPoint(lastPoint.lat, lastPoint.lon), point); + drawAt(canvas, endIcon, point.x, point.y, false); + } + } else { + projection.toPixels(new GeoPoint(firstPoint.lat, firstPoint.lon), point); + drawAt(canvas, pointIcon, point.x, point.y, false); + projection.toPixels(new GeoPoint(lastPoint.lat, lastPoint.lon), point); + drawAt(canvas, pointIcon, point.x, point.y, false); + } + } + } + } + + if (locationAware != null) { + final Point deviceLocation = locationAware.getDeviceLocation(); + if (deviceLocation != null) { + projection.toPixels(new GeoPoint(deviceLocation.lat, deviceLocation.lon), point); + drawAt(canvas, deviceLocationIcon, point.x, point.y, false); + } + + final Location referenceLocation = locationAware.getReferenceLocation(); + if (referenceLocation != null) { + projection.toPixels(new GeoPoint(referenceLocation.lat, referenceLocation.lon), point); + drawAt(canvas, referenceLocationIcon, point.x, point.y, false); + } + } + + if (stationsAware != null) { + final List stations = stationsAware.getStations(); + if (stations != null) { + Station selectedStation = null; + + for (final Station station : stations) { + if (station.location.hasLocation()) { + projection.toPixels(new GeoPoint(station.location.lat, station.location.lon), + point); + + if (stationsAware.isSelectedStation(station.location.id)) { + drawAt(canvas, glowIcon, point.x, point.y, false); + selectedStation = station; + } + + final Product product = station.getRelevantProduct(); + if (product == null) + drawAt(canvas, stationDefaultIcon, point.x, point.y, false); + else if (product == Product.HIGH_SPEED_TRAIN) + drawAt(canvas, stationHighspeedIcon, point.x, point.y, false); + else if (product == Product.REGIONAL_TRAIN) + drawAt(canvas, stationTrainIcon, point.x, point.y, false); + else if (product == Product.SUBURBAN_TRAIN) + drawAt(canvas, stationSuburbanIcon, point.x, point.y, false); + else if (product == Product.SUBWAY) + drawAt(canvas, stationSubwayIcon, point.x, point.y, false); + else if (product == Product.TRAM) + drawAt(canvas, stationTramIcon, point.x, point.y, false); + else if (product == Product.BUS) + drawAt(canvas, stationBusIcon, point.x, point.y, false); + else if (product == Product.FERRY) + drawAt(canvas, stationFerryIcon, point.x, point.y, false); + else if (product == Product.CABLECAR) + drawAt(canvas, stationCablecarIcon, point.x, point.y, false); + else if (product == Product.ON_DEMAND) + drawAt(canvas, stationCallIcon, point.x, point.y, false); + else + drawAt(canvas, stationDefaultIcon, point.x, point.y, false); + } + } + + if (selectedStation != null) { + projection.toPixels( + new GeoPoint(selectedStation.location.lat, selectedStation.location.lon), + point); + final TextView bubble = new TextView(getContext()); + bubble.setBackgroundResource(R.drawable.popup_dir_pointer_button); + bubble.setText(selectedStation.location.name); + bubble.setTypeface(Typeface.DEFAULT_BOLD); + bubble.setTextSize(TypedValue.COMPLEX_UNIT_PX, stationFontSize); + bubble.setIncludeFontPadding(false); + bubble.measure(MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0), + MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0)); + final int width = bubble.getMeasuredWidth(); + final int height = bubble.getMeasuredHeight(); + bubble.layout(point.x - width / 2, point.y - height / 2, point.x + width / 2, + point.y + height / 2); + // since point.x is ignored in layout (why?), we need to translate canvas + // ourselves + canvas.save(); + canvas.translate(point.x - width / 2, + point.y - height - stationDefaultIcon.getIntrinsicHeight() / 2.5f); + bubble.draw(canvas); + canvas.restore(); + } + } + } + } + } + + private Path pointsToPath(final Projection projection, final List points) { + final Path path = new Path(); + path.incReserve(points.size()); + + final android.graphics.Point point = new android.graphics.Point(); + + for (final Point p : points) { + projection.toPixels(new GeoPoint(p.lat, p.lon), point); + if (path.isEmpty()) + path.moveTo(point.x, point.y); + else + path.lineTo(point.x, point.y); + } + + return path; + } + + private Path pointsToPath(final Projection projection, final Point[] points) { + final Path path = new Path(); + path.incReserve(points.length); + + final android.graphics.Point point = new android.graphics.Point(); + + for (final Point p : points) { + projection.toPixels(new GeoPoint(p.lat, p.lon), point); + if (path.isEmpty()) + path.moveTo(point.x, point.y); + else + path.lineTo(point.x, point.y); + } + + return path; + } + + @Override + public boolean onTap(final GeoPoint p, final MapView mapView) { + final double tappedLat = p.getLatitudeE6() / 1E6; + final double tappedLon = p.getLongitudeE6() / 1E6; + boolean consumed = false; + + final float[] distanceBetweenResults = new float[1]; + + if (tripAware != null) { + int tappedLegIndex = -1; + float tappedPointDistance = 0; + + int iRoute = 0; + for (final Leg leg : tripAware.getTrip().legs) { + for (final Point point : leg.path) { + android.location.Location.distanceBetween(tappedLat, tappedLon, point.lat / 1E6, + point.lon / 1E6, distanceBetweenResults); + final float distance = distanceBetweenResults[0]; + if (tappedLegIndex == -1 || distance < tappedPointDistance) { + tappedLegIndex = iRoute; + tappedPointDistance = distance; + } + } + + iRoute++; + } + + if (tappedLegIndex != -1) { + tripAware.selectLeg(tappedLegIndex); + consumed = true; + } + } + + if (stationsAware != null) { + Station tappedStation = null; + float tappedStationDistance = 0; + + for (final Station station : stationsAware.getStations()) { + android.location.Location.distanceBetween(tappedLat, tappedLon, station.location.lat / 1E6, + station.location.lon / 1E6, distanceBetweenResults); + final float distance = distanceBetweenResults[0]; + if (tappedStation == null || distance < tappedStationDistance) { + tappedStation = station; + tappedStationDistance = distance; + } + } + + if (locationAware != null) { + if (tappedStation == null) { + stationsAware.selectStation(null); + consumed = true; + } else { + final Point deviceLocation = locationAware.getDeviceLocation(); + if (deviceLocation != null) { + android.location.Location.distanceBetween(tappedLat, tappedLon, + deviceLocation.lat / 1E6, deviceLocation.lon / 1E6, distanceBetweenResults); + final float distance = distanceBetweenResults[0]; + if (distance < tappedStationDistance) { + stationsAware.selectStation(null); + consumed = true; + } + } + } + } + + if (!consumed && tappedStation != null) { + stationsAware.selectStation(tappedStation); + consumed = true; + } + } + + return consumed; + } + }); + } + + public void setFromViaToAware(final FromViaToAware fromViaToAware) { + this.fromViaToAware = fromViaToAware; + invalidate(); + } + + public void setTripAware(final TripAware tripAware) { + this.tripAware = tripAware; + invalidate(); + } + + public void setStationsAware(final StationsAware stationsAware) { + this.stationsAware = stationsAware; + invalidate(); + } + + public void setLocationAware(final LocationAware locationAware) { + this.locationAware = locationAware; + invalidate(); + } + + public void setAreaAware(final AreaAware areaAware) { + this.areaAware = areaAware; + invalidate(); + } + + public void animateToLocation(final int lat, final int lon) { + if (lat == 0 && lon == 0) + return; + + final GeoPoint point = new GeoPoint(lat, lon); + + if (firstLocation) + getController().setCenter(point); + else + getController().animateTo(point); + + firstLocation = false; + } + + public void zoomToAll(final int maxZoomLevel) { + zoomLocked = true; + + final boolean hasLegSelection = tripAware != null && tripAware.hasSelection(); + + int minLat = Integer.MAX_VALUE, maxLat = Integer.MIN_VALUE; + int minLon = Integer.MAX_VALUE, maxLon = Integer.MIN_VALUE; + + boolean anything = false; + + if (areaAware != null) { + final Point[] area = areaAware.getArea(); + if (area != null) { + for (final Point p : area) { + final int lat = p.lat; + final int lon = p.lon; + + if (lat < minLat) + minLat = lat; + if (lat > maxLat) + maxLat = lat; + if (lon < minLon) + minLon = lon; + if (lon > maxLon) + maxLon = lon; + + anything = true; + } + } + } + + if (fromViaToAware != null) { + final Point from = fromViaToAware.getFrom(); + if (from != null) { + if (from.lat < minLat) + minLat = from.lat; + if (from.lat > maxLat) + maxLat = from.lat; + if (from.lon < minLon) + minLon = from.lon; + if (from.lon > maxLon) + maxLon = from.lon; + + anything = true; + } + + final Point via = fromViaToAware.getVia(); + if (via != null) { + if (via.lat < minLat) + minLat = via.lat; + if (via.lat > maxLat) + maxLat = via.lat; + if (via.lon < minLon) + minLon = via.lon; + if (via.lon > maxLon) + maxLon = via.lon; + + anything = true; + } + + final Point to = fromViaToAware.getTo(); + if (to != null) { + if (to.lat < minLat) + minLat = to.lat; + if (to.lat > maxLat) + maxLat = to.lat; + if (to.lon < minLon) + minLon = to.lon; + if (to.lon > maxLon) + maxLon = to.lon; + + anything = true; + } + } + + if (tripAware != null) { + for (final Leg leg : tripAware.getTrip().legs) { + if (!hasLegSelection || tripAware.isSelectedLeg(leg)) { + for (final Point p : leg.path) { + final int lat = p.lat; + final int lon = p.lon; + + if (lat < minLat) + minLat = lat; + if (lat > maxLat) + maxLat = lat; + if (lon < minLon) + minLon = lon; + if (lon > maxLon) + maxLon = lon; + + anything = true; + } + } + } + } + + if (locationAware != null && !hasLegSelection) { + final Location referenceLocation = locationAware.getReferenceLocation(); + if (referenceLocation != null) { + if (referenceLocation.lat < minLat) + minLat = referenceLocation.lat; + if (referenceLocation.lat > maxLat) + maxLat = referenceLocation.lat; + if (referenceLocation.lon < minLon) + minLon = referenceLocation.lon; + if (referenceLocation.lon > maxLon) + maxLon = referenceLocation.lon; + + anything = true; + } else { + final Point location = locationAware.getDeviceLocation(); + if (location != null) { + if (location.lat < minLat) + minLat = location.lat; + if (location.lat > maxLat) + maxLat = location.lat; + if (location.lon < minLon) + minLon = location.lon; + if (location.lon > maxLon) + maxLon = location.lon; + + anything = true; + } + } + } + + if (anything) { + final MapController controller = getController(); + + final float spanLat = (maxLat - minLat) * 1.1f; + final float spanLon = (maxLon - minLon) * 1.1f; + controller.zoomToSpan((int) spanLat, (int) spanLon); + if (getZoomLevel() > maxZoomLevel) + controller.setZoom(maxZoomLevel); + + final int centerLat = (maxLat + minLat) / 2; + final int centerLon = (maxLon + minLon) / 2; + controller.setCenter(new GeoPoint(centerLat, centerLon)); + } + } + + @Override + protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) { + if (zoomLocked) + zoomToAll(Constants.INITIAL_MAP_ZOOM_LEVEL); + + super.onSizeChanged(w, h, oldw, oldh); + } + + @Override + public boolean onTouchEvent(final MotionEvent ev) { + zoomLocked = false; + + return super.onTouchEvent(ev); + } + + private Drawable drawablePointer(final int resId, final int sizeDivider) { + final Resources res = getResources(); + final Drawable drawable = res.getDrawable(resId); + drawable.setBounds(-drawable.getIntrinsicWidth() / sizeDivider, -drawable.getIntrinsicHeight(), + drawable.getIntrinsicWidth() / sizeDivider, 0); + return drawable; + } + + private Drawable drawableCenter(final int resId, final int sizeDivider) { + final Resources res = getResources(); + final Drawable drawable = res.getDrawable(resId); + drawable.setBounds(-drawable.getIntrinsicWidth() / sizeDivider, -drawable.getIntrinsicHeight() / sizeDivider, + drawable.getIntrinsicWidth() / sizeDivider, drawable.getIntrinsicHeight() / sizeDivider); + return drawable; + } +} diff --git a/oeffi/src/de/schildbach/oeffi/directions/DirectionsActivity.java b/oeffi/src/de/schildbach/oeffi/directions/DirectionsActivity.java index dd6be2e..22d9ca1 100644 --- a/oeffi/src/de/schildbach/oeffi/directions/DirectionsActivity.java +++ b/oeffi/src/de/schildbach/oeffi/directions/DirectionsActivity.java @@ -36,9 +36,16 @@ import javax.net.ssl.SSLException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.android.maps.GeoPoint; +import com.google.android.maps.MapController; +import com.google.android.maps.MapView; +import com.google.android.maps.Overlay; + import de.schildbach.oeffi.Constants; +import de.schildbach.oeffi.FromViaToAware; import de.schildbach.oeffi.MyActionBar; import de.schildbach.oeffi.OeffiMainActivity; +import de.schildbach.oeffi.OeffiMapView; import de.schildbach.oeffi.R; import de.schildbach.oeffi.directions.TimeSpec.DepArr; import de.schildbach.oeffi.directions.list.QueryHistoryAdapter; @@ -55,6 +62,7 @@ import de.schildbach.oeffi.util.ConnectivityBroadcastReceiver; import de.schildbach.oeffi.util.DialogBuilder; import de.schildbach.oeffi.util.DividerItemDecoration; import de.schildbach.oeffi.util.Formats; +import de.schildbach.oeffi.util.GeocoderThread; import de.schildbach.oeffi.util.LocationUriParser; import de.schildbach.oeffi.util.Toast; import de.schildbach.oeffi.util.ToggleImageButton; @@ -68,6 +76,7 @@ import de.schildbach.pte.NetworkProvider.Option; import de.schildbach.pte.NetworkProvider.WalkSpeed; import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.LocationType; +import de.schildbach.pte.dto.Point; import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.QueryTripsResult; import de.schildbach.pte.dto.SuggestLocationsResult; @@ -89,7 +98,9 @@ import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.database.Cursor; +import android.location.Address; import android.net.ConnectivityManager; import android.net.Uri; import android.os.Bundle; @@ -140,6 +151,7 @@ public class DirectionsActivity extends OeffiMainActivity implements ActivityCom private RecyclerView viewQueryHistoryList; private QueryHistoryAdapter queryHistoryListAdapter; private TextView connectivityWarningView; + private OeffiMapView mapView; private TimeSpec time = null; @@ -256,6 +268,8 @@ public class DirectionsActivity extends OeffiMainActivity implements ActivityCom expandForm(); else collapseForm(); + + updateMap(); } }); actionBar.addButton(R.drawable.ic_shuffle_white_24dp, R.string.directions_action_return_trip_title) @@ -292,6 +306,7 @@ public class DirectionsActivity extends OeffiMainActivity implements ActivityCom final LocationView.Listener locationChangeListener = new LocationView.Listener() { public void changed() { + updateMap(); queryHistoryListAdapter.clearSelectedEntry(); requestFocusFirst(); } @@ -413,6 +428,57 @@ public class DirectionsActivity extends OeffiMainActivity implements ActivityCom queryHistoryListAdapter = new QueryHistoryAdapter(this, network, this, this); viewQueryHistoryList.setAdapter(queryHistoryListAdapter); + mapView = (OeffiMapView) findViewById(R.id.directions_map); + mapView.getOverlays().add(new Overlay() { + private Location pinLocation; + + @Override + public boolean onTap(final GeoPoint p, final MapView mapView) { + pinLocation = Location.coord(p.getLatitudeE6(), p.getLongitudeE6()); + + final View view = getLayoutInflater().inflate(R.layout.directions_map_pin, null); + final LocationTextView locationView = (LocationTextView) view + .findViewById(R.id.directions_map_pin_location); + final View buttonGroup = view.findViewById(R.id.directions_map_pin_buttons); + buttonGroup.findViewById(R.id.directions_map_pin_button_from).setOnClickListener(new OnClickListener() { + public void onClick(final View v) { + viewFromLocation.setLocation(pinLocation); + mapView.removeAllViews(); + } + }); + buttonGroup.findViewById(R.id.directions_map_pin_button_to).setOnClickListener(new OnClickListener() { + public void onClick(final View v) { + viewToLocation.setLocation(pinLocation); + mapView.removeAllViews(); + } + }); + locationView.setLocation(pinLocation); + locationView.setShowLocationType(false); + + mapView.removeAllViews(); + mapView.addView(view, new MapView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, p, MapView.LayoutParams.BOTTOM_CENTER)); + + new GeocoderThread(DirectionsActivity.this, p.getLatitudeE6() / 1E6, p.getLongitudeE6() / 1E6, + new GeocoderThread.Callback() { + public void onGeocoderResult(final Address address) { + pinLocation = LocationView.addressToLocation(address); + locationView.setLocation(pinLocation); + locationView.setShowLocationType(false); + } + + public void onGeocoderFail(final Exception exception) { + log.info("Problem in geocoder: {}", exception.getMessage()); + } + }); + + final MapController controller = mapView.getController(); + controller.animateTo(p); + + return false; + } + }); + connectivityReceiver = new ConnectivityBroadcastReceiver(connectivityManager) { @Override protected void onConnected() { @@ -490,6 +556,8 @@ public class DirectionsActivity extends OeffiMainActivity implements ActivityCom setActionBarSecondaryTitleFromNetwork(); updateGUI(); + updateMap(); + updateFragments(); } @Override @@ -555,6 +623,13 @@ public class DirectionsActivity extends OeffiMainActivity implements ActivityCom super.onDestroy(); } + @Override + public void onConfigurationChanged(final Configuration config) { + super.onConfigurationChanged(config); + + updateFragments(); + } + @Override public void onBackPressed() { if (isNavigationOpen()) @@ -572,6 +647,10 @@ public class DirectionsActivity extends OeffiMainActivity implements ActivityCom viewGo.requestFocus(); } + private void updateFragments() { + updateFragments(R.id.navigation_drawer_layout, R.id.directions_map_fragment); + } + private void updateGUI() { viewFromLocation.setHint(R.string.directions_from); viewViaLocation.setHint(R.string.directions_via); @@ -674,6 +753,33 @@ public class DirectionsActivity extends OeffiMainActivity implements ActivityCom builder.show(); } + private void updateMap() { + mapView.removeAllViews(); + mapView.setFromViaToAware(new FromViaToAware() { + public Point getFrom() { + final Location from = viewFromLocation.getLocation(); + if (from == null || !from.hasLocation()) + return null; + return new Point(from.lat, from.lon); + } + + public Point getVia() { + final Location via = viewViaLocation.getLocation(); + if (via == null || !via.hasLocation() || viewViaLocation.getVisibility() != View.VISIBLE) + return null; + return new Point(via.lat, via.lon); + } + + public Point getTo() { + final Location to = viewToLocation.getLocation(); + if (to == null || !to.hasLocation()) + return null; + return new Point(to.lat, to.lon); + } + }); + mapView.zoomToAll(Constants.INITIAL_MAP_ZOOM_LEVEL); + } + private void expandForm() { initLayoutTransitions(true); diff --git a/oeffi/src/de/schildbach/oeffi/directions/TripDetailsActivity.java b/oeffi/src/de/schildbach/oeffi/directions/TripDetailsActivity.java index f718694..24e8519 100644 --- a/oeffi/src/de/schildbach/oeffi/directions/TripDetailsActivity.java +++ b/oeffi/src/de/schildbach/oeffi/directions/TripDetailsActivity.java @@ -34,7 +34,9 @@ import de.schildbach.oeffi.Constants; import de.schildbach.oeffi.LocationAware; import de.schildbach.oeffi.MyActionBar; import de.schildbach.oeffi.OeffiActivity; +import de.schildbach.oeffi.OeffiMapView; import de.schildbach.oeffi.R; +import de.schildbach.oeffi.TripAware; import de.schildbach.oeffi.directions.TimeSpec.DepArr; import de.schildbach.oeffi.stations.LineView; import de.schildbach.oeffi.stations.NetworkContentProvider; @@ -65,6 +67,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Color; @@ -118,6 +121,7 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen private BroadcastReceiver tickReceiver; private ViewGroup legsGroup; + private OeffiMapView mapView; private ToggleImageButton trackButton; private NetworkId network; @@ -125,6 +129,7 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen private Date highlightedTime; private Location highlightedLocation; private Point location; + private int selectedLegIndex = -1; private final Map legExpandStates = new HashMap<>(); private Intent scheduleTripIntent; @@ -223,12 +228,16 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen location = LocationHelper.locationToPoint(lastKnownLocation); else location = null; + mapView.setLocationAware(TripDetailsActivity.this); } } else { locationManager.removeUpdates(TripDetailsActivity.this); location = null; + + mapView.setLocationAware(null); } + mapView.zoomToAll(Constants.INITIAL_MAP_ZOOM_LEVEL); updateGUI(); } }); @@ -281,6 +290,31 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen final TextView disclaimerSourceView = (TextView) findViewById(R.id.directions_trip_details_disclaimer_source); updateDisclaimerSource(disclaimerSourceView, network.name(), null); + + mapView = (OeffiMapView) findViewById(R.id.directions_trip_details_map); + mapView.setTripAware(new TripAware() { + public Trip getTrip() { + return trip; + } + + public void selectLeg(final int partIndex) { + selectedLegIndex = partIndex; + mapView.invalidate(); + mapView.zoomToAll(Constants.INITIAL_MAP_ZOOM_LEVEL); + } + + public boolean hasSelection() { + return selectedLegIndex != -1; + } + + public boolean isSelectedLeg(final Leg part) { + if (!hasSelection()) + return false; + + return trip.legs.get(selectedLegIndex).equals(part); + } + }); + mapView.zoomToAll(Constants.INITIAL_MAP_ZOOM_LEVEL); } @Override @@ -297,6 +331,7 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen registerReceiver(tickReceiver, new IntentFilter(Intent.ACTION_TIME_TICK)); updateGUI(); + updateFragments(); } @Override @@ -321,6 +356,13 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } + @Override + public void onConfigurationChanged(final Configuration config) { + super.onConfigurationChanged(config); + + updateFragments(); + } + private String requestLocationUpdates() { final Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); @@ -347,6 +389,10 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen public void onProviderDisabled(final String provider) { locationManager.removeUpdates(TripDetailsActivity.this); + + final String newProvider = requestLocationUpdates(); + if (newProvider == null) + mapView.setLocationAware(null); } public void onStatusChanged(final String provider, final int status, final Bundle extras) { @@ -364,6 +410,10 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen return null; } + private void updateFragments() { + updateFragments(R.id.directions_trip_details_list_frame, R.id.directions_trip_details_map_frame); + } + private void updateGUI() { final Date now = new Date(); updateHighlightedTime(now); @@ -378,6 +428,7 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen i++; } + mapView.invalidate(); } private void updateHighlightedTime(final Date now) { diff --git a/oeffi/src/de/schildbach/oeffi/network/NetworkPickerActivity.java b/oeffi/src/de/schildbach/oeffi/network/NetworkPickerActivity.java index 303bbba..d98830f 100644 --- a/oeffi/src/de/schildbach/oeffi/network/NetworkPickerActivity.java +++ b/oeffi/src/de/schildbach/oeffi/network/NetworkPickerActivity.java @@ -31,8 +31,13 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.android.maps.MapActivity; + +import de.schildbach.oeffi.AreaAware; import de.schildbach.oeffi.Constants; +import de.schildbach.oeffi.LocationAware; import de.schildbach.oeffi.MyActionBar; +import de.schildbach.oeffi.OeffiMapView; import de.schildbach.oeffi.R; import de.schildbach.oeffi.network.list.NetworkClickListener; import de.schildbach.oeffi.network.list.NetworkContextMenuItemListener; @@ -46,16 +51,17 @@ import de.schildbach.oeffi.util.LocationHelper; import de.schildbach.pte.AbstractNavitiaProvider; import de.schildbach.pte.NetworkId; import de.schildbach.pte.NetworkProvider; +import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.Point; import android.Manifest; import android.annotation.TargetApi; -import android.app.Activity; import android.app.ActivityManager.TaskDescription; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.content.res.Resources; import android.location.Address; import android.location.Criteria; @@ -73,8 +79,9 @@ import android.support.v7.widget.RecyclerView; import android.view.KeyEvent; import android.view.View; import android.widget.FrameLayout; +import android.widget.LinearLayout; -public class NetworkPickerActivity extends Activity implements ActivityCompat.OnRequestPermissionsResultCallback, +public class NetworkPickerActivity extends MapActivity implements ActivityCompat.OnRequestPermissionsResultCallback, LocationHelper.Callback, NetworkClickListener, NetworkContextMenuItemListener { public static void start(final Context context) { final Intent intent = new Intent(context, NetworkPickerActivity.class); @@ -86,6 +93,7 @@ public class NetworkPickerActivity extends Activity implements ActivityCompat.On private MyActionBar actionBar; private RecyclerView listView; private NetworksAdapter listAdapter; + private OeffiMapView mapView; private final List lastNetworks = new LinkedList<>(); @@ -128,6 +136,8 @@ public class NetworkPickerActivity extends Activity implements ActivityCompat.On listAdapter = new NetworksAdapter(this, network, this, this); listView.setAdapter(listAdapter); + mapView = (OeffiMapView) findViewById(R.id.network_picker_map); + if (network == null) { ((FrameLayout) findViewById(R.id.network_picker_firsttime_message_shadow)).setForeground(null); } else { @@ -137,6 +147,40 @@ public class NetworkPickerActivity extends Activity implements ActivityCompat.On finish(); } }); + final NetworkId networkId = prefsGetNetworkId(); + if (networkId != null) { + backgroundHandler.post(new GetAreaRunnable(NetworkProviderFactory.provider(networkId), handler) { + + @Override + protected void onResult(final Point[] area) { + mapView.setAreaAware(new AreaAware() { + final Point[] myArea = area != null && area.length > 1 ? area : null; + + public Point[] getArea() { + return myArea; + } + }); + mapView.setLocationAware(new LocationAware() { + final Location referenceLocation = area != null && area.length == 1 + ? Location.coord(area[0]) : null; + + public Point getDeviceLocation() { + return deviceLocation; + } + + public Location getReferenceLocation() { + return referenceLocation; + } + + public Float getDeviceBearing() { + return null; + } + }); + + mapView.zoomToAll(Constants.INITIAL_MAP_ZOOM_LEVEL_NETWORK); + } + }); + } } loadLastNetworks(); @@ -154,6 +198,7 @@ public class NetworkPickerActivity extends Activity implements ActivityCompat.On protected void onStart() { super.onStart(); + updateFragments(); maybeStartLocation(); } @@ -210,6 +255,8 @@ public class NetworkPickerActivity extends Activity implements ActivityCompat.On deviceLocation = here; + mapView.animateToLocation(here.lat, here.lon); + parseIndex(); updateGUI(); @@ -232,6 +279,13 @@ public class NetworkPickerActivity extends Activity implements ActivityCompat.On }); } + @Override + public void onConfigurationChanged(final Configuration config) { + super.onConfigurationChanged(config); + + updateFragments(); + } + @Override public boolean onKeyDown(final int keyCode, final KeyEvent event) { // disable back key if no network is selected @@ -267,8 +321,25 @@ public class NetworkPickerActivity extends Activity implements ActivityCompat.On } } + private void updateFragments() { + final Resources res = getResources(); + + final View listFrame = findViewById(R.id.network_picker_list_frame); + final boolean listShow = res.getBoolean(R.bool.layout_list_show); + listFrame.setVisibility(isInMultiWindowMode() || listShow ? View.VISIBLE : View.GONE); + + final View mapFrame = findViewById(R.id.network_picker_map_frame); + final boolean mapShow = res.getBoolean(R.bool.layout_map_show); + mapFrame.setVisibility(!isInMultiWindowMode() && mapShow ? View.VISIBLE : View.GONE); + + final LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) listFrame.getLayoutParams(); + layoutParams.width = listShow && mapShow ? res.getDimensionPixelSize(R.dimen.layout_list_width) + : LinearLayout.LayoutParams.MATCH_PARENT; + } + private void updateGUI() { listAdapter.notifyDataSetChanged(); + mapView.invalidate(); } private String prefsGetNetwork() { @@ -490,6 +561,11 @@ public class NetworkPickerActivity extends Activity implements ActivityCompat.On prefs.edit().putString(Constants.PREFS_KEY_LAST_NETWORK_PROVIDERS, prefsValue.toString()).commit(); } + @Override + protected boolean isRouteDisplayed() { + return false; + } + protected final void setPrimaryColor(final int colorResId) { final int color = getResources().getColor(colorResId); actionBar.setBackgroundColor(color); diff --git a/oeffi/src/de/schildbach/oeffi/stations/StationDetailsActivity.java b/oeffi/src/de/schildbach/oeffi/stations/StationDetailsActivity.java index 4239aaa..df12ccc 100644 --- a/oeffi/src/de/schildbach/oeffi/stations/StationDetailsActivity.java +++ b/oeffi/src/de/schildbach/oeffi/stations/StationDetailsActivity.java @@ -35,12 +35,14 @@ import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.android.maps.GeoPoint; import com.google.common.base.Joiner; import com.google.common.base.Strings; import de.schildbach.oeffi.Constants; import de.schildbach.oeffi.MyActionBar; import de.schildbach.oeffi.OeffiActivity; +import de.schildbach.oeffi.OeffiMapView; import de.schildbach.oeffi.R; import de.schildbach.oeffi.StationsAware; import de.schildbach.oeffi.network.NetworkProviderFactory; @@ -64,6 +66,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Typeface; @@ -127,6 +130,7 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa private DeparturesAdapter listAdapter; private TextView resultStatusView; private TextView disclaimerSourceView; + private OeffiMapView mapView; private BroadcastReceiver tickReceiver; @@ -187,6 +191,9 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa listAdapter = new DeparturesAdapter(this); listView.setAdapter(listAdapter); + mapView = (OeffiMapView) findViewById(R.id.stations_station_details_map); + mapView.setStationsAware(this); + resultStatusView = (TextView) findViewById(R.id.stations_station_details_result_status); final Intent intent = getIntent(); @@ -292,6 +299,8 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa registerReceiver(tickReceiver, new IntentFilter(Intent.ACTION_TIME_TICK)); load(); + + updateFragments(); } @Override @@ -316,6 +325,17 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } + @Override + public void onConfigurationChanged(final Configuration config) { + super.onConfigurationChanged(config); + + updateFragments(); + } + + private void updateFragments() { + updateFragments(R.id.stations_station_details_list_fragment, R.id.stations_station_details_map_fragment); + } + private void updateGUI() { final List selectedDepartures = this.selectedDepartures; if (selectedDepartures != null) { @@ -517,6 +537,9 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa selectedFavState = FavoriteStationsProvider.favState(getContentResolver(), selectedNetwork, selectedStation); + if (selectedStation.hasLocation()) + mapView.getController().animateTo(new GeoPoint(selectedStation.lat, selectedStation.lon)); + updateGUI(); actionBar.setPrimaryTitle(selectedStation.name); diff --git a/oeffi/src/de/schildbach/oeffi/stations/StationsActivity.java b/oeffi/src/de/schildbach/oeffi/stations/StationsActivity.java index 9647c79..6adf217 100644 --- a/oeffi/src/de/schildbach/oeffi/stations/StationsActivity.java +++ b/oeffi/src/de/schildbach/oeffi/stations/StationsActivity.java @@ -40,6 +40,7 @@ import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.android.maps.GeoPoint; import com.google.common.base.Strings; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Ordering; @@ -49,6 +50,7 @@ import de.schildbach.oeffi.Constants; import de.schildbach.oeffi.LocationAware; import de.schildbach.oeffi.MyActionBar; import de.schildbach.oeffi.OeffiMainActivity; +import de.schildbach.oeffi.OeffiMapView; import de.schildbach.oeffi.R; import de.schildbach.oeffi.StationsAware; import de.schildbach.oeffi.directions.DirectionsActivity; @@ -85,6 +87,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; import android.database.Cursor; @@ -148,6 +151,7 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware private String accurateLocationProvider, lowPowerLocationProvider; private MyActionBar actionBar; + private OeffiMapView stationMap; private RecyclerView stationList; private LinearLayoutManager stationListLayoutManager; private StationsAdapter stationListAdapter; @@ -237,6 +241,7 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware } stationListAdapter.notifyDataSetChanged(); + stationMap.invalidate(); } updateGUI(); @@ -286,6 +291,10 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware final Button missingCapabilityButton = (Button) findViewById(R.id.stations_network_missing_capability_button); missingCapabilityButton.setOnClickListener(selectNetworkListener); + stationMap = (OeffiMapView) findViewById(R.id.stations_map); + stationMap.setStationsAware(this); + stationMap.setLocationAware(this); + connectivityWarningView = (TextView) findViewById(R.id.stations_connectivity_warning_box); disclaimerSourceView = (TextView) findViewById(R.id.stations_disclaimer_source); @@ -478,6 +487,8 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware registerReceiver(tickReceiver, new IntentFilter(Intent.ACTION_TIME_TICK)); } + stationMap.zoomToAll(Constants.INITIAL_MAP_ZOOM_LEVEL); + setActionBarSecondaryTitleFromNetwork(); updateGUI(); } @@ -497,6 +508,7 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware stationsMap.clear(); stationListAdapter.notifyDataSetChanged(); + stationMap.invalidate(); loading = true; updateDisclaimerSource(disclaimerSourceView, network.name(), null); @@ -507,6 +519,17 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware handler.post(initStationsRunnable); } + @Override + public void onConfigurationChanged(final Configuration config) { + super.onConfigurationChanged(config); + + updateFragments(); + } + + private void updateFragments() { + updateFragments(R.id.navigation_drawer_layout, R.id.stations_map_fragment); + } + @Override protected void onPause() { saveProductFilter(); @@ -563,11 +586,15 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware fixedLocation = locations != null && locations.length >= 1 ? locations[0] : null; if (fixedLocation != null) { + stationMap.animateToLocation(fixedLocation.lat, fixedLocation.lon); + findViewById(R.id.stations_location_clear).setOnClickListener(new OnClickListener() { public void onClick(final View v) { fixedLocation = null; if (deviceLocation != null) { + stationMap.animateToLocation(deviceLocation.lat, deviceLocation.lon); + final float[] distanceBetweenResults = new float[2]; // remove non-favorites and re-calculate distances @@ -645,6 +672,9 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware } private void updateGUI() { + // fragments + updateFragments(); + // filter indicator final boolean isActive = products.size() < Product.values().length; filterActionButton.setSelected(isActive); @@ -985,6 +1015,7 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware if (added || changed) { stationListAdapter.notifyDataSetChanged(); + stationMap.invalidate(); } updateGUI(); @@ -1273,6 +1304,12 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware } } + // scroll map + if (station != null && station.location.hasLocation()) + stationMap.getController().animateTo(new GeoPoint(station.location.lat, station.location.lon)); + else if (station == null && deviceLocation != null) + stationMap.getController().animateTo(new GeoPoint(deviceLocation.lat, deviceLocation.lon)); + postLoadNextVisible(0); } @@ -1431,6 +1468,9 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware final double hereLat = here.getLatitude(); final double hereLon = here.getLongitude(); + if (deviceLocation == null && fixedLocation == null) + stationMap.animateToLocation((int) (hereLat * 1E6), (int) (hereLon * 1E6)); + deviceLocation = Point.fromDouble(hereLat, hereLon); stationListAdapter.setDeviceLocation(here);