diff --git a/app/src/main/java/de/apps4ics/mountainnavigation/HikePoint.java b/app/src/main/java/de/apps4ics/mountainnavigation/HikePoint.java new file mode 100644 index 0000000..6cadbd1 --- /dev/null +++ b/app/src/main/java/de/apps4ics/mountainnavigation/HikePoint.java @@ -0,0 +1,64 @@ +/** + * This file is part of MountainNavigation. + * + * MountainNavigation 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. + * + * MountainNavigation 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 MountainNavigation. If not, see . + * + * @copyright Copyright (c) 2016 Vinzenz Rosenkanz + * + * @author Vinzenz Rosenkranz + */ + +package de.apps4ics.mountainnavigation; + +import android.location.Location; + +import org.osmdroid.util.GeoPoint; + +public class HikePoint { + private long timestamp; + private GeoPoint location; + + public HikePoint(Location location, long timestamp) { + this(new GeoPoint(location.getLatitude(), location.getLongitude(), location.getAltitude()), timestamp); + } + + public HikePoint(GeoPoint geoPoint, long timestamp) { + this.location = geoPoint; + this.timestamp = timestamp; + } + + public HikePoint(GeoPoint geoPoint) { + this(geoPoint, System.currentTimeMillis()); + } + + public HikePoint(Location location) { + this(new GeoPoint(location.getLatitude(), location.getLongitude(), location.getAltitude())); + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public GeoPoint getLocation() { + return location; + } + + public void setLocation(GeoPoint location) { + this.location = location; + } +} diff --git a/app/src/main/java/de/apps4ics/mountainnavigation/MainActivity.java b/app/src/main/java/de/apps4ics/mountainnavigation/MainActivity.java index d098e7c..dbf9004 100644 --- a/app/src/main/java/de/apps4ics/mountainnavigation/MainActivity.java +++ b/app/src/main/java/de/apps4ics/mountainnavigation/MainActivity.java @@ -21,7 +21,9 @@ package de.apps4ics.mountainnavigation; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; @@ -43,11 +45,16 @@ import android.support.v7.app.AppCompatActivity; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; +import android.text.Editable; +import android.text.TextWatcher; import android.util.Log; +import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.support.v4.widget.DrawerLayout; import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; @@ -58,6 +65,7 @@ import org.osmdroid.api.IMapController; import org.osmdroid.bonuspack.overlays.Marker; +import org.osmdroid.bonuspack.overlays.Polyline; import org.osmdroid.tileprovider.tilesource.TileSourceFactory; import org.osmdroid.util.GeoPoint; import org.osmdroid.views.MapView; @@ -70,6 +78,7 @@ import de.apps4ics.mountainnavigation.adapters.ImageListAdapter; import de.apps4ics.mountainnavigation.handlers.DatabaseHandler; +import de.apps4ics.mountainnavigation.handlers.HikeHandler; import de.apps4ics.mountainnavigation.handlers.PoiHandler; import de.apps4ics.mountainnavigation.handlers.WeatherHandler; import de.apps4ics.mountainnavigation.pois.AddPoiDialog; @@ -88,7 +97,7 @@ private CharSequence mTitle; private CharSequence mDrawerTitle; private static final int GPS_MIN_TIME = 5000; - private static final int GPS_MIN_DIST = 5; + private static final int GPS_MIN_DIST = 10; private static final int MIN_WEATHER_RELOAD_TIME = 3600000; //1hour, in ms private static final int MIN_WEATHER_RELOAD_DIST = 5000; //5km, in m private static final int MIN_POI_RELOAD_TIME = 300000; //5min, in ms @@ -112,6 +121,7 @@ private DatabaseHandler dbHandler; private static PoiHandler poiHandler; private static WeatherHandler weatherHandler; + private static HikeHandler hikeHandler; private long lastWeatherInformation; private long lastPoiInformation; @@ -123,6 +133,7 @@ private ActionBarDrawerToggle drawerToggle; private DrawerLayout drawerLayout; private LinearLayout poiView; + private LinearLayout hikeView; private LinearLayout menuView; private ImageView weatherSymbol; private TextView weatherCity; @@ -134,6 +145,7 @@ public static ArrayList pathMarkers; public static OverlayItem imageMarker; + private Polyline hikeOverlay; private String provider; private static Location mLocation; @@ -172,16 +184,21 @@ mapController.setZoom(ZOOM_LEVEL); mapController.setCenter(new GeoPoint(48.52, 9.055)); + hikeOverlay = new Polyline(this); + hikeOverlay.setColor(res.getColor(R.color.polyline_color)); + mapView.getOverlays().add(hikeOverlay); + dbHandler = new DatabaseHandler(this); poiHandler = new PoiHandler(this, res, mapView); weatherHandler = new WeatherHandler(this); + hikeHandler = new HikeHandler(hikeOverlay); lastWeatherInformation = lastPoiInformation = 0; foundLocations = new ArrayList<>(); currentPosition = new Marker(mapView); - currentPosition.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM); + currentPosition.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER); currentPosition.setTitle(getString(R.string.osm_marker_title)); currentPosition.setIcon(res.getDrawable(R.drawable.ic_currency_96, null)); @@ -210,6 +227,7 @@ }); drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); menuView = (LinearLayout) findViewById(R.id.menuList); + hikeView = (LinearLayout) findViewById(R.id.hikeList); poiView = (LinearLayout) findViewById(R.id.poiList); weatherSymbol = (ImageView) findViewById(R.id.weather_symbol); weatherCity = (TextView) findViewById(R.id.weather_city); @@ -233,6 +251,101 @@ poiView.addView(row); } + final String[] hikeEntries = res.getStringArray(R.array.hikeEntries); + ArrayAdapter hikeAdapter = new ArrayAdapter<>(MainActivity.this, R.layout.drawer_list_simple, hikeEntries); + for(int i=0; i hikePoints = (ArrayList) hikeHandler.getPoints(); + if(hikePoints.size() < 2) return; + + LayoutInflater inflater = getLayoutInflater(); + View hikeDialog = inflater.inflate(R.layout.hike_dialog, null, true); + final EditText input = (EditText) hikeDialog.findViewById(R.id.hike_name); + TextView lengthView = (TextView) hikeDialog.findViewById(R.id.hike_length); + final TextView timeView = (TextView) hikeDialog.findViewById(R.id.hike_time); + + int lengthRest = hikeHandler.getTime(); + int hours = lengthRest / 3600; + lengthRest %= 3600; + int minutes = lengthRest / 60; + int seconds = lengthRest % 60; + + lengthView.setText(String.format(Locale.getDefault(), "Length so far: %.02fm", hikeHandler.getLength())); + timeView.setText(String.format(Locale.getDefault(), "Time so far: %02d:%02d:%02d", hours, minutes, seconds)); + + AlertDialog.Builder saveHikeBuilder = new AlertDialog.Builder(MainActivity.this); + saveHikeBuilder.setTitle("Save your hike") + .setView(hikeDialog) + .setPositiveButton("Save hike", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String name = input.getText().toString().trim(); + if(!name.equals("")) { + hikeHandler.stopRecording(); + view.setText(hikeEntries[position]); + } + } + }) + .setNegativeButton("Keep hiking", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + final AlertDialog saveHikeDialog = saveHikeBuilder.create(); + saveHikeDialog.show(); + final Button saveHikeDialogButton = saveHikeDialog.getButton(AlertDialog.BUTTON_POSITIVE); + saveHikeDialogButton.setEnabled(false); + + input.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if(s.length() > 0) saveHikeDialogButton.setEnabled(true); + else saveHikeDialogButton.setEnabled(false); + } + @Override + public void afterTextChanged(Editable s) { + } + }); + } else { + AlertDialog.Builder startHikeBuilder = new AlertDialog.Builder(MainActivity.this); + startHikeBuilder + .setTitle("Do you want to start a hike?") + .setMessage("This will record a new hike. From now on your locations will be recorded and you can save it afterwards.") + .setPositiveButton("Start hike", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + hikeHandler.startRecording(); + view.setText(res.getString(R.string.hike_title_stop_rec)); + Toaster(res.getString(R.string.hike_is_recording), true); + } + }) + .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + AlertDialog startHikeDialog = startHikeBuilder.create(); + startHikeDialog.show(); + } + } else if(key.equals(getString(R.string.hike_title_show_key))) { + //TODO Open dialog with all hikes around current position (or enter position) + } + } + }); + menuView.addView(view); + } + String[] menuEntries = res.getStringArray(R.array.menuEntries); ArrayAdapter menuAdapter = new ArrayAdapter<>(MainActivity.this, R.layout.drawer_list_simple, menuEntries); for(int i=0; i. + * + * @copyright Copyright (c) 2016 Vinzenz Rosenkanz + * + * @author Vinzenz Rosenkranz + */ + +package de.apps4ics.mountainnavigation.handlers; + +import android.location.Location; + +import org.osmdroid.bonuspack.overlays.Polyline; +import org.osmdroid.util.GeoPoint; + +import java.util.ArrayList; +import java.util.List; + +import de.apps4ics.mountainnavigation.HikePoint; + +public class HikeHandler { + private boolean isRecording; + private List timestamps; + private List geopoints; + private Polyline path; + private int time; + private float length; + private boolean needsUpdate; + + public HikeHandler(Polyline path) { + timestamps = new ArrayList<>(); + geopoints = new ArrayList<>(); + this.path = path; + needsUpdate = false; + } + + public boolean isRecording() { + return isRecording; + } + + public void startRecording() { + isRecording = true; + } + + public void stopRecording() { + isRecording = false; + updateProperties(); + path.setPoints(new ArrayList()); + } + + private void updateProperties() { + if(!needsUpdate) return; + needsUpdate = false; + if(timestamps.size() < 2) return; + time = (int) (timestamps.get(timestamps.size()-1) - timestamps.get(0)) / 1000; + GeoPoint first; + GeoPoint second = null; + float[] results = new float[3]; + for(GeoPoint p : geopoints) { + first = p; + if(second != null) { + Location.distanceBetween(first.getLatitude(), first.getLongitude(), second.getLatitude(), second.getLongitude(), results); + length += results[0]; + } + second = first; + } + } + + public void addLocation(Location location) { + if(!isRecording) return; + needsUpdate = true; + geopoints.add(new GeoPoint(location.getLatitude(), location.getLongitude(), location.getAltitude())); + timestamps.add(System.currentTimeMillis()); + path.setPoints(geopoints); + } + + public List getPoints() { + if(geopoints.size() != timestamps.size()) return null; + List points = new ArrayList<>(); + for(int i=0; i + + - - + - - - - - + android:layout_height="wrap_content" + android:id="@+id/weather_city" + android:gravity="center_vertical" + android:textSize="16sp" + android:textColor="@color/text_color" + android:fontFamily="sans-serif-medium" + android:text="@string/no_weather_data_available"/> + + + - - + + + + + + diff --git a/app/src/main/res/layout/hike_dialog.xml b/app/src/main/res/layout/hike_dialog.xml new file mode 100644 index 0000000..91d53bf --- /dev/null +++ b/app/src/main/res/layout/hike_dialog.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 07e705f..19d9d67 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -76,6 +76,8 @@ Dein Standort Latitude: %1$.03f<br/>Longitude: %2$.03f<br/>Höhe: %3$dm<br/>Hinzugefügt: %4$s Keine Wetterdaten verfügbar + Nehme auf… + Aufnahme stoppen de H:m @@ -130,6 +132,10 @@ Einstellungen Über + + Wanderung aufnehmen + Wanderungen anzeigen + Klein Mittel diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 376ea89..77296f0 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -24,4 +24,5 @@ #AA000000 #FF000000 #AFB9B9B9 + #AA0E84F1 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 96182bf..609637d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,6 +78,8 @@ Latitude: %1$.03f<br/>Longitude: %2$.03f<br/>Altitude: %3$d meters<br/>Added: %4$s %1$s, %2$s No weather data available + Recording… + Stop recording en h:m a @@ -119,6 +121,8 @@ about settings download + showHikes + recordHike hints dispHints maxHotTemp @@ -160,6 +164,14 @@ @string/menu_title_settings_key @string/menu_title_about_key + + Record hike + Show hikes + + + @string/hike_title_record_key + @string/hike_title_show_key + - OpenStreetMap: https://www.openstreetmap.org/ - OpenWeatherMap: https://openweathermap.org/