Trackbook got a notification. Double tracking while showing the map removed. While recording the track is now red. Bug fixes.

master
y20k 2016-09-06 17:27:04 +02:00
parent f83c651970
commit 3e543444d5
17 changed files with 538 additions and 116 deletions

View File

@ -18,13 +18,18 @@ package org.y20k.trackbook;
import android.Manifest; import android.Manifest;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.Menu; import android.view.Menu;
@ -55,8 +60,10 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
private boolean mTracking; private boolean mTracking;
private boolean mPermissionsGranted; private boolean mPermissionsGranted;
private List<String> mMissingPermissions; private List<String> mMissingPermissions;
private View mRootView;
private FloatingActionButton mFloatingActionButton; private FloatingActionButton mFloatingActionButton;
private MainActivityFragment mMainActivityFragment; private MainActivityFragment mMainActivityFragment;
private BroadcastReceiver mTrackingStoppedReceiver;
@Override @Override
@ -66,7 +73,7 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
// set state of tracking // set state of tracking
mTracking = false; mTracking = false;
if (savedInstanceState != null) { if (savedInstanceState != null) {
mTracking = savedInstanceState.getBoolean(INSTANCE_TRACKING_STARTED, false); mTracking = savedInstanceState.getBoolean(INSTANCE_TRACKING_STATE, false);
} }
// check permissions on Android 6 and higher // check permissions on Android 6 and higher
@ -85,6 +92,11 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
// set up main layout // set up main layout
setupLayout(); setupLayout();
// register broadcast receiver for stopped tracking
mTrackingStoppedReceiver = createTrackingStoppedReceiver();
IntentFilter trackingStoppedIntentFilter = new IntentFilter(ACTION_TRACKING_STOPPED);
LocalBroadcastManager.getInstance(this).registerReceiver(mTrackingStoppedReceiver, trackingStoppedIntentFilter);
} }
@ -102,9 +114,9 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
// handle action bar options menu selection // handle action bar options menu selection
switch (item.getItemId()) { switch (item.getItemId()) {
// CASE SETTINGS // CASE ABOUT
case R.id.action_settings: case R.id.action_bar_about:
LogHelper.v(LOG_TAG, "Settings was selected"); LogHelper.v(LOG_TAG, "About was selected"); // TODO remove
return true; return true;
// CASE DEFAULT // CASE DEFAULT
@ -116,7 +128,7 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean(INSTANCE_TRACKING_STARTED, mTracking); outState.putBoolean(INSTANCE_TRACKING_STATE, mTracking);
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
@ -132,6 +144,15 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
} }
@Override
public void onDestroy() {
super.onDestroy();
// disable broadcast receiver
LocalBroadcastManager.getInstance(this).unregisterReceiver(mTrackingStoppedReceiver);
}
@Override @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) { switch (requestCode) {
@ -213,31 +234,33 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
/* Handles tap on the record button */ /* Handles tap on the record button */
private void handleFloatingActionButtonClick(View view) { private void handleFloatingActionButtonClick(View view) {
if (mTracking) { if (mTracking) {
// show snackbar
Snackbar.make(view, R.string.snackbar_message_tracking_stopped, Snackbar.LENGTH_SHORT).setAction("Action", null).show(); Snackbar.make(view, R.string.snackbar_message_tracking_stopped, Snackbar.LENGTH_SHORT).setAction("Action", null).show();
// change state // change state
mFloatingActionButton.setImageResource(R.drawable.ic_fiber_manual_record_white_24dp); // --> is handled by broadcast receiver
mTracking = false;
// stop tracker service // stop tracker service
Intent intent = new Intent(this, TrackerService.class); Intent intent = new Intent(this, TrackerService.class);
intent.setAction(ACTION_STOP); intent.setAction(ACTION_STOP);
startService(intent); startService(intent);
LogHelper.v(LOG_TAG, "Stopping tracker service.");
} else { } else {
// show snackbar
Snackbar.make(view, R.string.snackbar_message_tracking_started, Snackbar.LENGTH_SHORT).setAction("Action", null).show(); Snackbar.make(view, R.string.snackbar_message_tracking_started, Snackbar.LENGTH_SHORT).setAction("Action", null).show();
// change state // change state
mFloatingActionButton.setImageResource(R.drawable.ic_fiber_manual_record_red_24dp);
mTracking = true; mTracking = true;
setFloatingActionButtonState();
// get last location from MainActivity Fragment
Location lastLocation = mMainActivityFragment.getCurrentBestLocation();
// 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);
intent.putExtra(EXTRA_LAST_LOCATION, lastLocation);
startService(intent); startService(intent);
LogHelper.v(LOG_TAG, "Starting tracker service.");
} }
// update tracking state in MainActivityFragment // update tracking state in MainActivityFragment
@ -274,4 +297,20 @@ public class MainActivity extends AppCompatActivity implements TrackbookKeys {
return permissions; return permissions;
} }
/* Creates receiver for stopped tracking */
private BroadcastReceiver createTrackingStoppedReceiver() {
return new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// change state
mTracking = false;
setFloatingActionButtonState();
// pass tracking state to MainActivityFragment
mMainActivityFragment.setTrackingState(false);
}
};
}
} }

View File

