diff --git a/README.md b/README.md index 9359e7a..3ced308 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,13 @@ Trackbook - Movement recorder for Android **Version 0.1.x ("The Great Gig in the Sky")** -Trackbook is a bare bones app for recording you own movements. Trackbook great for hiking, vacation or workout. Once started it displays your movements on a map. You can save your recorded tracks and share them with friends. +Trackbook is a bare bones app for recording your movements. Trackbook is great for hiking, vacation or workout. Once started it displays your movements on a map. You can save your recorded tracks and share them with friends. Trackbook is free software. It is published under the [MIT open source license](https://opensource.org/licenses/MIT). Trackbook uses [osmdroid](https://github.com/osmdroid/osmdroid) to display the map. osmdroid is also free software. It is published under the [Apache License](https://github.com/osmdroid/osmdroid/blob/master/LICENSE). Want to help? Please check out the notes in [CONTRIBUTE.md](https://github.com/y20k/transistor/blob/master/CONTRIBUTE.md) first. Install Trackbook ------------------ -Do not install. Trackbook does not do anythimg useful right now. See the Install Canary below? Wait until it flies. +Do not install Trackbook. Trackbook does not do anythimg useful right now. See the Install Canary below? Wait until it flies. .---. @@ -33,7 +33,7 @@ tbd Which Permissions does Trackbook need? --------------------------------------- ### Permission "INTERNET" -tbd +Trackbook needs to download map data from Open Street Map servers and therefore needs access to the internet. ### Permission "ACCESS_NETWORK_STATE" tbd @@ -48,4 +48,4 @@ tbd tbd ### Permission "WRITE_EXTERNAL_STORAGE" -tbd +Trackbook uses [osmdroid](https://github.com/osmdroid/osmdroid), which caches map tiles on Android's external storage. You can find the map cache in the `osmdroid` folder on the top level of the user-facing file system. diff --git a/app/src/main/java/org/y20k/trackbook/MainActivity.java b/app/src/main/java/org/y20k/trackbook/MainActivity.java index ca8692f..9f41b12 100644 --- a/app/src/main/java/org/y20k/trackbook/MainActivity.java +++ b/app/src/main/java/org/y20k/trackbook/MainActivity.java @@ -48,7 +48,7 @@ import java.util.Map; public class MainActivity extends AppCompatActivity implements TrackbookKeys { /* Define log tag */ - private static final String LOG_TAG = MainActivityFragment.class.getSimpleName(); + private static final String LOG_TAG = MainActivity.class.getSimpleName(); /* Main class variables */ @@ -56,6 +56,7 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys { private boolean mPermissionsGranted; private List mMissingPermissions; private FloatingActionButton mFloatingActionButton; + private MainActivityFragment mMainActivityFragment; @Override @@ -124,8 +125,10 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys { protected void onResume() { super.onResume(); - // set state of FloatingActionButton - setFloatingActionButtonState(); + // if not in onboarding mode: set state of FloatingActionButton + if (mFloatingActionButton != null) { + setFloatingActionButtonState(); + } } @@ -172,6 +175,9 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys { Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); + // get reference to fragment + mMainActivityFragment = (MainActivityFragment)getSupportFragmentManager().findFragmentById(R.id.content_main); + // show the record button and attach listener mFloatingActionButton = (FloatingActionButton) findViewById(R.id.fab); mFloatingActionButton.setOnClickListener(new View.OnClickListener() { @@ -227,14 +233,15 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys { mFloatingActionButton.setImageResource(R.drawable.ic_fiber_manual_record_red_24dp); mTracking = true; - // TODO putParcelable lastLocation - // start tracker service Intent intent = new Intent(this, TrackerService.class); intent.setAction(ACTION_START); startService(intent); LogHelper.v(LOG_TAG, "Starting tracker service."); } + + // update tracking state in MainActivityFragment + mMainActivityFragment.setTrackingState(mTracking); } diff --git a/app/src/main/java/org/y20k/trackbook/MainActivityFragment.java b/app/src/main/java/org/y20k/trackbook/MainActivityFragment.java index b2d345c..86e890c 100644 --- a/app/src/main/java/org/y20k/trackbook/MainActivityFragment.java +++ b/app/src/main/java/org/y20k/trackbook/MainActivityFragment.java @@ -43,6 +43,7 @@ import org.osmdroid.views.MapView; import org.osmdroid.views.overlay.ItemizedIconOverlay; import org.osmdroid.views.overlay.compass.CompassOverlay; import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider; +import org.y20k.trackbook.core.Track; import org.y20k.trackbook.helpers.LocationHelper; import org.y20k.trackbook.helpers.LogHelper; import org.y20k.trackbook.helpers.MapHelper; @@ -62,15 +63,18 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { /* Main class variables */ private Activity mActivity; + private Track mTrack; private boolean mFirstStart; - private BroadcastReceiver mWayPointReceiver; + private BroadcastReceiver mTrackUpdatedReceiver; private MapView mMapView; private IMapController mController; private LocationManager mLocationManager; private LocationListener mGPSListener; private LocationListener mNetworkListener; private ItemizedIconOverlay mMyLocationOverlay; + private ItemizedIconOverlay mTrackOverlay; private Location mCurrentBestLocation; + private boolean mTacking; /* Constructor (default) */ @@ -94,10 +98,16 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { mFirstStart = savedInstanceState.getBoolean(INSTANCE_FIRST_START, true); } + // restore tracking state + mTacking = false; + if (savedInstanceState != null) { + mTacking = savedInstanceState.getBoolean(INSTANCE_TRACKING_STARTED, false); + } + // register broadcast receiver for new WayPoints - mWayPointReceiver = createNewWayPointReceiver(); - IntentFilter newWayPointReceiverIntentFilter = new IntentFilter(ACTION_WAYPOINT_ADDED); - LocalBroadcastManager.getInstance(mActivity).registerReceiver(mWayPointReceiver, newWayPointReceiverIntentFilter); + mTrackUpdatedReceiver = createTrackUpdatedReceiver(); + IntentFilter trackUpdatedIntentFilter = new IntentFilter(ACTION_TRACK_UPDATED); + LocalBroadcastManager.getInstance(mActivity).registerReceiver(mTrackUpdatedReceiver, trackUpdatedIntentFilter); // acquire reference to Location Manager mLocationManager = (LocationManager) mActivity.getSystemService(Context.LOCATION_SERVICE); @@ -160,6 +170,14 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { mController.setZoom(16); } + // restore track + if (savedInstanceState != null) { + mTrack = savedInstanceState.getParcelable(INSTANCE_TRACK); + } + if (mTrack != null) { + drawTrackOverlay(mTrack); + } + // add compass to map CompassOverlay compassOverlay = new CompassOverlay(mActivity, new InternalCompassOrientationProvider(mActivity), mMapView); compassOverlay.enableCompass(); @@ -167,7 +185,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { // mark user's location on map if (mCurrentBestLocation != null) { - mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation)); + mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation), mTacking); mMapView.getOverlays().add(mMyLocationOverlay); } @@ -188,8 +206,8 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { public void onPause() { super.onPause(); - // disable location listener - stopFindingLocation(); + // disable location listeners + LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener); } @@ -198,7 +216,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { super.onDestroyView(); // unregister broadcast receiver - LocalBroadcastManager.getInstance(mActivity).unregisterReceiver(mWayPointReceiver); + LocalBroadcastManager.getInstance(mActivity).unregisterReceiver(mTrackUpdatedReceiver); // deactivate map mMapView.onDetach(); @@ -246,9 +264,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { mController.setCenter(position); // mark user's new location on map and remove last marker - mMapView.getOverlays().remove(mMyLocationOverlay); - mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation)); - mMapView.getOverlays().add(mMyLocationOverlay); + updateMyLocationMarker(); return true; @@ -267,64 +283,33 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { outState.putDouble(INSTANCE_LONGITUDE, mMapView.getMapCenter().getLongitude()); outState.putInt(INSTANCE_ZOOM_LEVEL, mMapView.getZoomLevel()); outState.putParcelable(INSTANCE_CURRENT_LOCATION, mCurrentBestLocation); + outState.putParcelable(INSTANCE_TRACK, mTrack); + outState.putBoolean(INSTANCE_TRACKING_STARTED, mTacking); super.onSaveInstanceState(outState); } + /* Sets tracking state */ + public void setTrackingState (boolean trackingStarted) { + mTacking = trackingStarted; + updateMyLocationMarker(); + } + + /* Start finding location for map */ private void startFindingLocation() { - - // listener that responds to location updates - mGPSListener = createLocationListener(); - mNetworkListener = createLocationListener(); - - // inform user that Trackbook is getting location updates - if (mFirstStart) { - Toast.makeText(mActivity, mActivity.getString(R.string.toast_message_acquiring_location), Toast.LENGTH_LONG).show(); - mFirstStart = false; - } - - // start listener + // create location listeners List locationProviders = mLocationManager.getProviders(true); if (locationProviders.contains(LocationManager.GPS_PROVIDER)) { - try { - // enable location listener (gps) - mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mGPSListener); - } catch (SecurityException e) { - // catches permission problems - e.printStackTrace(); - } - } else if (locationProviders.contains(LocationManager.NETWORK_PROVIDER)) { - try { - // enable location listener (network) - mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, mNetworkListener); - } catch (SecurityException e) { - // catches permission problems - e.printStackTrace(); - } + mGPSListener = createLocationListener(); } - } - - /* Stops finding location for map */ - private void stopFindingLocation() { - // disable location listeners - List locationProviders = mLocationManager.getProviders(true); - if (locationProviders.contains(LocationManager.GPS_PROVIDER)) { - try { - mLocationManager.removeUpdates(mGPSListener); - } catch (SecurityException e) { - // catches permission problems - e.printStackTrace(); - } - } else if (locationProviders.contains(LocationManager.NETWORK_PROVIDER)) { - try { - mLocationManager.removeUpdates(mNetworkListener); - } catch (SecurityException e) { - // catches permission problems - e.printStackTrace(); - } + if (locationProviders.contains(LocationManager.NETWORK_PROVIDER)) { + mNetworkListener = createLocationListener(); } + + // register listeners + LocationHelper.registerLocationListeners(mLocationManager, mGPSListener, mNetworkListener); } @@ -338,9 +323,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { mCurrentBestLocation = location; LogHelper.v(LOG_TAG, "Location isBetterLocation(!): " + location.getProvider()); // TODO remove // mark user's new location on map and remove last marker - mMapView.getOverlays().remove(mMyLocationOverlay); - mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation)); - mMapView.getOverlays().add(mMyLocationOverlay); + updateMyLocationMarker(); } } @@ -359,6 +342,22 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { } + /* Updates marker for current user location */ + private void updateMyLocationMarker() { + mMapView.getOverlays().remove(mMyLocationOverlay); + mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation),mTacking); + mMapView.getOverlays().add(mMyLocationOverlay); + } + + + /* Draws track onto overlay */ + private void drawTrackOverlay(Track track) { + mMapView.getOverlays().remove(mTrackOverlay); + mTrackOverlay = MapHelper.createTrackOverlay(mActivity, track); + mMapView.getOverlays().add(mTrackOverlay); + } + + /* Prompts user to turn on location */ private void promptUserForLocation() { // TODO prompt user to turn on location @@ -367,13 +366,14 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys { /* Creates receiver for new WayPoints */ - private BroadcastReceiver createNewWayPointReceiver () { + private BroadcastReceiver createTrackUpdatedReceiver() { return new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (intent.hasExtra(EXTRA_WAYPOINT_LOCATION) && intent.hasExtra(EXTRA_WAYPOINT_IS_STOPOVER)) { - // TODO draw location on map - Toast.makeText(mActivity, "New WayPoint: " + intent.getParcelableExtra(EXTRA_WAYPOINT_LOCATION).toString(), Toast.LENGTH_LONG).show(); // TODO Remove + if (intent.hasExtra(EXTRA_TRACK)) { + mTrack = intent.getParcelableExtra(EXTRA_TRACK); + drawTrackOverlay(mTrack); + Toast.makeText(mActivity, "New WayPoint.", Toast.LENGTH_LONG).show(); // TODO Remove } } }; diff --git a/app/src/main/java/org/y20k/trackbook/TrackerService.java b/app/src/main/java/org/y20k/trackbook/TrackerService.java index 7cb06df..c3a6630 100644 --- a/app/src/main/java/org/y20k/trackbook/TrackerService.java +++ b/app/src/main/java/org/y20k/trackbook/TrackerService.java @@ -26,12 +26,16 @@ import android.os.Bundle; import android.os.CountDownTimer; import android.os.IBinder; import android.support.annotation.Nullable; -import android.util.Log; +import android.support.v4.content.LocalBroadcastManager; import org.y20k.trackbook.core.Track; +import org.y20k.trackbook.core.WayPoint; import org.y20k.trackbook.helpers.LocationHelper; +import org.y20k.trackbook.helpers.LogHelper; import org.y20k.trackbook.helpers.TrackbookKeys; +import java.util.List; + /** * TrackerService class @@ -46,8 +50,9 @@ public class TrackerService extends Service implements TrackbookKeys { private Track mTrack; private CountDownTimer mTimer; private LocationManager mLocationManager; - private LocationListener mGPSListener; - private LocationListener mNetworkListener; + private LocationListener mGPSListener = null; + private LocationListener mNetworkListener = null; + private Location mCurrentBestLocation; @Override public int onStartCommand(Intent intent, int flags, int startId) { @@ -57,25 +62,26 @@ public class TrackerService extends Service implements TrackbookKeys { // checking for empty intent if (intent == null) { - Log.v(LOG_TAG, "Null-Intent received. Stopping self."); + LogHelper.v(LOG_TAG, "Null-Intent received. Stopping self."); stopSelf(); } // ACTION START else if (intent.getAction().equals(ACTION_START)) { - Log.v(LOG_TAG, "Service received command: START"); + LogHelper.v(LOG_TAG, "Service received command: START"); // create a new track - mTrack = new Track(getApplicationContext()); + mTrack = new Track(); - // create gps and network location listeners - createListeners(); + // add first location to track + mCurrentBestLocation = LocationHelper.determineLastKnownLocation(mLocationManager); + addWayPointToTrack(); - // set a timer to prevent endless tracking + // set timer to retrieve new locations and to prevent endless tracking mTimer = new CountDownTimer(CONSTANT_MAXIMAL_DURATION, CONSTANT_TRACKING_INTERVAL) { @Override public void onTick(long l) { - // TODO + addWayPointToTrack(); } @Override @@ -83,15 +89,22 @@ public class TrackerService extends Service implements TrackbookKeys { // TODO } }; + mTimer.start(); + + // create gps and network location listeners + startFindingLocation(); } // ACTION STOP else if (intent.getAction().equals(ACTION_STOP)) { - Log.v(LOG_TAG, "Service received command: STOP"); + LogHelper.v(LOG_TAG, "Service received command: STOP"); + + // stop timer + mTimer.cancel(); // remove listeners - removeListeners(); + LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener); } // START_STICKY is used for services that are explicitly started and stopped as needed @@ -108,10 +121,10 @@ public class TrackerService extends Service implements TrackbookKeys { @Override public void onDestroy() { - Log.v(LOG_TAG, "onDestroy called."); + LogHelper.v(LOG_TAG, "onDestroy called."); // remove listeners - removeListeners(); + LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener); // cancel notification stopForeground(true); @@ -120,23 +133,49 @@ public class TrackerService extends Service implements TrackbookKeys { } + /* Adds a new WayPoint to current track */ + public void addWayPointToTrack() { + + // create new WayPoint + WayPoint newWayPoint = null; + + // get number of previously tracked WayPoints + int trackSize = mTrack.getWayPoints().size(); + + if (trackSize > 0) { + // get last waypoint and compare it to current location + Location lastWayPoint = mTrack.getWayPointLocation(trackSize-1); + if (LocationHelper.isNewWayPoint(lastWayPoint, mCurrentBestLocation)) { + LogHelper.v(LOG_TAG, "!!! Ding. " + mTrack.getSize()); + // if new, add current best location to track + newWayPoint = mTrack.addWayPoint(mCurrentBestLocation); + } + } else { + // add first location to track + newWayPoint = mTrack.addWayPoint(mCurrentBestLocation); + LogHelper.v(LOG_TAG, "!!! Dong. " + mTrack.getSize()); + } + + // send local broadcast if new WayPoint added + if (newWayPoint != null) { + Intent i = new Intent(); + i.setAction(ACTION_TRACK_UPDATED); + i.putExtra(EXTRA_TRACK, mTrack); + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(i); + } + + } + + /* Creates a location listener */ private LocationListener createLocationListener() { return new LocationListener() { public void onLocationChanged(Location location) { - - // get number of tracked WayPoints - int trackSize = mTrack.getWayPoints().size(); - - if (trackSize >= 2) { - Location lastWayPoint = mTrack.getWayPointLocation(trackSize-2); - if (LocationHelper.isNewWayPoint(lastWayPoint, location)) { - // add new location to track - mTrack.addWayPoint(location); - } - } else { - // add first location to track - mTrack.addWayPoint(location); + // check if the new location is better + if (LocationHelper.isBetterLocation(location, mCurrentBestLocation)) { + // save location + mCurrentBestLocation = location; + // TODO hand over mCurrentBestLocation to fragment } } @@ -156,53 +195,18 @@ public class TrackerService extends Service implements TrackbookKeys { /* Creates gps and network location listeners */ - private void createListeners() { - Log.v(LOG_TAG, "Setting up location listeners."); - - // listeners that responds to location updates - mGPSListener = createLocationListener(); - mNetworkListener = createLocationListener(); + private void startFindingLocation() { + LogHelper.v(LOG_TAG, "Setting up location listeners."); // register location listeners and request updates - try { - mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mGPSListener); - } catch (SecurityException e) { - e.printStackTrace(); - } - try { - mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mNetworkListener); - } catch (SecurityException e) { - e.printStackTrace(); + List locationProviders = mLocationManager.getProviders(true); + if (locationProviders.contains(LocationManager.GPS_PROVIDER)) { + mGPSListener = createLocationListener(); + } else if (locationProviders.contains(LocationManager.NETWORK_PROVIDER)) { + mNetworkListener = createLocationListener(); } + LocationHelper.registerLocationListeners(mLocationManager, mGPSListener, mNetworkListener); } - /* Removes gps and network location listeners */ - private void removeListeners() { - Log.v(LOG_TAG, "Removing location listeners."); - - // remove gps listener - if (mGPSListener != null) { - Log.v(LOG_TAG, "Removing GPS location listener."); - try { - mLocationManager.removeUpdates(mGPSListener); - } catch (SecurityException e) { - // catches permission problems - e.printStackTrace(); - } - } - - // remove network listener - if (mNetworkListener != null) { - Log.v(LOG_TAG, "Removing network location listener."); - try { - mLocationManager.removeUpdates(mNetworkListener); - } catch (SecurityException e) { - // catches permission problems - e.printStackTrace(); - } - } - - } - } diff --git a/app/src/main/java/org/y20k/trackbook/core/Track.java b/app/src/main/java/org/y20k/trackbook/core/Track.java index d38a93d..a5e7f12 100644 --- a/app/src/main/java/org/y20k/trackbook/core/Track.java +++ b/app/src/main/java/org/y20k/trackbook/core/Track.java @@ -17,9 +17,9 @@ package org.y20k.trackbook.core; import android.content.Context; -import android.content.Intent; import android.location.Location; -import android.support.v4.content.LocalBroadcastManager; +import android.os.Parcel; +import android.os.Parcelable; import org.y20k.trackbook.helpers.LocationHelper; import org.y20k.trackbook.helpers.LogHelper; @@ -32,7 +32,7 @@ import java.util.List; /** * Track class */ -public class Track implements TrackbookKeys { +public class Track implements TrackbookKeys, Parcelable { /* Define log tag */ private static final String LOG_TAG = Track.class.getSimpleName(); @@ -41,33 +41,58 @@ public class Track implements TrackbookKeys { /* Main class variables */ private Context mContext; private List mWayPoints; + private float mTrackLength; /* Constructor */ - public Track(Context context) { - mContext = context; + public Track() { mWayPoints = new ArrayList(); + mTrackLength = 0; + } + + + /* Constructor used by CREATOR */ + protected Track(Parcel in) { + mWayPoints = in.createTypedArrayList(WayPoint.CREATOR); + mTrackLength = in.readFloat(); + } + + + /* CREATOR for Track object used to do parcel related operations */ + public static final Creator CREATOR = new Creator() { + @Override + public Track createFromParcel(Parcel in) { + return new Track(in); + } + + @Override + public Track[] newArray(int size) { + return new Track[size]; + } + }; + + + /* Set mContext needed by */ + public void setContext(Context context) { + mContext = context; } /* Adds new waypoint */ - public void addWayPoint(Location location) { + public WayPoint addWayPoint(Location location) { + // add up distance + mTrackLength = addDistanceToTrack(location); + // create new waypoint - WayPoint wayPoint = new WayPoint(); - wayPoint.location = location; - wayPoint.isStopOver = LocationHelper.isStopOver(location); + WayPoint wayPoint = new WayPoint(location, LocationHelper.isStopOver(location), mTrackLength); // add new waypoint to track mWayPoints.add(wayPoint); - // send local broadcast: new WayPoint added - Intent i = new Intent(); - i.setAction(ACTION_WAYPOINT_ADDED); - i.putExtra(EXTRA_WAYPOINT_LOCATION, location); - i.putExtra(EXTRA_WAYPOINT_IS_STOPOVER, wayPoint.isStopOver); - LocalBroadcastManager.getInstance(mContext).sendBroadcast(i); + // TODO remove log here + LogHelper.v(LOG_TAG, "Waypoint No. " + mWayPoints.indexOf(wayPoint) + " Location: " + wayPoint.getLocation().toString()); - LogHelper.v(LOG_TAG, "!!! Waypoint No. " + mWayPoints.indexOf(wayPoint) + " Location: " + wayPoint.location.toString()); // TODO remove + return wayPoint; } @@ -77,23 +102,45 @@ public class Track implements TrackbookKeys { } + /* Getter size of Track / number of WayPoints */ + public int getSize() { + return mWayPoints.size(); + } + + /* Getter for location of specific WayPoint */ public Location getWayPointLocation(int index) { - return mWayPoints.get(index).location; + return mWayPoints.get(index).getLocation(); } - /** - * Inner class: Defines data type WayPoint - */ - private class WayPoint { + /* Adds distance to given location to length of track */ + private float addDistanceToTrack(Location location) { + // get number of previously recorded waypoints + int wayPointCount = mWayPoints.size(); - private Location location; - private boolean isStopOver; + // at least two data points are needed + if (wayPointCount >= 2) { + // add up distance + Location lastLocation = mWayPoints.get(wayPointCount-2).getLocation(); + mTrackLength = mTrackLength + lastLocation.distanceTo(location); + } + return mTrackLength; } - /** - * End of inner class - */ + + + @Override + public int describeContents() { + return 0; + } + + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeTypedList(mWayPoints); + parcel.writeFloat(mTrackLength); + } + } diff --git a/app/src/main/java/org/y20k/trackbook/core/WayPoint.java b/app/src/main/java/org/y20k/trackbook/core/WayPoint.java new file mode 100644 index 0000000..8f44712 --- /dev/null +++ b/app/src/main/java/org/y20k/trackbook/core/WayPoint.java @@ -0,0 +1,109 @@ +/** + * WayPoint.java + * Implements the WayPoint class + * A WayPoint stores a location plus additional metadata + * + * This file is part of + * TRACKBOOK - Movement Recorder for Android + * + * Copyright (c) 2016 - Y20K.org + * Licensed under the MIT-License + * http://opensource.org/licenses/MIT + * + * Trackbook uses osmdroid - OpenStreetMap-Tools for Android + * https://github.com/osmdroid/osmdroid + */ + +package org.y20k.trackbook.core; + +import android.location.Location; +import android.os.Parcel; +import android.os.Parcelable; + + +/** + * WayPoint class + */ +public class WayPoint implements Parcelable { + + private Location mLocation; + private boolean mIsStopOver; + private float mDistanceToStartingPoint; + + /* Constructor */ + public WayPoint(Location location, boolean isStopOver, float distanceToStartingPoint) { + mLocation = location; + mIsStopOver = isStopOver; + mDistanceToStartingPoint = distanceToStartingPoint; + } + + /* Constructor used by CREATOR */ + protected WayPoint(Parcel in) { + mLocation = in.readParcelable(Location.class.getClassLoader()); + mIsStopOver = in.readByte() != 0; + mDistanceToStartingPoint = in.readFloat(); + } + + + /* CREATOR for WayPoint object used to do parcel related operations */ + public static final Creator CREATOR = new Creator() { + @Override + public WayPoint createFromParcel(Parcel in) { + return new WayPoint(in); + } + + @Override + public WayPoint[] newArray(int size) { + return new WayPoint[size]; + } + }; + + + /* Getter for mIsStopOver */ + public Location getLocation() { + return mLocation; + } + + + /* Getter for mIsStopOver */ + public boolean getIsStopOver() { + return mIsStopOver; + } + + + /* Getter for mDistanceToStartingPoint */ + public float getDistanceToStartingPoint() { + return mDistanceToStartingPoint; + } + + + /* Setter for mLocation */ + public void setLocation(Location location) { + mLocation = location; + } + + + /* Setter for mIsStopOver */ + public void setIsStopOver(boolean isStopOver) { + mIsStopOver = isStopOver; + } + + + /* Setter for mDistanceToStartingPoint */ + public void setDistanceToStartingPoint(float distanceToStartingPoint) { + mDistanceToStartingPoint = distanceToStartingPoint; + } + + @Override + public int describeContents() { + return 0; + } + + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeParcelable(mLocation, i); + parcel.writeByte((byte) (mIsStopOver ? 1 : 0)); + parcel.writeFloat(mDistanceToStartingPoint); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/y20k/trackbook/helpers/LocationHelper.java b/app/src/main/java/org/y20k/trackbook/helpers/LocationHelper.java index c5430bd..d5381cf 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/LocationHelper.java +++ b/app/src/main/java/org/y20k/trackbook/helpers/LocationHelper.java @@ -17,6 +17,7 @@ package org.y20k.trackbook.helpers; import android.location.Location; +import android.location.LocationListener; import android.location.LocationManager; import android.os.SystemClock; @@ -147,7 +148,7 @@ public final class LocationHelper { long timeDifference = newLocation.getElapsedRealtimeNanos() - lastWayPoint.getElapsedRealtimeNanos(); // distance is bigger than 10 meters and time difference bigger than 20 seconds - return distance > 10 && timeDifference > TWENTY_SECONDS; + return distance > 10 && timeDifference >= TWENTY_SECONDS; } @@ -158,6 +159,69 @@ public final class LocationHelper { } + /* Registers gps and network location listeners */ + public static void registerLocationListeners(LocationManager locationManager, LocationListener gpsListener, LocationListener networkListener) { + LogHelper.v(LOG_TAG, "Registering location listeners."); + + // get location providers + List locationProviders = locationManager.getProviders(true); + + // got GPS location provider? + if (locationProviders.contains(LocationManager.GPS_PROVIDER) && gpsListener != null) { + try { + // register GPS location listener and request updates + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, gpsListener); + } catch (SecurityException e) { + // catches permission problems + e.printStackTrace(); + } + } + + // got network location provider? + if (locationProviders.contains(LocationManager.NETWORK_PROVIDER) && networkListener != null) { + try { + // register network location listener and request updates + locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, networkListener); + } catch (SecurityException e) { + // catches permission problems + e.printStackTrace(); + } + } + } + + + /* Removes gps and network location listeners */ + public static void removeLocationListeners(LocationManager locationManager, LocationListener gpsListener, LocationListener networkListener) { + LogHelper.v(LOG_TAG, "Removing location listeners."); + + // get location providers + List locationProviders = locationManager.getProviders(true); + + // got GPS location provider? + if (locationProviders.contains(LocationManager.GPS_PROVIDER) && gpsListener != null) { + try { + // remove GPS listener + locationManager.removeUpdates(gpsListener); + } catch (SecurityException e) { + // catches permission problems + e.printStackTrace(); + } + } + + // got network location provider? + if (locationProviders.contains(LocationManager.NETWORK_PROVIDER) && networkListener != null) { + try { + // remove network listener + locationManager.removeUpdates(networkListener); + } catch (SecurityException e) { + // catches permission problems + e.printStackTrace(); + } + } + + } + + /* Checks whether two location providers are the same */ private static boolean isSameProvider(String provider1, String provider2) { // credit: the isSameProvider method was sample code from: https://developer.android.com/guide/topics/location/strategies.html diff --git a/app/src/main/java/org/y20k/trackbook/helpers/MapHelper.java b/app/src/main/java/org/y20k/trackbook/helpers/MapHelper.java index ff98d58..68c6e13 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/MapHelper.java +++ b/app/src/main/java/org/y20k/trackbook/helpers/MapHelper.java @@ -20,13 +20,17 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.location.Location; import android.support.v7.widget.AppCompatDrawableManager; +import android.text.format.DateFormat; import org.osmdroid.util.GeoPoint; import org.osmdroid.views.overlay.ItemizedIconOverlay; import org.osmdroid.views.overlay.OverlayItem; import org.y20k.trackbook.R; +import org.y20k.trackbook.core.Track; +import org.y20k.trackbook.core.WayPoint; import java.util.ArrayList; +import java.util.List; /** @@ -39,25 +43,29 @@ public final class MapHelper { /* Creates icon overlay for current position */ - public static ItemizedIconOverlay createMyLocationOverlay(Context context, Location currentBestLocation, boolean locationIsNew) { + public static ItemizedIconOverlay createMyLocationOverlay(Context context, Location currentBestLocation, boolean locationIsNew, boolean trackingStarted) { - final GeoPoint position = new GeoPoint(currentBestLocation.getLatitude(), currentBestLocation.getLongitude()); final ArrayList overlayItems = new ArrayList<>(); LogHelper.v(LOG_TAG, "Location is new? " + locationIsNew + " Provider: " + currentBestLocation.getProvider()); // TODO remove // create marker Drawable newMarker; - if (locationIsNew) { + if (trackingStarted) { + newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_dot_red_24dp); + } else if (locationIsNew) { newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_dot_blue_24dp); } else { newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_dot_grey_24dp); } + final GeoPoint position = new GeoPoint(currentBestLocation.getLatitude(), currentBestLocation.getLongitude()); OverlayItem overlayItem = new OverlayItem(context.getString(R.string.marker_my_location_title), context.getString(R.string.marker_my_location_description), position); overlayItem.setMarker(newMarker); + + // add marker to list of overlay items overlayItems.add(overlayItem); - // create overlay - ItemizedIconOverlay myLocationOverlay = new ItemizedIconOverlay<>(overlayItems, + // create and return overlay for current position + return new ItemizedIconOverlay<>(overlayItems, new ItemizedIconOverlay.OnItemGestureListener() { @Override public boolean onItemSingleTapUp(final int index, final OverlayItem item) { @@ -71,9 +79,52 @@ public final class MapHelper { return true; } }, context); - - // return overlay for current position - return myLocationOverlay; } + + /* Creates icon overlay for track */ + public static ItemizedIconOverlay createTrackOverlay(Context context, Track track){ + + final List wayPoints = track.getWayPoints(); + final ArrayList overlayItems = new ArrayList<>(); + + for (WayPoint wayPoint : wayPoints) { + // create marker + Drawable newMarker; + if (wayPoint.getIsStopOver()) { + newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_crumb_blue_24dp); + } else { + newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_crumb_blue_24dp); + } + final String title = Float.toString(wayPoint.getDistanceToStartingPoint()); + final String description = DateFormat.getDateFormat(context).format(wayPoint.getLocation().getTime()); + final GeoPoint position = new GeoPoint(wayPoint.getLocation().getLatitude(), wayPoint.getLocation().getLongitude()); + OverlayItem overlayItem = new OverlayItem(title, description, position); + overlayItem.setMarker(newMarker); + + // add marker to list of overlay items + overlayItems.add(overlayItem); + } + + // return overlay for current position + return new ItemizedIconOverlay<>(overlayItems, + new ItemizedIconOverlay.OnItemGestureListener() { + @Override + public boolean onItemSingleTapUp(final int index, final OverlayItem item) { + LogHelper.v(LOG_TAG, "Tap on a track crumb icon detected. Measured distance: " + item.getTitle()); + return true; + } + + @Override + public boolean onItemLongPress(final int index, final OverlayItem item) { + LogHelper.v(LOG_TAG, "Long press on a track crumb icon detected. Timestamp: " + item.getSnippet()); + return true; + } + + }, context); + } + + + + } \ No newline at end of file diff --git a/app/src/main/java/org/y20k/trackbook/helpers/TrackbookKeys.java b/app/src/main/java/org/y20k/trackbook/helpers/TrackbookKeys.java index c09aea3..c989b3c 100644 --- a/app/src/main/java/org/y20k/trackbook/helpers/TrackbookKeys.java +++ b/app/src/main/java/org/y20k/trackbook/helpers/TrackbookKeys.java @@ -25,11 +25,10 @@ public interface TrackbookKeys { /* ACTIONS */ public static final String ACTION_START = "org.y20k.transistor.action.START"; public static final String ACTION_STOP = "org.y20k.transistor.action.STOP"; - public static final String ACTION_WAYPOINT_ADDED = "WAYPOINT_ADDED"; + public static final String ACTION_TRACK_UPDATED = "TRACK_UPDATED"; /* EXTRAS */ - public static final String EXTRA_WAYPOINT_LOCATION = "WAYPOINT_LOCATION"; - public static final String EXTRA_WAYPOINT_IS_STOPOVER = "WAYPOINT_IS_STOPOVER"; + public static final String EXTRA_TRACK = "TRACK"; /* ARGS */ public static final String ARG_PERMISSIONS_GRANTED = "ArgPermissionsGranted"; @@ -50,13 +49,14 @@ public interface TrackbookKeys { public static final String INSTANCE_ZOOM_LEVEL = "zoomLevel"; public static final String INSTANCE_CURRENT_LOCATION = "currentLocation"; public static final String INSTANCE_TRACKING_STARTED = "trackingStarted"; + public static final String INSTANCE_TRACK = "track"; /* RESULTS */ /* CONSTANTS */ public static final int CONSTANT_MINIMAL_STOP_TIME = 300000; // equals 5 minutes public static final long CONSTANT_MAXIMAL_DURATION = 43200000; // equals 8 hours - public static final long CONSTANT_TRACKING_INTERVAL = 5000; // equals 5 seconds + public static final long CONSTANT_TRACKING_INTERVAL = 15000; // equals 15 seconds /* MISC */ public static final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124; diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index d1ca9ed..9ec3f79 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/fragment" + android:id="@+id/content_main" android:name="org.y20k.trackbook.MainActivityFragment" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 7f4ec50..29bf875 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -1,6 +1,8 @@ -