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")** **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. 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 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? Which Permissions does Trackbook need?
--------------------------------------- ---------------------------------------
### Permission "INTERNET" ### 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" ### Permission "ACCESS_NETWORK_STATE"
tbd tbd
@ -48,4 +48,4 @@ tbd
tbd tbd
### Permission "WRITE_EXTERNAL_STORAGE" ### 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 { public class MainActivity extends AppCompatActivity implements TrackbookKeys {
/* Define log tag */ /* Define log tag */
private static final String LOG_TAG = MainActivityFragment.class.getSimpleName(); private static final String LOG_TAG = MainActivity.class.getSimpleName();
/* Main class variables */ /* Main class variables */
@ -56,6 +56,7 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
private boolean mPermissionsGranted; private boolean mPermissionsGranted;
private List<String> mMissingPermissions; private List<String> mMissingPermissions;
private FloatingActionButton mFloatingActionButton; private FloatingActionButton mFloatingActionButton;
private MainActivityFragment mMainActivityFragment;
@Override @Override
@ -124,9 +125,11 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
// set state of FloatingActionButton // if not in onboarding mode: set state of FloatingActionButton
if (mFloatingActionButton != null) {
setFloatingActionButtonState(); setFloatingActionButtonState();
} }
}
@Override @Override
@ -172,6 +175,9 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
// get reference to fragment
mMainActivityFragment = (MainActivityFragment)getSupportFragmentManager().findFragmentById(R.id.content_main);
// show the record button and attach listener // show the record button and attach listener
mFloatingActionButton = (FloatingActionButton) findViewById(R.id.fab); mFloatingActionButton = (FloatingActionButton) findViewById(R.id.fab);
mFloatingActionButton.setOnClickListener(new View.OnClickListener() { 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); mFloatingActionButton.setImageResource(R.drawable.ic_fiber_manual_record_red_24dp);
mTracking = true; mTracking = true;
// TODO putParcelable lastLocation
// start tracker service // start tracker service
Intent intent = new Intent(this, TrackerService.class); Intent intent = new Intent(this, TrackerService.class);
intent.setAction(ACTION_START); intent.setAction(ACTION_START);
startService(intent); startService(intent);
LogHelper.v(LOG_TAG, "Starting tracker service."); 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.ItemizedIconOverlay;
import org.osmdroid.views.overlay.compass.CompassOverlay; import org.osmdroid.views.overlay.compass.CompassOverlay;
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider; import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider;
import org.y20k.trackbook.core.Track;
import org.y20k.trackbook.helpers.LocationHelper; import org.y20k.trackbook.helpers.LocationHelper;
import org.y20k.trackbook.helpers.LogHelper; import org.y20k.trackbook.helpers.LogHelper;
import org.y20k.trackbook.helpers.MapHelper; import org.y20k.trackbook.helpers.MapHelper;
@ -62,15 +63,18 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
/* Main class variables */ /* Main class variables */
private Activity mActivity; private Activity mActivity;
private Track mTrack;
private boolean mFirstStart; private boolean mFirstStart;
private BroadcastReceiver mWayPointReceiver; private BroadcastReceiver mTrackUpdatedReceiver;
private MapView mMapView; private MapView mMapView;
private IMapController mController; private IMapController mController;
private LocationManager mLocationManager; private LocationManager mLocationManager;
private LocationListener mGPSListener; private LocationListener mGPSListener;
private LocationListener mNetworkListener; private LocationListener mNetworkListener;
private ItemizedIconOverlay mMyLocationOverlay; private ItemizedIconOverlay mMyLocationOverlay;
private ItemizedIconOverlay mTrackOverlay;
private Location mCurrentBestLocation; private Location mCurrentBestLocation;
private boolean mTacking;
/* Constructor (default) */ /* Constructor (default) */
@ -94,10 +98,16 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
mFirstStart = savedInstanceState.getBoolean(INSTANCE_FIRST_START, true); 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 // register broadcast receiver for new WayPoints
mWayPointReceiver = createNewWayPointReceiver(); mTrackUpdatedReceiver = createTrackUpdatedReceiver();
IntentFilter newWayPointReceiverIntentFilter = new IntentFilter(ACTION_WAYPOINT_ADDED); IntentFilter trackUpdatedIntentFilter = new IntentFilter(ACTION_TRACK_UPDATED);
LocalBroadcastManager.getInstance(mActivity).registerReceiver(mWayPointReceiver, newWayPointReceiverIntentFilter); LocalBroadcastManager.getInstance(mActivity).registerReceiver(mTrackUpdatedReceiver, trackUpdatedIntentFilter);
// acquire reference to Location Manager // acquire reference to Location Manager
mLocationManager = (LocationManager) mActivity.getSystemService(Context.LOCATION_SERVICE); mLocationManager = (LocationManager) mActivity.getSystemService(Context.LOCATION_SERVICE);
@ -160,6 +170,14 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
mController.setZoom(16); mController.setZoom(16);
} }
// restore track
if (savedInstanceState != null) {
mTrack = savedInstanceState.getParcelable(INSTANCE_TRACK);
}
if (mTrack != null) {
drawTrackOverlay(mTrack);
}
// add compass to map // add compass to map
CompassOverlay compassOverlay = new CompassOverlay(mActivity, new InternalCompassOrientationProvider(mActivity), mMapView); CompassOverlay compassOverlay = new CompassOverlay(mActivity, new InternalCompassOrientationProvider(mActivity), mMapView);
compassOverlay.enableCompass(); compassOverlay.enableCompass();
@ -167,7 +185,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
// mark user's location on map // mark user's location on map
if (mCurrentBestLocation != null) { if (mCurrentBestLocation != null) {
mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation)); mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation), mTacking);
mMapView.getOverlays().add(mMyLocationOverlay); mMapView.getOverlays().add(mMyLocationOverlay);
} }
@ -188,8 +206,8 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
// disable location listener // disable location listeners
stopFindingLocation(); LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener);
} }
@ -198,7 +216,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
super.onDestroyView(); super.onDestroyView();
// unregister broadcast receiver // unregister broadcast receiver
LocalBroadcastManager.getInstance(mActivity).unregisterReceiver(mWayPointReceiver); LocalBroadcastManager.getInstance(mActivity).unregisterReceiver(mTrackUpdatedReceiver);
// deactivate map // deactivate map
mMapView.onDetach(); mMapView.onDetach();
@ -246,9 +264,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
mController.setCenter(position); mController.setCenter(position);
// mark user's new location on map and remove last marker // mark user's new location on map and remove last marker
mMapView.getOverlays().remove(mMyLocationOverlay); updateMyLocationMarker();
mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation));
mMapView.getOverlays().add(mMyLocationOverlay);
return true; return true;
@ -267,64 +283,33 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
outState.putDouble(INSTANCE_LONGITUDE, mMapView.getMapCenter().getLongitude()); outState.putDouble(INSTANCE_LONGITUDE, mMapView.getMapCenter().getLongitude());
outState.putInt(INSTANCE_ZOOM_LEVEL, mMapView.getZoomLevel()); outState.putInt(INSTANCE_ZOOM_LEVEL, mMapView.getZoomLevel());
outState.putParcelable(INSTANCE_CURRENT_LOCATION, mCurrentBestLocation); outState.putParcelable(INSTANCE_CURRENT_LOCATION, mCurrentBestLocation);
outState.putParcelable(INSTANCE_TRACK, mTrack);
outState.putBoolean(INSTANCE_TRACKING_STARTED, mTacking);
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
/* Sets tracking state */
public void setTrackingState (boolean trackingStarted) {
mTacking = trackingStarted;
updateMyLocationMarker();
}
/* Start finding location for map */ /* Start finding location for map */
private void startFindingLocation() { private void startFindingLocation() {
// create location listeners
// listener that responds to location updates List locationProviders = mLocationManager.getProviders(true);
if (locationProviders.contains(LocationManager.GPS_PROVIDER)) {
mGPSListener = createLocationListener(); mGPSListener = createLocationListener();
}
if (locationProviders.contains(LocationManager.NETWORK_PROVIDER)) {
mNetworkListener = 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 // register listeners
List locationProviders = mLocationManager.getProviders(true); LocationHelper.registerLocationListeners(mLocationManager, mGPSListener, mNetworkListener);
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();
}
}
}
/* 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();
}
}
} }
@ -338,9 +323,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
mCurrentBestLocation = location; mCurrentBestLocation = location;
LogHelper.v(LOG_TAG, "Location isBetterLocation(!): " + location.getProvider()); // TODO remove LogHelper.v(LOG_TAG, "Location isBetterLocation(!): " + location.getProvider()); // TODO remove
// mark user's new location on map and remove last marker // mark user's new location on map and remove last marker
mMapView.getOverlays().remove(mMyLocationOverlay); updateMyLocationMarker();
mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation));
mMapView.getOverlays().add(mMyLocationOverlay);
} }
} }
@ -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 */ /* Prompts user to turn on location */
private void promptUserForLocation() { private void promptUserForLocation() {
// TODO prompt user to turn on location // TODO prompt user to turn on location
@ -367,13 +366,14 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
/* Creates receiver for new WayPoints */ /* Creates receiver for new WayPoints */
private BroadcastReceiver createNewWayPointReceiver () { private BroadcastReceiver createTrackUpdatedReceiver() {
return new BroadcastReceiver() { return new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (intent.hasExtra(EXTRA_WAYPOINT_LOCATION) && intent.hasExtra(EXTRA_WAYPOINT_IS_STOPOVER)) { if (intent.hasExtra(EXTRA_TRACK)) {
// TODO draw location on map mTrack = intent.getParcelableExtra(EXTRA_TRACK);
Toast.makeText(mActivity, "New WayPoint: " + intent.getParcelableExtra(EXTRA_WAYPOINT_LOCATION).toString(), Toast.LENGTH_LONG).show(); // TODO Remove 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.CountDownTimer;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.Nullable; 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.Track;
import org.y20k.trackbook.core.WayPoint;
import org.y20k.trackbook.helpers.LocationHelper; import org.y20k.trackbook.helpers.LocationHelper;
import org.y20k.trackbook.helpers.LogHelper;
import org.y20k.trackbook.helpers.TrackbookKeys; import org.y20k.trackbook.helpers.TrackbookKeys;
import java.util.List;
/** /**
* TrackerService class * TrackerService class
@ -46,8 +50,9 @@ public class TrackerService extends Service implements TrackbookKeys {
private Track mTrack; private Track mTrack;
private CountDownTimer mTimer; private CountDownTimer mTimer;
private LocationManager mLocationManager; private LocationManager mLocationManager;
private LocationListener mGPSListener; private LocationListener mGPSListener = null;
private LocationListener mNetworkListener; private LocationListener mNetworkListener = null;
private Location mCurrentBestLocation;
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
@ -57,25 +62,26 @@ public class TrackerService extends Service implements TrackbookKeys {
// checking for empty intent // checking for empty intent
if (intent == null) { if (intent == null) {
Log.v(LOG_TAG, "Null-Intent received. Stopping self."); LogHelper.v(LOG_TAG, "Null-Intent received. Stopping self.");
stopSelf(); stopSelf();
} }
// ACTION START // ACTION START
else if (intent.getAction().equals(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 // create a new track
mTrack = new Track(getApplicationContext()); mTrack = new Track();
// create gps and network location listeners // add first location to track
createListeners(); 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) { mTimer = new CountDownTimer(CONSTANT_MAXIMAL_DURATION, CONSTANT_TRACKING_INTERVAL) {
@Override @Override
public void onTick(long l) { public void onTick(long l) {
// TODO addWayPointToTrack();
} }
@Override @Override
@ -83,15 +89,22 @@ public class TrackerService extends Service implements TrackbookKeys {
// TODO // TODO
} }
}; };
mTimer.start();
// create gps and network location listeners
startFindingLocation();
} }
// ACTION STOP // ACTION STOP
else if (intent.getAction().equals(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 // remove listeners
removeListeners(); LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener);
} }
// START_STICKY is used for services that are explicitly started and stopped as needed // 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 @Override
public void onDestroy() { public void onDestroy() {
Log.v(LOG_TAG, "onDestroy called."); LogHelper.v(LOG_TAG, "onDestroy called.");
// remove listeners // remove listeners
removeListeners(); LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener);
// cancel notification // cancel notification
stopForeground(true); 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 */ /* Creates a location listener */
private LocationListener createLocationListener() { private LocationListener createLocationListener() {
return new LocationListener() { return new LocationListener() {
public void onLocationChanged(Location location) { public void onLocationChanged(Location location) {
// check if the new location is better
// get number of tracked WayPoints if (LocationHelper.isBetterLocation(location, mCurrentBestLocation)) {
int trackSize = mTrack.getWayPoints().size(); // save location
mCurrentBestLocation = location;
if (trackSize >= 2) { // TODO hand over mCurrentBestLocation to fragment
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);
} }
} }
@ -156,53 +195,18 @@ public class TrackerService extends Service implements TrackbookKeys {
/* Creates gps and network location listeners */ /* Creates gps and network location listeners */
private void createListeners() { private void startFindingLocation() {
Log.v(LOG_TAG, "Setting up location listeners."); LogHelper.v(LOG_TAG, "Setting up location listeners.");
// listeners that responds to location updates
mGPSListener = createLocationListener();
mNetworkListener = createLocationListener();
// register location listeners and request updates // register location listeners and request updates
try { List locationProviders = mLocationManager.getProviders(true);
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mGPSListener); if (locationProviders.contains(LocationManager.GPS_PROVIDER)) {
} catch (SecurityException e) { mGPSListener = createLocationListener();
e.printStackTrace(); } else if (locationProviders.contains(LocationManager.NETWORK_PROVIDER)) {
} mNetworkListener = createLocationListener();
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mNetworkListener);
} catch (SecurityException e) {
e.printStackTrace();
} }
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; package org.y20k.trackbook.core;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.location.Location; 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.LocationHelper;
import org.y20k.trackbook.helpers.LogHelper; import org.y20k.trackbook.helpers.LogHelper;
@ -32,7 +32,7 @@ import java.util.List;
/** /**
* Track class * Track class
*/ */
public class Track implements TrackbookKeys { public class Track implements TrackbookKeys, Parcelable {
/* Define log tag */ /* Define log tag */
private static final String LOG_TAG = Track.class.getSimpleName(); private static final String LOG_TAG = Track.class.getSimpleName();
@ -41,33 +41,58 @@ public class Track implements TrackbookKeys {
/* Main class variables */ /* Main class variables */
private Context mContext; private Context mContext;
private List<WayPoint> mWayPoints; private List<WayPoint> mWayPoints;
private float mTrackLength;
/* Constructor */ /* Constructor */
public Track(Context context) { public Track() {
mContext = context;
mWayPoints = new ArrayList<WayPoint>(); 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 */ /* Adds new waypoint */
public void addWayPoint(Location location) { public WayPoint addWayPoint(Location location) {
// add up distance
mTrackLength = addDistanceToTrack(location);
// create new waypoint // create new waypoint
WayPoint wayPoint = new WayPoint(); WayPoint wayPoint = new WayPoint(location, LocationHelper.isStopOver(location), mTrackLength);
wayPoint.location = location;
wayPoint.isStopOver = LocationHelper.isStopOver(location);
// add new waypoint to track // add new waypoint to track
mWayPoints.add(wayPoint); mWayPoints.add(wayPoint);
// send local broadcast: new WayPoint added // TODO remove log here
Intent i = new Intent(); LogHelper.v(LOG_TAG, "Waypoint No. " + mWayPoints.indexOf(wayPoint) + " Location: " + wayPoint.getLocation().toString());
i.setAction(ACTION_WAYPOINT_ADDED);
i.putExtra(EXTRA_WAYPOINT_LOCATION, location);
i.putExtra(EXTRA_WAYPOINT_IS_STOPOVER, wayPoint.isStopOver);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(i);
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 */ /* Getter for location of specific WayPoint */
public Location getWayPointLocation(int index) { public Location getWayPointLocation(int index) {
return mWayPoints.get(index).location; return mWayPoints.get(index).getLocation();
} }
/** /* Adds distance to given location to length of track */
* Inner class: Defines data type WayPoint private float addDistanceToTrack(Location location) {
*/ // get number of previously recorded waypoints
private class WayPoint { int wayPointCount = mWayPoints.size();
// 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;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeTypedList(mWayPoints);
parcel.writeFloat(mTrackLength);
}
private Location location;
private boolean isStopOver;
}
/**
* End of inner class
*/
} }

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; package org.y20k.trackbook.helpers;
import android.location.Location; import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager; import android.location.LocationManager;
import android.os.SystemClock; import android.os.SystemClock;
@ -147,7 +148,7 @@ public final class LocationHelper {
long timeDifference = newLocation.getElapsedRealtimeNanos() - lastWayPoint.getElapsedRealtimeNanos(); long timeDifference = newLocation.getElapsedRealtimeNanos() - lastWayPoint.getElapsedRealtimeNanos();
// distance is bigger than 10 meters and time difference bigger than 20 seconds // 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 */ /* Checks whether two location providers are the same */
private static boolean isSameProvider(String provider1, String provider2) { 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 // 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.graphics.drawable.Drawable;
import android.location.Location; import android.location.Location;
import android.support.v7.widget.AppCompatDrawableManager; import android.support.v7.widget.AppCompatDrawableManager;
import android.text.format.DateFormat;
import org.osmdroid.util.GeoPoint; import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.overlay.ItemizedIconOverlay; import org.osmdroid.views.overlay.ItemizedIconOverlay;
import org.osmdroid.views.overlay.OverlayItem; import org.osmdroid.views.overlay.OverlayItem;
import org.y20k.trackbook.R; import org.y20k.trackbook.R;
import org.y20k.trackbook.core.Track;
import org.y20k.trackbook.core.WayPoint;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
/** /**
@ -39,25 +43,29 @@ public final class MapHelper {
/* Creates icon overlay for current position */ /* 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<>(); final ArrayList<OverlayItem> overlayItems = new ArrayList<>();
LogHelper.v(LOG_TAG, "Location is new? " + locationIsNew + " Provider: " + currentBestLocation.getProvider()); // TODO remove LogHelper.v(LOG_TAG, "Location is new? " + locationIsNew + " Provider: " + currentBestLocation.getProvider()); // TODO remove
// create marker // create marker
Drawable newMarker; 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); newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_dot_blue_24dp);
} else { } else {
newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_dot_grey_24dp); 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 overlayItem = new OverlayItem(context.getString(R.string.marker_my_location_title), context.getString(R.string.marker_my_location_description), position);
overlayItem.setMarker(newMarker); overlayItem.setMarker(newMarker);
// add marker to list of overlay items
overlayItems.add(overlayItem); overlayItems.add(overlayItem);
// create overlay // create and return overlay for current position
ItemizedIconOverlay myLocationOverlay = new ItemizedIconOverlay<>(overlayItems, return new ItemizedIconOverlay<>(overlayItems,
new ItemizedIconOverlay.OnItemGestureListener<OverlayItem>() { new ItemizedIconOverlay.OnItemGestureListener<OverlayItem>() {
@Override @Override
public boolean onItemSingleTapUp(final int index, final OverlayItem item) { public boolean onItemSingleTapUp(final int index, final OverlayItem item) {
@ -71,9 +79,52 @@ public final class MapHelper {
return true; return true;
} }
}, context); }, context);
}
/* 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 overlay for current position
return myLocationOverlay; 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 */ /* ACTIONS */
public static final String ACTION_START = "org.y20k.transistor.action.START"; 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_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 */ /* EXTRAS */
public static final String EXTRA_WAYPOINT_LOCATION = "WAYPOINT_LOCATION"; public static final String EXTRA_TRACK = "TRACK";
public static final String EXTRA_WAYPOINT_IS_STOPOVER = "WAYPOINT_IS_STOPOVER";
/* ARGS */ /* ARGS */
public static final String ARG_PERMISSIONS_GRANTED = "ArgPermissionsGranted"; 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_ZOOM_LEVEL = "zoomLevel";
public static final String INSTANCE_CURRENT_LOCATION = "currentLocation"; public static final String INSTANCE_CURRENT_LOCATION = "currentLocation";
public static final String INSTANCE_TRACKING_STARTED = "trackingStarted"; public static final String INSTANCE_TRACKING_STARTED = "trackingStarted";
public static final String INSTANCE_TRACK = "track";
/* RESULTS */ /* RESULTS */
/* CONSTANTS */ /* CONSTANTS */
public static final int CONSTANT_MINIMAL_STOP_TIME = 300000; // equals 5 minutes 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_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 */ /* MISC */
public static final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124; 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:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragment" android:id="@+id/content_main"
android:name="org.y20k.trackbook.MainActivityFragment" android:name="org.y20k.trackbook.MainActivityFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

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