@ -74,7 +74,8 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
private ItemizedIconOverlay mMyLocationOverlay; private ItemizedIconOverlay mMyLocationOverlay;
private ItemizedIconOverlay mTrackOverlay; private ItemizedIconOverlay mTrackOverlay;
private Location mCurrentBestLocation; private Location mCurrentBestLocation;
private boolean mTacking; private boolean mTrackerServiceRunning;
private boolean mLocalTrackerRunning;
/* Constructor (default) */ /* Constructor (default) */
@ -99,16 +100,11 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
} }
// restore tracking state // restore tracking state
mTacking = false; mTrackerServiceRunning = false;
if (savedInstanceState != null) { if (savedInstanceState != null) {
mTacking = savedInstanceState.getBoolean(INSTANCE_TRACKING_STARTED, false); mTrackerServiceRunning = savedInstanceState.getBoolean(INSTANCE_TRACKING_STATE, false);
} }
// register broadcast receiver for new WayPoints
mTrackUpdatedReceiver = createTrackUpdatedReceiver();
IntentFilter trackUpdatedIntentFilter = new IntentFilter(ACTION_TRACK_UPDATED);
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);
@ -134,6 +130,11 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
mCurrentBestLocation = LocationHelper.determineLastKnownLocation(mLocationManager); mCurrentBestLocation = LocationHelper.determineLastKnownLocation(mLocationManager);
} }
// register broadcast receiver for new WayPoints
mTrackUpdatedReceiver = createTrackUpdatedReceiver();
IntentFilter trackUpdatedIntentFilter = new IntentFilter(ACTION_TRACK_UPDATED);
LocalBroadcastManager.getInstance(mActivity).registerReceiver(mTrackUpdatedReceiver, trackUpdatedIntentFilter);
} }
@ -165,7 +166,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
mCurrentBestLocation = savedInstanceState.getParcelable(INSTANCE_CURRENT_LOCATION); mCurrentBestLocation = savedInstanceState.getParcelable(INSTANCE_CURRENT_LOCATION);
} else if (mCurrentBestLocation != null) { } else if (mCurrentBestLocation != null) {
// fallback or first run: set map to current position // fallback or first run: set map to current position
GeoPoint position = new GeoPoint(mCurrentBestLocation.getLatitude(), mCurrentBestLocation.getLongitude()); GeoPoint position = convertToGeoPoint(mCurrentBestLocation);
mController.setCenter(position); mController.setCenter(position);
mController.setZoom(16); mController.setZoom(16);
} }
@ -184,8 +185,8 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
mMapView.getOverlays().add(compassOverlay); mMapView.getOverlays().add(compassOverlay);
// mark user's location on map // mark user's location on map
if (mCurrentBestLocation != null) { if (mCurrentBestLocation != null && !mTrackerServiceRunning) {
mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation), mTacking); mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation));
mMapView.getOverlays().add(mMyLocationOverlay); mMapView.getOverlays().add(mMyLocationOverlay);
} }
@ -197,8 +198,21 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
// start tracking position // start preliminary tracking - if no TrackerService is running
startFindingLocation(); if (!mTrackerServiceRunning) {
startPreliminaryTracking();
}
// center map on current position - if TrackerService is running
if (mTrackerServiceRunning) {
mController.setCenter(convertToGeoPoint(mCurrentBestLocation));
}
// draw track on map - if available
if (mTrack != null) {
drawTrackOverlay(mTrack);
}
} }
@ -206,8 +220,8 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
// disable location listeners // disable preliminary location listeners
LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener); stopPreliminaryTracking();
} }
@ -215,9 +229,6 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
public void onDestroyView(){ public void onDestroyView(){
super.onDestroyView(); super.onDestroyView();
// unregister broadcast receiver
LocalBroadcastManager.getInstance(mActivity).unregisterReceiver(mTrackUpdatedReceiver);
// deactivate map // deactivate map
mMapView.onDetach(); mMapView.onDetach();
} }
@ -228,6 +239,9 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
// reset first start state // reset first start state
mFirstStart = true; mFirstStart = true;
// disable broadcast receivers
LocalBroadcastManager.getInstance(mActivity).unregisterReceiver(mTrackUpdatedReceiver);
super.onDestroy(); super.onDestroy();
} }
@ -239,7 +253,7 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
switch (item.getItemId()) { switch (item.getItemId()) {
// CASE MY LOCATION // CASE MY LOCATION
case R.id.action_my_location: case R.id.action_bar_my_location:
Toast.makeText(mActivity, mActivity.getString(R.string.toast_message_my_location), Toast.LENGTH_LONG).show(); Toast.makeText(mActivity, mActivity.getString(R.string.toast_message_my_location), Toast.LENGTH_LONG).show();
@ -253,12 +267,11 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
GeoPoint position; GeoPoint position;
if (mCurrentBestLocation != null) { if (mCurrentBestLocation != null) {
// app has a current best estimate location // app has a current best estimate location
position = new GeoPoint(mCurrentBestLocation.getLatitude(), mCurrentBestLocation.getLongitude());
} else { } else {
// app does not have any location fix // app does not have any location fix
mCurrentBestLocation = LocationHelper.determineLastKnownLocation(mLocationManager); mCurrentBestLocation = LocationHelper.determineLastKnownLocation(mLocationManager);
position = new GeoPoint(mCurrentBestLocation.getLatitude(), mCurrentBestLocation.getLongitude());
} }
position = convertToGeoPoint(mCurrentBestLocation);
// center map on current position // center map on current position
mController.setCenter(position); mController.setCenter(position);
@ -284,26 +297,45 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
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.putParcelable(INSTANCE_TRACK, mTrack);
outState.putBoolean(INSTANCE_TRACKING_STARTED, mTacking); outState.putBoolean(INSTANCE_TRACKING_STATE, mTrackerServiceRunning);
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
/* Sets tracking state */ /* Setter for tracking state */
public void setTrackingState (boolean trackingStarted) { public void setTrackingState (boolean trackingState) {
mTacking = trackingStarted; mTrackerServiceRunning = trackingState;
// turn on/off tracking for MainActivity Fragment - prevent double tracking
if (mTrackerServiceRunning) {
stopPreliminaryTracking();
} else if (!mLocalTrackerRunning){
startPreliminaryTracking();
if (mTrack != null) {
drawTrackOverlay(mTrack);
}
}
// update marker
updateMyLocationMarker(); updateMyLocationMarker();
LogHelper.v(LOG_TAG, "TrackingState: " + trackingState);
} }
/* Start finding location for map */ /* Getter for current best location */
private void startFindingLocation() { public Location getCurrentBestLocation() {
return mCurrentBestLocation;
}
/* Start preliminary tracking for map */
private void startPreliminaryTracking() {
mLocalTrackerRunning = true;
// create location listeners // create location listeners
List locationProviders = mLocationManager.getProviders(true); List locationProviders = mLocationManager.getProviders(true);
if (locationProviders.contains(LocationManager.GPS_PROVIDER)) { if (locationProviders.contains(LocationManager.GPS_PROVIDER)) {
mGPSListener = createLocationListener(); mGPSListener = createLocationListener();
} }
if (locationProviders.contains(LocationManager.NETWORK_PROVIDER)) { if (locationProviders.contains(LocationManager.NETWORK_PROVIDER)) {
mNetworkListener = createLocationListener(); mNetworkListener = createLocationListener();
} }
@ -313,6 +345,14 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
} }
/* Removes gps and network location listeners */
private void stopPreliminaryTracking() {
mLocalTrackerRunning = false;
// remove listeners
LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener);
}
/* Creates listener for changes in location status */ /* Creates listener for changes in location status */
private LocationListener createLocationListener() { private LocationListener createLocationListener() {
return new LocationListener() { return new LocationListener() {
@ -321,7 +361,6 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
if (LocationHelper.isBetterLocation(location, mCurrentBestLocation)) { if (LocationHelper.isBetterLocation(location, mCurrentBestLocation)) {
// save location // save location
mCurrentBestLocation = location; mCurrentBestLocation = location;
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
updateMyLocationMarker(); updateMyLocationMarker();
} }
@ -345,15 +384,18 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
/* Updates marker for current user location */ /* Updates marker for current user location */
private void updateMyLocationMarker() { private void updateMyLocationMarker() {
mMapView.getOverlays().remove(mMyLocationOverlay); mMapView.getOverlays().remove(mMyLocationOverlay);
mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation),mTacking); // only update while not tracking
if (!mTrackerServiceRunning) {
mMyLocationOverlay = MapHelper.createMyLocationOverlay(mActivity, mCurrentBestLocation, LocationHelper.isNewLocation(mCurrentBestLocation));
mMapView.getOverlays().add(mMyLocationOverlay); mMapView.getOverlays().add(mMyLocationOverlay);
} }
}
/* Draws track onto overlay */ /* Draws track onto overlay */
private void drawTrackOverlay(Track track) { private void drawTrackOverlay(Track track) {
mMapView.getOverlays().remove(mTrackOverlay); mMapView.getOverlays().remove(mTrackOverlay);
mTrackOverlay = MapHelper.createTrackOverlay(mActivity, track); mTrackOverlay = MapHelper.createTrackOverlay(mActivity, track, mTrackerServiceRunning);
mMapView.getOverlays().add(mTrackOverlay); mMapView.getOverlays().add(mTrackOverlay);
} }
@ -370,16 +412,29 @@ public class MainActivityFragment extends Fragment implements TrackbookKeys {
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_TRACK)) { if (intent.hasExtra(EXTRA_TRACK) && intent.hasExtra(EXTRA_LAST_LOCATION)) {
// draw track on map
mTrack = intent.getParcelableExtra(EXTRA_TRACK); mTrack = intent.getParcelableExtra(EXTRA_TRACK);
drawTrackOverlay(mTrack); drawTrackOverlay(mTrack);
Toast.makeText(mActivity, "New WayPoint.", Toast.LENGTH_LONG).show(); // TODO Remove Toast.makeText(mActivity, "New WayPoint.", Toast.LENGTH_LONG).show(); // TODO Remove
// center map over last location
mCurrentBestLocation = intent.getParcelableExtra(EXTRA_LAST_LOCATION);
mController.setCenter(convertToGeoPoint(mCurrentBestLocation));
// clear intent
intent.removeExtra(EXTRA_TRACK);
intent.removeExtra(EXTRA_LAST_LOCATION);
} }
} }
}; };
} }
/* Converts Location to GeoPoint */
private GeoPoint convertToGeoPoint (Location location) {
return new GeoPoint(location.getLatitude(), location.getLongitude());
}
/* Saves state of map */ /* Saves state of map */
private void saveMaoState(Context context) { private void saveMaoState(Context context) {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);

View File

@ -32,6 +32,7 @@ import org.y20k.trackbook.core.Track;
import org.y20k.trackbook.core.WayPoint; 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.LogHelper;
import org.y20k.trackbook.helpers.NotificationHelper;
import org.y20k.trackbook.helpers.TrackbookKeys; import org.y20k.trackbook.helpers.TrackbookKeys;
import java.util.List; import java.util.List;
@ -73,27 +74,40 @@ public class TrackerService extends Service implements TrackbookKeys {
// create a new track // create a new track
mTrack = new Track(); mTrack = new Track();
// add first location to track // get last location
if (intent.hasExtra(EXTRA_LAST_LOCATION)) {
mCurrentBestLocation = intent.getParcelableExtra(EXTRA_LAST_LOCATION);
}
// get last location - fallback
if (mCurrentBestLocation == null) {
mCurrentBestLocation = LocationHelper.determineLastKnownLocation(mLocationManager); mCurrentBestLocation = LocationHelper.determineLastKnownLocation(mLocationManager);
}
// add last location as waypoint to track
addWayPointToTrack(); addWayPointToTrack();
// set timer to retrieve new locations and 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(EIGHT_HOURS_IN_MILLISECONDS, FIFTEEN_SECONDS_IN_MILLISECONDS) {
@Override @Override
public void onTick(long l) { public void onTick(long millisUntilFinished) {
// update track duration
mTrack.setTrackDuration(EIGHT_HOURS_IN_MILLISECONDS - millisUntilFinished);
// try to add WayPoint to Track
addWayPointToTrack(); addWayPointToTrack();
// update notification
NotificationHelper.update(mTrack, true);
} }
@Override @Override
public void onFinish() { public void onFinish() {
// TODO // remove listeners
stopFindingLocation();
} }
}; };
mTimer.start(); mTimer.start();
// create gps and network location listeners // create gps and network location listeners
startFindingLocation(); startFindingLocation();
} }
// ACTION STOP // ACTION STOP
@ -104,7 +118,7 @@ public class TrackerService extends Service implements TrackbookKeys {
mTimer.cancel(); mTimer.cancel();
// remove listeners // remove listeners
LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener); stopFindingLocation();
} }
// 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
@ -124,7 +138,7 @@ public class TrackerService extends Service implements TrackbookKeys {
LogHelper.v(LOG_TAG, "onDestroy called."); LogHelper.v(LOG_TAG, "onDestroy called.");
// remove listeners // remove listeners
LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener); stopFindingLocation();
// cancel notification // cancel notification
stopForeground(true); stopForeground(true);
@ -162,6 +176,7 @@ public class TrackerService extends Service implements TrackbookKeys {
Intent i = new Intent(); Intent i = new Intent();
i.setAction(ACTION_TRACK_UPDATED); i.setAction(ACTION_TRACK_UPDATED);
i.putExtra(EXTRA_TRACK, mTrack); i.putExtra(EXTRA_TRACK, mTrack);
i.putExtra(EXTRA_LAST_LOCATION, mCurrentBestLocation);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(i); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(i);
} }
@ -185,11 +200,11 @@ public class TrackerService extends Service implements TrackbookKeys {
} }
public void onProviderEnabled(String provider) { public void onProviderEnabled(String provider) {
// TODO do something LogHelper.v(LOG_TAG, "Location provider enabled: " + provider);
} }
public void onProviderDisabled(String provider) { public void onProviderDisabled(String provider) {
// TODO do something LogHelper.v(LOG_TAG, "Location provider disabled: " + provider);
} }
}; };
} }
@ -197,7 +212,9 @@ public class TrackerService extends Service implements TrackbookKeys {
/* Creates gps and network location listeners */ /* Creates gps and network location listeners */
private void startFindingLocation() { private void startFindingLocation() {
LogHelper.v(LOG_TAG, "Setting up location listeners."); LogHelper.v(LOG_TAG, "startFindingLocation"); // TODO remove
// put up notification
NotificationHelper.show(this,mTrack);
// register location listeners and request updates // register location listeners and request updates
List locationProviders = mLocationManager.getProviders(true); List locationProviders = mLocationManager.getProviders(true);
@ -210,4 +227,24 @@ public class TrackerService extends Service implements TrackbookKeys {
} }
/* Removes gps and network location listeners */
private void stopFindingLocation() {
LogHelper.v(LOG_TAG, "stopFindingLocation"); // TODO remove
// remove listeners
LocationHelper.removeLocationListeners(mLocationManager, mGPSListener, mNetworkListener);
// notify MainActivityFragment
Intent i = new Intent();
i.setAction(ACTION_TRACKING_STOPPED);
i.putExtra(EXTRA_TRACK, mTrack);
i.putExtra(EXTRA_LAST_LOCATION, mCurrentBestLocation);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(i);
// // cancel notification
// NotificationHelper.stop();
// change notification
NotificationHelper.update(mTrack, false);
}
} }

