/*
* Copyright (C) 2012 - 2013 jonas.oreland@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.runnerup.util;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.ViewTreeObserver;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import org.runnerup.R;
import org.runnerup.common.util.Constants;
import org.runnerup.db.entities.LocationEntity;
import java.util.ArrayList;
import java.util.List;
import static org.runnerup.util.Formatter.Format.TXT_SHORT;
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
public class MapWrapper implements Constants {
private MapView mapView = null;
private MapboxMap map;
private long mID = 0;
private SQLiteDatabase mDB = null;
private final Context context;
private Formatter formatter = null;
public MapWrapper(Context context, SQLiteDatabase mDB, long mID, Formatter formatter, MapView mapView) {
this.context = context;
this.mDB = mDB;
this.mID = mID;
this.formatter = formatter;
this.mapView = mapView;
}
public static void start(Context context) {
Mapbox.getInstance(context, context.getString(R.string.mapboxAccessToken));
}
private void setStyle() {
if (map != null) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
Resources res = context.getResources();
String val = prefs.getString(res.getString(R.string.pref_mapbox_default_style), "");
if (TextUtils.isEmpty(val)) {
//The preferences should prevent from setting an empty value for display reasons
//However, that handling is not so easy
//(MapBox seem to handle no style set OK though).
val = res.getString(R.string.mapboxDefaultStyle);
}
map.setStyleUrl(val);
}
}
public void onCreate(Bundle savedInstanceState) {
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(MapboxMap mapboxMap) {
map = mapboxMap;
setStyle();
new LoadRoute().execute(new LoadParam(context, mDB, mID));
}
});
}
public void onResume() {
setStyle();
mapView.onResume();
}
public void onStart() {
mapView.onStart();
}
public void onStop() {
mapView.onStop();
}
public void onPause() {
mapView.onPause();
}
public void onSaveInstanceState(Bundle outState) {
mapView.onSaveInstanceState(outState);
}
public void onLowMemory() {
mapView.onLowMemory();
}
public void onDestroy() {
mapView.onDestroy();
}
class Route {
final List<LatLng> path = new ArrayList<>(10);
final ArrayList<MarkerViewOptions> markers = new ArrayList<>(10);
}
private class LoadParam {
LoadParam(Context context, SQLiteDatabase mDB, long mID) {
this.context = context;
this.mDB = mDB;
this.mID = mID;
}
final Context context;
final SQLiteDatabase mDB;
final long mID;
}
private class LoadRoute extends AsyncTask<LoadParam, Void, Route> {
@Override
protected Route doInBackground(LoadParam... params) {
Route route = new Route();
LocationEntity.LocationList<LocationEntity> ll = new LocationEntity.LocationList<>(params[0].mDB, params[0].mID);
IconFactory iconFactory = IconFactory.getInstance(params[0].context);
int lastLap = 0;
for (LocationEntity loc : ll) {
LatLng point = new LatLng(loc.getLatitude(), loc.getLongitude());
route.path.add(point);
int type = loc.getType();
MarkerViewOptions m;
String title = "";
Integer iconId = null;
//Start/end markers are not set in db, special handling
if (route.markers.isEmpty()) {
type = DB.LOCATION.TYPE_START;
}
switch (type) {
case DB.LOCATION.TYPE_START:
iconId = R.drawable.ic_map_marker_start;
title = context.getResources().getString(R.string.Start);
break;
case DB.LOCATION.TYPE_END:
iconId = R.drawable.ic_map_marker_end;
title = context.getResources().getString(R.string.Stop);
break;
case DB.LOCATION.TYPE_PAUSE:
iconId = R.drawable.ic_map_marker_pause;
title = context.getResources().getString(R.string.Pause);
break;
case DB.LOCATION.TYPE_RESUME:
iconId = R.drawable.ic_map_marker_resume;
title = context.getResources().getString(R.string.Resume);
break;
case DB.LOCATION.TYPE_GPS:
break;
}
if (lastLap != loc.getLap()) {
if (lastLap >= 0) {
title = context.getString(R.string.cue_lap) + " " + loc.getLap();
}
iconId = R.drawable.ic_map_marker_lap;
lastLap = loc.getLap();
}
if (iconId != null) {
String snippet = formatter.formatDistance(TXT_SHORT, loc.getDistance().longValue()) + " " +
formatter.formatElapsedTime(TXT_SHORT, Math.round(loc.getElapsed() / 1000.0));
Icon icon = iconFactory.fromBitmap(BitmapFactory.decodeResource(context.getResources(),iconId));
m = new MarkerViewOptions().title(title).position(point).snippet(snippet).icon(icon).anchor(0.5f, 86f / 96f);
if (type == DB.LOCATION.TYPE_START) {
m.snippet(null);
}
route.markers.add(m);
}
}
ll.close();
//Track is ended with a pause, replace with end
if (!route.markers.isEmpty()) {
MarkerViewOptions m = route.markers.get(route.markers.size() - 1);
Icon icon = iconFactory.fromBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_map_marker_end));
m.title(context.getResources().getString(R.string.Stop)).icon(icon);
}
return route;
}
@Override
protected void onPostExecute(Route route) {
if (route != null && map != null &&
android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
if (route.path.size() > 1) {
LatLng[] pointsArray = new LatLng[route.path.size()];
route.path.toArray(pointsArray);
map.addPolyline(new PolylineOptions()
.add(pointsArray)
.color(Color.RED)
.width(3));
Log.v(getClass().getName(), "Added polyline");
final LatLngBounds box =
new LatLngBounds.Builder().includes(route.path).build();
final CameraUpdate initialCameraPosition = CameraUpdateFactory.newLatLngBounds(box, 50);
map.moveCamera(initialCameraPosition);
//Since MapBox 4.2.0-beta.3 moving the camera in onMapReady is not working if map is not visible
//The proper solution is a redesign using fragments, see https://github.com/mapbox/mapbox-gl-native/issues/6855#event-841575956
//A workaround is to try move the camera at view updates as long as position is 0,0
mapView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
// We check which build version we are using.
@Override
public void onGlobalLayout() {
if (map.getCameraPosition().target.getLatitude() != 0 ||
map.getCameraPosition().target.getLongitude() != 0) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mapView.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
} else {
mapView.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
}
} else {
map.moveCamera(initialCameraPosition);
}
}
}
);
}
if (route.markers.size() > 0) {
for (MarkerViewOptions m : route.markers) {
map.addMarker(m);
}
}
Log.v(getClass().getName(), "Added " + route.markers.size() + " markers");
}
}
}
}