Trackbook now draws breadcrumbs onto map. Track and WayPoint objects are now parcelable.

master
y20k 2016-09-01 13:45:46 +02:00
parent 7b793fc9dd
commit c636886e90
11 changed files with 469 additions and 185 deletions

View File

@ -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.

View File

@ -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<String> 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);
}

View File

@ -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
}
}
};

View File

@ -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();
}
}
}
}

View File

@ -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<WayPoint> mWayPoints;
private float mTrackLength;
/* Constructor */
public Track(Context context) {
mContext = context;
public Track() {
mWayPoints = new ArrayList<WayPoint>();
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<Track> CREATOR = new Creator<Track>() {
@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);
}
}

View File

@ -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<WayPoint> CREATOR = new Creator<WayPoint>() {
@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);
}
}

View File

@ -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

View File

@ -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<OverlayItem> 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<OverlayItem>() {
@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<WayPoint> wayPoints = track.getWayPoints();
final ArrayList<OverlayItem> 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<OverlayItem>() {
@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);
}
}

View File

@ -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;

View File

@ -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"

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragment_main"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"