View File

@ -16,7 +16,6 @@
package org.y20k.trackbook.core; package org.y20k.trackbook.core;
import android.content.Context;
import android.location.Location; import android.location.Location;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
@ -27,6 +26,7 @@ import org.y20k.trackbook.helpers.TrackbookKeys;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
/** /**
@ -39,9 +39,9 @@ public class Track implements TrackbookKeys, Parcelable {
/* Main class variables */ /* Main class variables */
private Context mContext;
private List<WayPoint> mWayPoints; private List<WayPoint> mWayPoints;
private float mTrackLength; private float mTrackLength;
private long mTrackDuration;
/* Constructor */ /* Constructor */
@ -72,12 +72,6 @@ public class Track implements TrackbookKeys, Parcelable {
}; };
/* Set mContext needed by */
public void setContext(Context context) {
mContext = context;
}
/* Adds new waypoint */ /* Adds new waypoint */
public WayPoint addWayPoint(Location location) { public WayPoint addWayPoint(Location location) {
// add up distance // add up distance
@ -105,6 +99,12 @@ public class Track implements TrackbookKeys, Parcelable {
} }
/* Setter for duration of track */
public void setTrackDuration(long trackDuration) {
mTrackDuration = trackDuration;
}
/* Getter for mWayPoints */ /* Getter for mWayPoints */
public List<WayPoint> getWayPoints() { public List<WayPoint> getWayPoints() {
return mWayPoints; return mWayPoints;
@ -117,12 +117,25 @@ public class Track implements TrackbookKeys, Parcelable {
} }
/* Getter for duration of track */
public String getTrackDuration() {
return convertToReadableTime(mTrackDuration);
}
/* Getter for distance of track */
public String getTrackDistance() {
float trackDistance = mWayPoints.get(mWayPoints.size()-1).getDistanceToStartingPoint();
return String.format ("%.0f", trackDistance) + "m";
}
/* 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).getLocation(); return mWayPoints.get(index).getLocation();
} }
/* Adds distance to given location to length of track */ /* Adds distance to given location to length of track */
private float addDistanceToTrack(Location location) { private float addDistanceToTrack(Location location) {
// get number of previously recorded waypoints // get number of previously recorded waypoints
@ -139,6 +152,14 @@ public class Track implements TrackbookKeys, Parcelable {
} }
/* Converts milliseconds to hh:mm:ss */
private String convertToReadableTime(long milliseconds) {
return String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(milliseconds),
TimeUnit.MILLISECONDS.toMinutes(milliseconds) % TimeUnit.HOURS.toMinutes(1),
TimeUnit.MILLISECONDS.toSeconds(milliseconds) % TimeUnit.MINUTES.toSeconds(1));
}
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;

View File

@ -27,19 +27,12 @@ import java.util.List;
/** /**
* LocationHelper class * LocationHelper class
*/ */
public final class LocationHelper { public final class LocationHelper implements TrackbookKeys {
/* Define log tag */ /* Define log tag */
private static final String LOG_TAG = LocationHelper.class.getSimpleName(); private static final String LOG_TAG = LocationHelper.class.getSimpleName();
/* Main class variables */
// private static final int TWO_MINUTES = 1000 * 1000 * 60 * 2;
private static final long FIVE_MINUTES = 5L * 60000000000L; // 2 minutes
private static final long TWO_MINUTES = 2L * 60000000000L; // 2 minutes
private static final long TWENTY_SECONDS = 20000000000L; // 20 seconds
/* Determines last known location */ /* Determines last known location */
public static Location determineLastKnownLocation(LocationManager locationManager) { public static Location determineLastKnownLocation(LocationManager locationManager) {
// define variables // define variables
@ -72,13 +65,14 @@ public final class LocationHelper {
} }
} }
// return best estimate location if (gpsLocation == null) {
if (isBetterLocation(networkLocation, gpsLocation)) {
LogHelper.v(LOG_TAG, "Best last known location came from: " + networkLocation.getProvider()); // TODO remove
return networkLocation; return networkLocation;
} else { } else if (networkLocation == null) {
LogHelper.v(LOG_TAG, "Best last known location came from: " + gpsLocation.getProvider()); // TODO remove
return gpsLocation; return gpsLocation;
} else if (isBetterLocation(gpsLocation, networkLocation)) {
return gpsLocation;
} else {
return networkLocation;
} }
} }
@ -94,8 +88,8 @@ public final class LocationHelper {
// check whether the new location fix is newer or older // check whether the new location fix is newer or older
long timeDelta = location.getElapsedRealtimeNanos() - currentBestLocation.getElapsedRealtimeNanos(); long timeDelta = location.getElapsedRealtimeNanos() - currentBestLocation.getElapsedRealtimeNanos();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES; boolean isSignificantlyNewer = timeDelta > TWO_MINUTES_IN_NANOSECONDS;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES; boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES_IN_NANOSECONDS;
boolean isNewer = timeDelta > 0; boolean isNewer = timeDelta > 0;
// if it's been more than two minutes since the current location, use the new location because the user has likely moved // if it's been more than two minutes since the current location, use the new location because the user has likely moved
@ -118,16 +112,12 @@ public final class LocationHelper {
// determine location quality using a combination of timeliness and accuracy // determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) { if (isMoreAccurate) {
LogHelper.v(LOG_TAG, "Location isMoreAccurate: " + location.getProvider()); // TODO remove
return true; return true;
} else if (isNewer && !isLessAccurate) { } else if (isNewer && !isLessAccurate) {
LogHelper.v(LOG_TAG, "Location isNewer && !isLessAccurate: " + location.getProvider()); // TODO remove
return true; return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) { } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
LogHelper.v(LOG_TAG, "Location isNewer && !isSignificantlyLessAccurate && isFromSameProvider: " + location.getProvider()); // TODO remove
return true; return true;
} }
LogHelper.v(LOG_TAG, "Location is not better: " + location.getProvider()); // TODO remove
return false; return false;
} }
@ -138,7 +128,7 @@ public final class LocationHelper {
return false; return false;
} else { } else {
long locationTime = SystemClock.elapsedRealtimeNanos() - location.getElapsedRealtimeNanos(); long locationTime = SystemClock.elapsedRealtimeNanos() - location.getElapsedRealtimeNanos();
return locationTime < TWO_MINUTES; return locationTime < TWO_MINUTES_IN_NANOSECONDS;
} }
} }
@ -148,15 +138,15 @@ public final class LocationHelper {
float distance = newLocation.distanceTo(lastLocation); float distance = newLocation.distanceTo(lastLocation);
long timeDifference = newLocation.getElapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos(); long timeDifference = newLocation.getElapsedRealtimeNanos() - lastLocation.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 12 seconds
return distance > 10 && timeDifference >= TWENTY_SECONDS; return distance > 10 && timeDifference >= TWELVE_SECONDS_IN_NANOSECONDS;
} }
/* Checks if given location is a stop over */ /* Checks if given location is a stop over */
public static boolean isStopOver(Location lastLocation, Location newLocation) { public static boolean isStopOver(Location lastLocation, Location newLocation) {
long timeDifference = newLocation.getElapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos(); long timeDifference = newLocation.getElapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos();
return timeDifference >= FIVE_MINUTES; return timeDifference >= FIVE_MINUTES_IN_NANOSECONDS;
} }

