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