View File

@ -21,6 +21,7 @@ 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 android.text.format.DateFormat;
import android.widget.Toast;
import org.osmdroid.util.GeoPoint; import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.overlay.ItemizedIconOverlay; import org.osmdroid.views.overlay.ItemizedIconOverlay;
@ -42,17 +43,14 @@ public final class MapHelper {
private static final String LOG_TAG = MapHelper.class.getSimpleName(); private static final String LOG_TAG = MapHelper.class.getSimpleName();
/* Creates icon overlay for current position */ /* Creates icon overlay for current position (used in MainActivity Fragment) */
public static ItemizedIconOverlay createMyLocationOverlay(Context context, Location currentBestLocation, boolean locationIsNew, boolean trackingStarted) { public static ItemizedIconOverlay createMyLocationOverlay(Context context, Location currentBestLocation, boolean locationIsNew) {
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
// create marker // create marker
Drawable newMarker; Drawable newMarker;
if (trackingStarted) { if (locationIsNew) {
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);
@ -83,20 +81,62 @@ public final class MapHelper {
/* Creates icon overlay for track */ /* Creates icon overlay for track */
public static ItemizedIconOverlay createTrackOverlay(Context context, Track track){ public static ItemizedIconOverlay createTrackOverlay(final Context context, Track track, boolean trackingActive){
WayPoint wayPoint;
boolean currentPosition;
final int trackSize = track.getSize();
final List<WayPoint> wayPoints = track.getWayPoints(); final List<WayPoint> wayPoints = track.getWayPoints();
final ArrayList<OverlayItem> overlayItems = new ArrayList<>(); final ArrayList<OverlayItem> overlayItems = new ArrayList<>();
for (WayPoint wayPoint : wayPoints) { for (int i = 0 ; i < track.getSize() ; i++) {
// get waypoint and check if it is current position
wayPoint = wayPoints.get(i);
currentPosition = i == trackSize - 1;
// create marker // create marker
Drawable newMarker; Drawable newMarker;
// CASE 1: Tracking active and waypoint is not current position
if (trackingActive && !currentPosition) {
if (wayPoint.getIsStopOver()) { if (wayPoint.getIsStopOver()) {
// stop over marker
newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_crumb_grey_24dp); newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_crumb_grey_24dp);
} else { } else {
// default marker for this case
newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_crumb_red_24dp);
}
}
// CASE 2: Tracking active and waypoint is current position
else if (trackingActive && currentPosition) {
if (wayPoint.getIsStopOver()) {
// stop over marker
newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_dot_grey_24dp);
} else {
// default marker for this case
newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_dot_red_24dp);
}
}
// CASE 3: Tracking not active and waypoint is not current position
else if (!trackingActive && !currentPosition) {
if (wayPoint.getIsStopOver()) {
// stop over marker
newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_crumb_grey_24dp);
} else {
// default marker for this case
newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_crumb_blue_24dp); newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_crumb_blue_24dp);
} }
final String title = Float.toString(wayPoint.getDistanceToStartingPoint()); }
// CASE 4: Tracking not active and waypoint is current position
else {
// default marker
newMarker = AppCompatDrawableManager.get().getDrawable(context, R.drawable.ic_my_location_crumb_blue_24dp);
}
final String title = Float.toString(wayPoint.getDistanceToStartingPoint()) + " (" + wayPoint.getLocation().getProvider() + ")";
final String description = DateFormat.getDateFormat(context).format(wayPoint.getLocation().getTime()); final String description = DateFormat.getDateFormat(context).format(wayPoint.getLocation().getTime());
final GeoPoint position = new GeoPoint(wayPoint.getLocation().getLatitude(), wayPoint.getLocation().getLongitude()); final GeoPoint position = new GeoPoint(wayPoint.getLocation().getLatitude(), wayPoint.getLocation().getLongitude());
OverlayItem overlayItem = new OverlayItem(title, description, position); OverlayItem overlayItem = new OverlayItem(title, description, position);
@ -111,6 +151,7 @@ public final class MapHelper {
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) {
Toast.makeText(context, "Measured distance: " + item.getTitle(), Toast.LENGTH_LONG).show(); // TODO make string
LogHelper.v(LOG_TAG, "Tap on a track crumb icon detected. Measured distance: " + item.getTitle()); LogHelper.v(LOG_TAG, "Tap on a track crumb icon detected. Measured distance: " + item.getTitle());
return true; return true;
} }
@ -125,6 +166,4 @@ public final class MapHelper {
} }
} }

View File

@ -1,7 +1,177 @@
/**
* NotificationHelper.java
* Implements the NotificationHelper class
* A NotificationHelper creates and configures a notification
*
* 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.helpers; package org.y20k.trackbook.helpers;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.app.TaskStackBuilder;
import android.support.v7.app.NotificationCompat;
import org.y20k.trackbook.MainActivity;
import org.y20k.trackbook.R;
import org.y20k.trackbook.TrackerService;
import org.y20k.trackbook.core.Track;
/** /**
* Created by solaris on 29/07/16. * NotificationHelper class
*/ */
public class NotificationHelper { public class NotificationHelper implements TrackbookKeys {
/* Define log tag */
private static final String LOG_TAG = NotificationHelper.class.getSimpleName();
/* Main class variables */
private static Notification mNotification;
private static Service mService;
/* Create and put up notification */
public static void show(final Service service, Track track) {
// save service
mService = service;
// build notification
mNotification = getNotificationBuilder(track, true).build();
// display notification
mService.startForeground(TRACKER_SERVICE_NOTIFICATION_ID, mNotification);
}
/* Updates the notification */
public static void update(Track track, boolean tracking) {
// build notification
mNotification = getNotificationBuilder(track, tracking).build();
// display updated notification
NotificationManager notificationManager = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(TRACKER_SERVICE_NOTIFICATION_ID, mNotification);
if (!tracking) {
// make notification swipe-able
mService.stopForeground(false);
}
}
/* Stop displaying notification */
public static void stop() {
if (mService != null) {
mService.stopForeground(true);
}
}
/* Creates a notification builder */
private static NotificationCompat.Builder getNotificationBuilder(Track track, boolean tracking) {
String contentText = mService.getString(R.string.notification_content_distance) + ": " + track.getTrackDistance() + " | " +
mService.getString(R.string.notification_content_duration) + " : " + track.getTrackDuration();
// explicit intent for notification tap
Intent tapActionIntent = new Intent(mService, MainActivity.class);
// explicit intent for stopping playback
Intent stopActionIntent = new Intent(mService, TrackerService.class);
stopActionIntent.setAction(ACTION_STOP);
// artificial back stack for started Activity.
// -> navigating backward from the Activity leads to Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mService);
// // backstack: adds back stack for Intent (but not the Intent itself)
// stackBuilder.addParentStack(MainActivity.class);
// backstack: add explicit intent for notification tap
stackBuilder.addNextIntent(tapActionIntent);
// pending intent wrapper for notification tap
PendingIntent tapActionPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
// pending intent wrapper for notification stop action
PendingIntent stopActionPendingIntent = PendingIntent.getService(mService, 0, stopActionIntent, 0);
// construct notification in builder
NotificationCompat.Builder builder;
builder = new NotificationCompat.Builder(mService);
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
builder.setShowWhen(false);
builder.setContentIntent(tapActionPendingIntent);
builder.setSmallIcon(R.drawable.ic_notification_small_24dp);
builder.setLargeIcon(getNotificationIconLarge(tracking));
if (tracking) {
builder.addAction(R.drawable.ic_stop_white_36dp, mService.getString(R.string.notification_stop), stopActionPendingIntent);
builder.setContentTitle(mService.getString(R.string.notification_title_trackbook_running));
builder.setContentText(contentText);
// third line of text - only appears in expanded view
// builder.setSubText();
} else {
builder.setContentTitle(mService.getString(R.string.notification_title_trackbook_not_running));
builder.setContentText(contentText);
// third line of text - only appears in expanded view
// builder.setSubText();
}
return builder;
}
/* Get station image for notification's large icon */
private static Bitmap getNotificationIconLarge(boolean tracking) {
// get dimensions
Resources resources = mService.getResources();
int height = (int) resources.getDimension(android.R.dimen.notification_large_icon_height);
int width = (int) resources.getDimension(android.R.dimen.notification_large_icon_width);
Bitmap bitmap;
if (tracking) {
bitmap = getBitmap(R.drawable.ic_notification_large_tracking_48dp);
} else {
bitmap = getBitmap(R.drawable.ic_notification_large_not_tracking_48dp);
}
return Bitmap.createScaledBitmap(bitmap, width, height, false);
}
/* Return a bitmap for a given resource id of a vector drawable */
private static Bitmap getBitmap(int resource) {
VectorDrawableCompat drawable = VectorDrawableCompat.create(mService.getResources(), resource, null);
if (drawable != null) {
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} else {
return null;
}
}
} }

View File

@ -26,9 +26,11 @@ public interface TrackbookKeys {
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_TRACK_UPDATED = "TRACK_UPDATED"; public static final String ACTION_TRACK_UPDATED = "TRACK_UPDATED";
public static final String ACTION_TRACKING_STOPPED = "TRACKING_STOPPED";
/* EXTRAS */ /* EXTRAS */
public static final String EXTRA_TRACK = "TRACK"; public static final String EXTRA_TRACK = "TRACK";
public static final String EXTRA_LAST_LOCATION = "LAST_LOCATION";
/* ARGS */ /* ARGS */
public static final String ARG_PERMISSIONS_GRANTED = "ArgPermissionsGranted"; public static final String ARG_PERMISSIONS_GRANTED = "ArgPermissionsGranted";
@ -48,21 +50,21 @@ public interface TrackbookKeys {
public static final String INSTANCE_LONGITUDE = "longitude"; public static final String INSTANCE_LONGITUDE = "longitude";
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_STATE = "trackingState";
public static final String INSTANCE_TRACK = "track"; 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 long EIGHT_HOURS_IN_MILLISECONDS = 43200000; // maximum tracking duration
public static final long CONSTANT_MAXIMAL_DURATION = 43200000; // equals 8 hours public static final long FIFTEEN_SECONDS_IN_MILLISECONDS = 15000; // timer interval for tracking
public static final long CONSTANT_TRACKING_INTERVAL = 15000; // equals 15 seconds public static final long FIVE_MINUTES_IN_NANOSECONDS = 5L * 60000000000L; // determines a stop over
public static final long TWO_MINUTES_IN_NANOSECONDS = 2L * 60000000000L; // defines an old location
public static final long TWELVE_SECONDS_IN_NANOSECONDS = 12000000000L; // defines a new location
/* MISC */ /* MISC */
public static final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124; public static final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
public static final int LOCATION_STATUS_OFFLINE = 0; public static final int TRACKER_SERVICE_NOTIFICATION_ID = 1;
public static final int LOCATION_STATUS_OK = 1;
public static final int LOCATION_STATUS_GPS_ONLY = 2;
public static final int LOCATION_STATUS_NETWORK_ONLY = 3;
} }

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="96.0"
android:viewportWidth="96.0"
android:width="24dp">
<path
android:fillColor="@color/trackbook_red"
android:pathData="M48,48m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:alpha="0.01" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M6,6h12v12H6z"/>
</vector>

View File

@ -4,6 +4,6 @@
android:viewportWidth="24.0" android:viewportWidth="24.0"
android:viewportHeight="24.0"> android:viewportHeight="24.0">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="@color/trackbook_white"
android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/> android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
</vector> </vector>

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="48dp"
android:viewportHeight="192.0"
android:viewportWidth="192.0"
android:width="48dp" >
<path
android:fillColor="@color/trackbook_blue"
android:pathData="M96,96m-96,0a96,96 0,1 1,192 0a96,96 0,1 1,-192 0"/>
<path
android:fillColor="@color/trackbook_white"
android:pathData="M48,96a48,46.5 0,1 0,96 0a48,46.5 0,1 0,-96 0z"/>
</vector>

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="48dp"
android:viewportHeight="192.0"
android:viewportWidth="192.0"
android:width="48dp" >
<path
android:fillColor="@color/trackbook_blue"
android:pathData="M96,96m-96,0a96,96 0,1 1,192 0a96,96 0,1 1,-192 0"/>
<path
android:fillColor="@color/trackbook_red_dark"
android:pathData="M48,96a48,46.5 0,1 0,96 0a48,46.5 0,1 0,-96 0z"/>
</vector>

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="96.0"
android:viewportWidth="96.0"
android:height="24dp"
android:width="24dp">
<path
android:fillColor="@color/trackbook_white"
android:pathData="M44,12.99L20.69,8.74L5.12,5.89C2.29,5.38 0,7.44 0,10.49v67.38c0,3.06 2.29,5.96 5.12,6.47l15.57,2.85l22.19,4.05L44,91.46V12.99z"/>
<path
android:fillColor="@color/trackbook_white"
android:pathData="M90.88,5.89L75.31,8.74L53.12,12.79L52,12.99v78.46l23.31,-4.26l15.57,-2.85c2.83,-0.52 5.12,-3.41 5.12,-6.47V10.49C96,7.44 93.71,5.38 90.88,5.89z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="36dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0"
android:width="36dp">
<path
android:fillColor="@color/trackbook_white"
android:pathData="M6,6h12v12H6z"/>
</vector>

View File

@ -4,16 +4,16 @@
tools:context="org.y20k.trackbook.MainActivity"> tools:context="org.y20k.trackbook.MainActivity">
<item <item
android:id="@+id/action_my_location" android:id="@+id/action_bar_my_location"
android:icon="@drawable/ic_my_location_white_24dp" android:icon="@drawable/ic_my_location_white_24dp"
android:title="@string/menu_my_location" android:title="@string/menu_my_location"
android:visible="true" android:visible="true"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/action_settings" android:id="@+id/action_bar_about"
android:orderInCategory="100" android:orderInCategory="100"
android:title="@string/menu_settings" android:title="@string/menu_about"
app:showAsAction="never" /> app:showAsAction="never" />
</menu> </menu>

View File

@ -3,8 +3,16 @@
<string name="app_name">Trackbook</string> <string name="app_name">Trackbook</string>
<!-- menu entries --> <!-- menu entries -->
<string name="menu_settings">Settings</string>
<string name="menu_my_location">My Location</string> <string name="menu_my_location">My Location</string>
<string name="menu_about">About</string>
<!-- notification -->
<string name="notification_title_trackbook_running">Trackbook running</string>
<string name="notification_title_trackbook_not_running">Trackbook not running</string>
<string name="notification_stop">Stop</string>
<string name="notification_content_duration">Duration</string>
<string name="notification_content_distance">Distance</string>
<!-- snackbar messages --> <!-- snackbar messages -->
<string name="snackbar_message_tracking_stopped">Tracking stopped</string> <string name="snackbar_message_tracking_stopped">Tracking stopped</string>