diff --git a/.gitignore b/.gitignore
index 2b0c0d1..86f53bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@
/build
/captures
.externalNativeBuild
+/app/app-release.apk
diff --git a/app/build.gradle b/app/build.gradle
index 20ff886..dcd1fdb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -47,5 +47,6 @@ dependencies {
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:24.2.1'
compile 'com.google.android.gms:play-services-maps:10.2.1'
+ compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
testCompile 'junit:junit:4.12'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 80b76cf..a555f7a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,8 @@
package="ville.fi.hikemate">
+
+
@@ -12,14 +14,14 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
-
+
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/java/ville/fi/hikemate/Activities/MainActivity.java b/app/src/main/java/ville/fi/hikemate/Activities/MainActivity.java
index 957abc8..201bd93 100644
--- a/app/src/main/java/ville/fi/hikemate/Activities/MainActivity.java
+++ b/app/src/main/java/ville/fi/hikemate/Activities/MainActivity.java
@@ -8,34 +8,54 @@
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import java.util.ArrayList;
-import java.util.zip.Inflater;
-
import ville.fi.hikemate.Fragments.HikePlansFragment;
import ville.fi.hikemate.Fragments.HikeListFragment;
import ville.fi.hikemate.Fragments.NewHikeFragment;
import ville.fi.hikemate.Fragments.ViewPagerAdapter;
import ville.fi.hikemate.R;
-import ville.fi.hikemate.Resources.Hike;
-import ville.fi.hikemate.Resources.HikeList;
import ville.fi.hikemate.Utils.Debug;
-import ville.fi.hikemate.Utils.StorageHandler;
-
-import static android.R.id.list;
+/**
+ * MainActivity is the main view of the app.
+ *
+ * MainActivity consists of an action bar and a tab layout. Tab views are
+ * fragments and they are added to a view pager adapter. Each fragment has
+ * it's own class. User can also start tracking a new hike. This takes the
+ * user to a new activity.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
public class MainActivity extends AppCompatActivity {
+ /**
+ * Context of the class.
+ */
Context host = this;
+
+ /**
+ * Toolbar of the activity.
+ */
private Toolbar toolbar;
+
+ /**
+ * Tab layout of the activity.
+ */
private TabLayout tabLayout;
+
+ /**
+ * Pager for the tab layout.
+ */
private ViewPager viewPager;
- private HikeList hikes;
+ /**
+ * Sets up the activity.
+ *
+ * Makes the initial load of the debug class and setups the tab view.
+ *
+ * @param savedInstanceState bundle for the activity
+ */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -44,31 +64,37 @@ protected void onCreate(Bundle savedInstanceState) {
setupView();
}
+ /**
+ * Starts the map activity for user location tracking.
+ *
+ * @param v view of the activity
+ */
public void startTracking(View v) {
System.out.println("startTracking");
Intent i = new Intent(host, MapActivity.class);
startActivity(i);
}
+ /**
+ * Sets up the tab view.
+ */
private void setupView() {
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+ getSupportActionBar().setHomeButtonEnabled(false);
viewPager = (ViewPager) findViewById(R.id.viewpager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
- ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
+ ViewPagerAdapter adapter = new ViewPagerAdapter(
+ getSupportFragmentManager());
adapter.addFragment(new NewHikeFragment(), "New Hike");
adapter.addFragment(new HikeListFragment(), "Your Hikes");
adapter.addFragment(new HikePlansFragment(), "Hike Plans");
viewPager.setAdapter(adapter);
}
-
- public HikeList getHikes() {
- return hikes;
- }
}
diff --git a/app/src/main/java/ville/fi/hikemate/Activities/MapActivity.java b/app/src/main/java/ville/fi/hikemate/Activities/MapActivity.java
index 97888bc..5a480fd 100644
--- a/app/src/main/java/ville/fi/hikemate/Activities/MapActivity.java
+++ b/app/src/main/java/ville/fi/hikemate/Activities/MapActivity.java
@@ -1,109 +1,227 @@
package ville.fi.hikemate.Activities;
import android.Manifest;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
+import android.support.v4.content.LocalBroadcastManager;
import android.view.View;
+import android.widget.Button;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
+import java.io.File;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.LinkedList;
import ville.fi.hikemate.R;
import ville.fi.hikemate.Resources.Hike;
-import ville.fi.hikemate.Resources.HikeList;
-import ville.fi.hikemate.Resources.HikeLocation;
+import ville.fi.hikemate.Services.LocationService;
import ville.fi.hikemate.Utils.Debug;
+import ville.fi.hikemate.Utils.HikeToLatLng;
import ville.fi.hikemate.Utils.StorageHandler;
+/**
+ * MapActivity is the activity that displays the user tracked path.
+ *
+ * The user taken path is drawn to the map. User can cancel, save or take a
+ * photo during the hike.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
public class MapActivity extends FragmentActivity implements OnMapReadyCallback{
+ /**
+ * Context of the activity.
+ */
private Context host = this;
+
+ /**
+ * This activity.
+ */
private FragmentActivity thisActivity = this;
+
+ /**
+ * Integer used for permission request.
+ */
private final int MY_PERMISSIONS_REQUEST_LOCATION = 0;
+
+ /**
+ * Map of the view.
+ */
private GoogleMap mMap;
- private LocationManager locationManager;
- private LocationListener locationListener;
- private Location location;
+
+ /**
+ * List of user's hikes.
+ */
private LinkedList hikes;
+
+ /**
+ * Current hike.
+ */
private Hike hike;
+
+ /**
+ * Handler for loading and saving the hike.
+ */
private StorageHandler sh;
+
+ /**
+ * Polyline for the user travelled path.
+ */
private Polyline hikePolyLine;
+
+ /**
+ * Status of the tracking process.
+ */
private boolean firstLocationGot = false;
+
+ /**
+ * Different status of the tracking process.
+ */
private boolean isTrackingLocation;
+
+ /**
+ * Yet another status of the tracking process.
+ */
+ private boolean locationFound = false;
+
+ /**
+ * Intent for the main activity.
+ */
private Intent mainActivityIntent;
+
+ /**
+ * List of the hike's lat and lng points.
+ */
private LinkedList hikeLatLng;
+ /**
+ * Intent for the location service.
+ */
+ private Intent locationServiceIntent;
+
+ /**
+ * Formatter for the dates.
+ */
+ private DateFormat dateFormat;
+
+ /**
+ * Integer for the image capture request.
+ */
+ private final int REQUEST_IMAGE_CAPTURE = 1;
+
+ /**
+ * Integer for the photo capture request.
+ */
+ private final int REQUEST_TAKE_PHOTO = 1;
+
+ /**
+ * Previously taken photo's file path.
+ */
+ private String mCurrentPhotoPath;
+
+ /**
+ * Button for taking photos.
+ */
+ private Button takeImageButton;
+
+ /**
+ * Button for cancelling the tracking.
+ */
+ private Button cancelTrackingButton;
+
+ /**
+ * Button for saving the tracked hike.
+ */
+ private Button saveTrackingButton;
+
+ /**
+ * Sets up the activity.
+ *
+ * Initializes attributes used in this activity and asks user permission
+ * for the location tracking.
+ *
+ * @param savedInstanceState bundle for the activity
+ */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
+
mainActivityIntent = new Intent(host, MainActivity.class);
isTrackingLocation = true;
sh = new StorageHandler();
hikes = sh.readStorage(host);
- hike = new Hike("MyHike");
- hikeLatLng = getLatLng(hike);
-
- SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
+ Date date = new Date();
+ dateFormat = new SimpleDateFormat("d MMM yyyy");
+ hike = new Hike(dateFormat.format(date));
+ dateFormat = new SimpleDateFormat("HH:mm");
+ hike.setTime(dateFormat.format(date));
+ hikeLatLng = HikeToLatLng.getLatLng(hike);
+
+ takeImageButton = (Button) findViewById(R.id.button_takeImage);
+ takeImageButton.setEnabled(false);
+ cancelTrackingButton = (Button) findViewById(R.id.button_cancel);
+ cancelTrackingButton.setEnabled(false);
+ saveTrackingButton = (Button) findViewById(R.id.button_stopAndSave);
+ saveTrackingButton.setEnabled(false);
+
+ SupportMapFragment mapFragment =
+ (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
- locationManager = (LocationManager) host.getSystemService(Context.LOCATION_SERVICE);
- initLocationListener();
+ LocalBroadcastManager.getInstance(host).registerReceiver(
+ mMessageReceiver, new IntentFilter("GPSLocations"));
+ locationServiceIntent = new Intent(host, LocationService.class);
- // Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(host,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
- System.out.println("Asking permissions, no permissions");
- Debug.print(host, "Asking permissions", "Requesting permission", "No permissions, dialog", 1);
- // Should we show an explanation?
- if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
- Manifest.permission.ACCESS_FINE_LOCATION)) {
-
- // Show an explanation to the user *asynchronously* -- don't block
- // this thread waiting for the user's response! After the user
- // sees the explanation, try again to request the permission.
+ if (ActivityCompat.shouldShowRequestPermissionRationale(
+ thisActivity, Manifest.permission.ACCESS_FINE_LOCATION)) {
} else {
- // No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
} else {
- try {
- locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 1, locationListener);
- Debug.print(host, "onRequestPermissionResult", "LocationGranted", "LocationManager requesting", 1);
- } catch(SecurityException e) {
- e.printStackTrace();
- }
+ startService(locationServiceIntent);
+ Debug.toastThisLonger(host, "Fetching location...");
}
}
/**
- * Returns the user to the main activity view.
+ * Cancels tracking and returns the user to the main activity view.
*
- * @param v
+ * @param v view of the activity
*/
public void cancelTracking(View v) {
isTrackingLocation = false;
+ stopService(locationServiceIntent);
startActivity(mainActivityIntent);
}
@@ -112,96 +230,186 @@ public void cancelTracking(View v) {
*
* Saves the hike to the storage and returns the user to the main activity.
*
- * @param v
+ * @param v view of the activity
*/
public void saveHike(View v) {
isTrackingLocation = false;
+ stopService(locationServiceIntent);
for (LatLng latLng : hikeLatLng) {
hike.addLocation(latLng.latitude, latLng.longitude);
}
- hikes.add(hike);
+ hikes.addFirst(hike);
sh.writeStorage(host, hikes);
startActivity(mainActivityIntent);
}
+ /**
+ * Takes a photo using device's camera.
+ *
+ * @param v view of the activity.
+ */
+ public void takeImage(View v) {
+ if (locationFound) {
+ Intent takePictureIntent = new Intent(
+ MediaStore.ACTION_IMAGE_CAPTURE);
+
+ if (takePictureIntent.resolveActivity(
+ getPackageManager()) != null) {
+
+ File storageDir = Environment
+ .getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES);
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
+ .format(new Date());
+ String imageFileName = "JPEG_" + timeStamp + "_.jpg";
+ File photoFile = new File(storageDir, imageFileName);
+ mCurrentPhotoPath = photoFile.getAbsolutePath();
+
+ if (photoFile != null) {
+
+ Uri photoURI = Uri.fromFile(photoFile);
+ takePictureIntent.putExtra(
+ MediaStore.EXTRA_OUTPUT, photoURI);
+ startActivityForResult(
+ takePictureIntent, REQUEST_TAKE_PHOTO);
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a marker to a map.
+ *
+ * Adds a marker to a map and stores the user taken photo to the
+ * device's gallery.
+ *
+ * @param requestCode code of the request
+ * @param resultCode code of the result
+ * @param data data of the result
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
+ System.out.println("We have results!");
+
+ mMap.addMarker(new MarkerOptions()
+ .position(new LatLng(hikeLatLng.getLast().latitude,
+ hikeLatLng.getLast().longitude))
+ );
+ hike.addPhotoMapMarker(mCurrentPhotoPath,
+ hikeLatLng.getLast().latitude,
+ hikeLatLng.getLast().longitude);
+ galleryAddPic();
+ }
+ }
+
+ /**
+ * Sets the user taken photo to the gallery of the device.
+ */
+ private void galleryAddPic() {
+ Intent mediaScanIntent = new Intent(
+ Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ File f = new File(mCurrentPhotoPath);
+ Uri contentUri = Uri.fromFile(f);
+ mediaScanIntent.setData(contentUri);
+ this.sendBroadcast(mediaScanIntent);
+ }
+
+ /**
+ * Sets the map attribute to be the map that was got as a parameter.
+ *
+ * @param googleMap map fragment that was readied
+ */
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
}
+ /**
+ * Checks whether the user has the required permissions or not.
+ *
+ * @param requestCode code of the request
+ * @param permissions array of permissions
+ * @param grantResults array of permission results
+ */
@Override
public void onRequestPermissionsResult(int requestCode,
- String permissions[], int[] grantResults) {
- Debug.print(host, "onRequestPermissionResult", "Requesting permission", "Requesting permission", 1);
+ String[] permissions,
+ int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
if (grantResults.length > 0
- && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- try {
- locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 1, locationListener);
- Debug.print(host, "onRequestPermissionResult", "LocationGranted", "LocationManager requesting", 1);
- } catch(SecurityException e) {
- e.printStackTrace();
- }
-
+ && grantResults[0] ==
+ PackageManager.PERMISSION_GRANTED) {
+ startService(locationServiceIntent);
+ Debug.toastThisLonger(host, "Fetching location...");
} else {
- // permission denied, boo! Disable the
- // functionality that depends on this permission.
- // TODO: Go back to MainActivity.
}
+
return;
}
}
}
- private void initLocationListener() {
- Debug.print(host, "initLocationListener", "Initializing location listener", "Location listener init", 1);
- locationListener = new LocationListener() {
- @Override
- public void onLocationChanged(Location location) {
- Debug.print(host, "onLocationChanged", "Changing location", "Location changed", 1);
-
- if (isTrackingLocation) {
- hikeLatLng.add(new LatLng(location.getLatitude(), location.getLongitude()));
-
- hikePolyLine = mMap.addPolyline(new PolylineOptions()
- .clickable(false));
- hikePolyLine.setPoints(hikeLatLng);
-
- if (firstLocationGot == false) {
- mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 15));
- firstLocationGot = true;
- } else {
- mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), mMap.getCameraPosition().zoom));
- }
- }
- }
-
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {}
-
- @Override
- public void onProviderEnabled(String provider) {}
-
- @Override
- public void onProviderDisabled(String provider) {}
- };
- }
-
+ /**
+ * Returns the currently active hike.
+ *
+ * @return returns the currenlty active hike
+ */
public Hike getHike() {
return hike;
}
- public LinkedList getLatLng(Hike hike) {
- LinkedList locs = new LinkedList<>();
+ /**
+ * Receiver for the LocationService's locations.
+ *
+ * Receives location services location changes and adds these locations to
+ * the locations list. Adds a new polyline to the map as well.
+ */
+ private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
+
+ /**
+ * Sets the given locations to a linked list and draws a line according to them.
+ *
+ * @param context
+ * @param intent
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ LinkedList locations =
+ (LinkedList) intent.getExtras().get("Locations");
+
+ if (locationFound == false) {
+ Debug.toastThis(host, "Location fetched, tracking started!");
+ cancelTrackingButton.setEnabled(true);
+ saveTrackingButton.setEnabled(true);
+ takeImageButton.setEnabled(true);
+ locationFound = true;
+ }
+
+ if (isTrackingLocation) {
+ hikeLatLng.add(locations.getLast());
- for (HikeLocation loc: hike.getLocations()) {
- locs.add(new LatLng(loc.getLat(), loc.getLng()));
- }
+ hikePolyLine = mMap.addPolyline(new PolylineOptions()
+ .clickable(false));
+ hikePolyLine.setPoints(hikeLatLng);
- return locs;
- }
+ if (firstLocationGot == false) {
+ mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
+ new LatLng(locations.getLast().latitude,
+ locations.getLast().longitude), 15));
+ firstLocationGot = true;
+ } else {
+ mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
+ new LatLng(locations.getLast().latitude,
+ locations.getLast().longitude),
+ mMap.getCameraPosition().zoom));
+ }
+ }
+ }
+ };
}
diff --git a/app/src/main/java/ville/fi/hikemate/Activities/StoredMapActivity.java b/app/src/main/java/ville/fi/hikemate/Activities/StoredMapActivity.java
index 736e91f..8e06e09 100644
--- a/app/src/main/java/ville/fi/hikemate/Activities/StoredMapActivity.java
+++ b/app/src/main/java/ville/fi/hikemate/Activities/StoredMapActivity.java
@@ -1,14 +1,20 @@
package ville.fi.hikemate.Activities;
+import android.Manifest;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
+import android.support.v4.content.ContextCompat;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
@@ -16,56 +22,210 @@
import ville.fi.hikemate.R;
import ville.fi.hikemate.Resources.Hike;
-import ville.fi.hikemate.Resources.HikeLocation;
+import ville.fi.hikemate.Resources.PhotoMapMarker;
import ville.fi.hikemate.Utils.HikeToLatLng;
+import ville.fi.hikemate.Utils.MarkerInfoWindowAdapter;
import ville.fi.hikemate.Utils.StorageHandler;
-public class StoredMapActivity extends FragmentActivity implements OnMapReadyCallback {
+/**
+ * StoredMapActivity shows the user tracked route and the photo markers.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
+public class StoredMapActivity extends FragmentActivity
+ implements OnMapReadyCallback {
+ /**
+ * Context of the activity.
+ */
private Context host = this;
+
+ /**
+ * This activity.
+ */
+ private FragmentActivity thisActivity = this;
+
+ /**
+ * Map of the view.
+ */
private GoogleMap mMap;
+
+ /**
+ * List of hikes.
+ */
private LinkedList hikes;
+
+ /**
+ * List of hike's location points.
+ */
private LinkedList hike;
+
+ /**
+ * List of hike's photo markers.
+ */
+ private LinkedList photoMapMarkers;
+
+ /**
+ * Handler for the reading and writing of the storage.
+ */
private StorageHandler sh;
+
+ /**
+ * Polyline of the user tracked route.
+ */
private Polyline hikePolyLine;
+ /**
+ * Integer for the writing storage permission.
+ */
+ private final int MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 0;
+
+ /**
+ * Tracks the last clicked marker.
+ */
+ private Marker lastClicked;
+
/**
* Initializes this activity's attributes.
*
- * @param savedInstanceState
+ * @param savedInstanceState bundle for the activity
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stored_map);
- sh = new StorageHandler();
+ if (ContextCompat.checkSelfPermission(thisActivity,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
- SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
- .findFragmentById(R.id.stored_map);
- mapFragment.getMapAsync(this);
+ if (ActivityCompat.shouldShowRequestPermissionRationale(
+ thisActivity,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- hikes = sh.readStorage(host);
- hike = HikeToLatLng.getLatLng(hikes.get((int) getIntent().getExtras().get("position")));
+ } else {
+ ActivityCompat.requestPermissions(thisActivity,
+ new String[]{
+ Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
+ }
+ } else {
+ sh = new StorageHandler();
+
+ SupportMapFragment mapFragment =
+ (SupportMapFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.stored_map);
+ mapFragment.getMapAsync(this);
+
+ hikes = sh.readStorage(host);
+ hike = HikeToLatLng.getLatLng(hikes.get(
+ (int) getIntent().getExtras().get("position")));
+ photoMapMarkers = hikes.get(
+ (int) getIntent().getExtras().get("position"))
+ .getPhotoMapMarkers();
+ }
}
+ /**
+ * Sets the map attribute to be the map that was got as a parameter.
+ *
+ * Draws the polyline according to the user tracked location points to
+ * the map. Adds photo markers to the path. Moves the camera to the
+ * first location point of the hike.
+ *
+ * @param googleMap map fragment that was readied
+ */
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
+ mMap.setInfoWindowAdapter(new MarkerInfoWindowAdapter(this));
hikePolyLine = mMap.addPolyline(new PolylineOptions()
.clickable(false));
hikePolyLine.setPoints(hike);
- mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(hike.get(0).latitude, hike.get(0).longitude), 15));
+
+ if (photoMapMarkers != null) {
+ for (PhotoMapMarker p : photoMapMarkers) {
+ mMap.addMarker(new MarkerOptions()
+ .position(new LatLng(p.getLat(), p.getLon()))
+ .snippet(p.getPath()));
+ }
+ }
+
+ initMarkerClickListener(googleMap);
+
+ mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
+ new LatLng(hike.get(0).latitude, hike.get(0).longitude), 15));
}
- public LinkedList getLatLng(Hike hike) {
- LinkedList locs = new LinkedList<>();
+ /**
+ * Initializes the marker click listener.
+ *
+ * @param map map fragment of the activity
+ */
+ private void initMarkerClickListener(GoogleMap map) {
+ map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
+ @Override
+ public boolean onMarkerClick(Marker marker) {
+ if (lastClicked != null) {
+ lastClicked.hideInfoWindow();
- for (HikeLocation loc: hike.getLocations()) {
- locs.add(new LatLng(loc.getLat(), loc.getLng()));
- }
+ if (lastClicked.equals(marker)) {
+ lastClicked = null;
- return locs;
+ return true;
+ }
+ }
+
+ marker.showInfoWindow();
+ lastClicked = marker;
+
+ mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
+ new LatLng(marker.getPosition().latitude + 0.005,
+ marker.getPosition().longitude),
+ 15));
+
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Reads the external storage and set ups the hike and markers.
+ *
+ * @param requestCode code of the request
+ * @param permissions array of permission
+ * @param grantResults array of results
+ */
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String[] permissions,
+ int[] grantResults) {
+ switch (requestCode) {
+ case MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
+ if (grantResults.length > 0
+ && grantResults[0] ==
+ PackageManager.PERMISSION_GRANTED) {
+ sh = new StorageHandler();
+
+ SupportMapFragment mapFragment =
+ (SupportMapFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.stored_map);
+ mapFragment.getMapAsync(this);
+
+ hikes = sh.readStorage(host);
+ hike = HikeToLatLng.getLatLng(hikes.get((int) getIntent()
+ .getExtras().get("position")));
+ photoMapMarkers = hikes.get((int) getIntent().getExtras()
+ .get("position")).getPhotoMapMarkers();
+ } else {
+
+ }
+
+ return;
+ }
+ }
}
}
diff --git a/app/src/main/java/ville/fi/hikemate/Fragments/HikeListFragment.java b/app/src/main/java/ville/fi/hikemate/Fragments/HikeListFragment.java
index 6a1a3d2..3716ebf 100644
--- a/app/src/main/java/ville/fi/hikemate/Fragments/HikeListFragment.java
+++ b/app/src/main/java/ville/fi/hikemate/Fragments/HikeListFragment.java
@@ -1,68 +1,101 @@
package ville.fi.hikemate.Fragments;
-import android.content.Context;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
import java.util.LinkedList;
-import java.util.List;
import ville.fi.hikemate.Activities.StoredMapActivity;
import ville.fi.hikemate.R;
import ville.fi.hikemate.Resources.Hike;
-import ville.fi.hikemate.Resources.HikeList;
import ville.fi.hikemate.Utils.HikeListAdapter;
import ville.fi.hikemate.Utils.StorageHandler;
+/**
+ * HikeListFragment is a fragment for the list view of the user's hikes.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
public class HikeListFragment extends Fragment {
+
+ /**
+ * List of user saved hikes.
+ */
LinkedList hikes;
- ObjectMapper mapper;
+
+ /**
+ * Text view for the info message.
+ */
private TextView emptyList;
+ /**
+ * Default constructor.
+ */
public HikeListFragment() {
- System.out.println("HikeListFragment");
- }
+ }
+ /**
+ * Sets up the fragment.
+ *
+ * @param savedInstanceState bundle for the fragment
+ */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
+ /**
+ * Sets up the fragment's view.
+ *
+ * * Starts the stored map activity if a list item is clicked.
+ *
+ * @param inflater inflater for the view
+ * @param container oontainer for the view group
+ * @param savedInstanceState bundle for the fragment
+ * @return view of the fragment
+ */
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_hike_list, container, false);
+ View view = inflater.inflate(R.layout.fragment_hike_list, container,
+ false);
emptyList = (TextView) view.findViewById(R.id.list_emptyList);
- ObjectMapper mapper = new ObjectMapper();
StorageHandler sh = new StorageHandler();
hikes = sh.readStorage(getActivity());
+
if (hikes.size() < 1) {
emptyList.setVisibility(View.VISIBLE);
} else {
emptyList.setVisibility(View.GONE);
}
- System.out.println("Hike 0: " + hikes.size());
-
- HikeListAdapter adapter = new HikeListAdapter(getActivity(), R.layout.hike_list_item, hikes);
+ HikeListAdapter adapter = new HikeListAdapter(getActivity(),
+ R.layout.hike_list_item, hikes);
ListView listView = (ListView) view.findViewById(R.id.ListView_hikes);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ /**
+ * Starts an activity on item click.
+ *
+ * @param parent parent of the adapter
+ * @param view view of the adapter
+ * @param position position of the item
+ * @param id id of the item
+ */
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Intent i = new Intent(getActivity(), StoredMapActivity.class);
diff --git a/app/src/main/java/ville/fi/hikemate/Fragments/HikePlansFragment.java b/app/src/main/java/ville/fi/hikemate/Fragments/HikePlansFragment.java
index 2c9adcc..e7a43ea 100644
--- a/app/src/main/java/ville/fi/hikemate/Fragments/HikePlansFragment.java
+++ b/app/src/main/java/ville/fi/hikemate/Fragments/HikePlansFragment.java
@@ -8,23 +8,45 @@
import ville.fi.hikemate.R;
+/**
+ * HikePlansFragment is a fragment for the apps hike plans view.
+ *
+ * Hike plans were planned but are not implemented to the final product.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
public class HikePlansFragment extends Fragment {
+ /**
+ * Default constructor.
+ */
public HikePlansFragment() {
- // Required empty public constructor
- }
+ }
+ /**
+ * Sets up the fragment.
+ *
+ * @param savedInstanceState bundle for the fragment
+ */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
}
+ /**
+ * Sets up the fragment's view.
+ *
+ * @param inflater inflater for the view
+ * @param container container for the view group
+ * @param savedInstanceState bundle for the fragment
+ * @return view of the fragment
+ */
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- // Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_hike_plans, container, false);
}
}
diff --git a/app/src/main/java/ville/fi/hikemate/Fragments/NewHikeFragment.java b/app/src/main/java/ville/fi/hikemate/Fragments/NewHikeFragment.java
index d4751ef..e5fd644 100644
--- a/app/src/main/java/ville/fi/hikemate/Fragments/NewHikeFragment.java
+++ b/app/src/main/java/ville/fi/hikemate/Fragments/NewHikeFragment.java
@@ -1,7 +1,5 @@
package ville.fi.hikemate.Fragments;
-import android.content.Context;
-import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
@@ -10,21 +8,43 @@
import ville.fi.hikemate.R;
+/**
+ * NewHikeFragment is the fragment for the new hike view.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
public class NewHikeFragment extends Fragment {
+ /**
+ * Default constructor.
+ */
public NewHikeFragment() {
- // Required empty public constructor
+
}
+ /**
+ * Sets up the fragment.
+ *
+ * @param savedInstanceState bundle for the fragment
+ */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
+ /**
+ * Sets up the fragment's view.
+ *
+ * @param inflater inflater for the view
+ * @param container container for the view group
+ * @param savedInstanceState bundle for the fragment
+ * @return view of the fragment
+ */
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- // Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_new_hike, container, false);
}
}
diff --git a/app/src/main/java/ville/fi/hikemate/Fragments/ViewPagerAdapter.java b/app/src/main/java/ville/fi/hikemate/Fragments/ViewPagerAdapter.java
index 3232744..f5db3a0 100644
--- a/app/src/main/java/ville/fi/hikemate/Fragments/ViewPagerAdapter.java
+++ b/app/src/main/java/ville/fi/hikemate/Fragments/ViewPagerAdapter.java
@@ -8,33 +8,71 @@
import java.util.List;
/**
- * Created by Ville on 10.4.2017.
+ * ViewPagerAdapter is an adapter for the main activity's tab view.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
*/
-
public class ViewPagerAdapter extends FragmentPagerAdapter {
+ /**
+ * List of tab view's fragments.
+ */
private final List mFragmentList = new ArrayList<>();
+
+ /**
+ * List of tabs' titles.
+ */
private final List mFragmentTitleList = new ArrayList<>();
+ /**
+ * Adapter's constructor.
+ *
+ * @param manager manager for the adapter
+ */
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
+ /**
+ * Returns a fragment in the specific position.
+ *
+ * @param position position of the fragment
+ * @return fragment of the specific position
+ */
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
+ /**
+ * Returns the amount of fragments.
+ *
+ * @return fragment list's size
+ */
@Override
public int getCount() {
return mFragmentList.size();
}
+ /**
+ * Adds a fragment to a list.
+ *
+ * @param fragment fragment to be added
+ * @param title fragment's title
+ */
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
+ /**
+ * Returns the title of the fragment.
+ *
+ * @param position position of the fragment
+ * @return title of the fragment
+ */
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
diff --git a/app/src/main/java/ville/fi/hikemate/Resources/Hike.java b/app/src/main/java/ville/fi/hikemate/Resources/Hike.java
index d8891df..d94a8ca 100644
--- a/app/src/main/java/ville/fi/hikemate/Resources/Hike.java
+++ b/app/src/main/java/ville/fi/hikemate/Resources/Hike.java
@@ -1,55 +1,160 @@
package ville.fi.hikemate.Resources;
-import com.google.android.gms.maps.model.LatLng;
-
import java.util.LinkedList;
-import java.util.List;
/**
- * Created by Ville on 10.4.2017.
+ * Hike is a user tracked hike.
+ *
+ * Hike stores a name (which is actually the hike's date) and the time of the
+ * hike. It also has a list of locations and a photo marker list.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
*/
-
public class Hike {
+ /**
+ * Name/Date of the hike.
+ */
private String name;
+
+ /**
+ * Time of the hike in hours and minutes.
+ */
+ private String time;
+
+ /**
+ * List of hike's locations.
+ */
private LinkedList locations;
+ /**
+ * List of hike's photo markers.
+ */
+ private LinkedList photoMapMarkers;
+
+ /**
+ * Default constructor.
+ */
public Hike() {
}
+ /**
+ * Constructs a hike and sets it a name and inits the lists.
+ *
+ * @param name name of the hike
+ */
public Hike(String name) {
setName(name);
locations = new LinkedList<>();
+ photoMapMarkers = new LinkedList<>();
}
+ /**
+ * Returns the name of the hike.
+ *
+ * @return name of the hike
+ */
public String getName() {
return name;
}
+ /**
+ * Sets the name of the hike.
+ *
+ * @param name name of the hike
+ */
public void setName(String name) {
this.name = name;
}
+ /**
+ * Returns the time of the hike.
+ *
+ * @return time of the hike
+ */
+ public String getTime() {
+ return time;
+ }
+
+ /**
+ * Sets the time of the hike.
+ *
+ * @param time time of the hike
+ */
+ public void setTime(String time) {
+ this.time = time;
+ }
+
+ /**
+ * Adds a new location to the hike's location list.
+ *
+ * @param lat latitude of the location
+ * @param lng longitude of the location
+ */
public void addLocation(double lat, double lng) {
locations.add(new HikeLocation(lat, lng));
}
+ /**
+ * Adds a new photo marker to the hike's photo marker list.
+ *
+ * @param path file path of the photo
+ * @param lat latitude of the marker's location
+ * @param lon longitude of the marker's location
+ */
+ public void addPhotoMapMarker(String path, double lat, double lon) {
+ photoMapMarkers.add(new PhotoMapMarker(path, lat, lon));
+ }
+
+ /**
+ * Returns the hike's photo markers list.
+ *
+ * @return hike's photo markers list.
+ */
+ public LinkedList getPhotoMapMarkers() {
+ return photoMapMarkers;
+ }
+
+ /**
+ * Returns the hike's location list.
+ *
+ * @return hike's locations
+ */
public LinkedList getLocations() {
return locations;
}
+ /**
+ * Returns the hike in a string format.
+ *
+ * @return hike's string format
+ */
@Override
public String toString() {
String hike = "";
hike += "[";
+
for (HikeLocation l : locations) {
hike += l.toString();
+
if (!l.equals(locations.get(locations.size() - 1))) {
hike += ", ";
}
}
+ hike += ", ";
+
+ for (PhotoMapMarker p : photoMapMarkers) {
+ hike += p.toString();
+
+ if (!p.equals(photoMapMarkers.get(photoMapMarkers.size() -1))) {
+ hike += ", ";
+ }
+ }
+
hike += "]";
return hike;
diff --git a/app/src/main/java/ville/fi/hikemate/Resources/HikeList.java b/app/src/main/java/ville/fi/hikemate/Resources/HikeList.java
index 0c730a6..6236ab7 100644
--- a/app/src/main/java/ville/fi/hikemate/Resources/HikeList.java
+++ b/app/src/main/java/ville/fi/hikemate/Resources/HikeList.java
@@ -3,8 +3,15 @@
import java.util.LinkedList;
/**
- * Created by Ville on 10.4.2017.
+ * HikeList is a helper class for listing user's every hike.
+ *
+ * HikeList is unfortunately not used due to the jackson 2
+ * library's restrictions.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
*/
-
public class HikeList extends LinkedList {
+
}
diff --git a/app/src/main/java/ville/fi/hikemate/Resources/HikeLocation.java b/app/src/main/java/ville/fi/hikemate/Resources/HikeLocation.java
index 153be02..9479ee7 100644
--- a/app/src/main/java/ville/fi/hikemate/Resources/HikeLocation.java
+++ b/app/src/main/java/ville/fi/hikemate/Resources/HikeLocation.java
@@ -1,41 +1,84 @@
package ville.fi.hikemate.Resources;
-import com.google.android.gms.maps.model.LatLng;
-
/**
- * Created by Ville on 10.4.2017.
+ * HikeLocation stores the user's location data.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
*/
-
public class HikeLocation {
+ /**
+ * Latitude of the location.
+ */
private double lat;
+
+ /**
+ * Longitude of the location.
+ */
private double lng;
+ /**
+ * Default constructor.
+ */
public HikeLocation() {
}
+ /**
+ * Constructs a new hike location and sets it's attributes.
+ *
+ * @param lat latitude of the location
+ * @param lng longitude of the location
+ */
public HikeLocation(double lat, double lng) {
this.lat = lat;
this.lng = lng;
}
+ /**
+ * Sets the latitude of the location.
+ *
+ * @param lat latitude of the location
+ */
public void setLat(double lat) {
this.lat = lat;
}
+ /**
+ * Sets the longitude of the location.
+ *
+ * @param lng longitude of the location
+ */
public void setLng(double lng) {
this.lng = lng;
}
+ /**
+ * Returns the latitude of the location.
+ *
+ * @return latitude of the location
+ */
public double getLat() {
return lat;
}
- public double getLng() { return lng; }
+ /**
+ * Returns the longitude of the location.
+ *
+ * @return longitude of the location
+ */
+ public double getLng() { return lng; }
+ /**
+ * Returns the location in string format.
+ *
+ * @return the location in string format
+ */
@Override
public String toString() {
- return "{ \"lat\":" + String.valueOf(lat) + ", \"lng\":" + String.valueOf(lng) + " }";
+ return "{ \"lat\":" + String.valueOf(lat) + ", \"lng\":" +
+ String.valueOf(lng) + " }";
}
}
diff --git a/app/src/main/java/ville/fi/hikemate/Resources/PhotoMapMarker.java b/app/src/main/java/ville/fi/hikemate/Resources/PhotoMapMarker.java
new file mode 100644
index 0000000..5819dd9
--- /dev/null
+++ b/app/src/main/java/ville/fi/hikemate/Resources/PhotoMapMarker.java
@@ -0,0 +1,117 @@
+package ville.fi.hikemate.Resources;
+
+/**
+ * PhotoMapMarker is a class that stores data of a map marker.
+ *
+ * Stores the file path of the photo map marker and it's location.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
+public class PhotoMapMarker {
+
+ /**
+ * File path of the photo.
+ */
+ private String path;
+
+ /**
+ * Marker's latitude position.
+ */
+ private double lat;
+
+ /**
+ * Marker's longitude position.
+ */
+ private double lon;
+
+ /**
+ * Default constructor.
+ */
+ public PhotoMapMarker() {
+
+ }
+
+ /**
+ * Constructs a new photo map marker and sets it's attributes.
+ *
+ * @param path file path of the photo
+ * @param latitude latitude of the marker
+ * @param longitude longitude of the marker
+ */
+ public PhotoMapMarker(String path, double latitude, double longitude) {
+ setPath(path);
+ setLat(latitude);
+ setLon(longitude);
+ }
+
+ /**
+ * Returns the photo's file path.
+ *
+ * @return photo's file path
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Sets the photo's file path.
+ *
+ * @param path photo's file path
+ */
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ /**
+ * Returns the latitude of the marker.
+ *
+ * @return latitude of the marker
+ */
+ public double getLat() {
+ return lat;
+ }
+
+ /**
+ * Sets the latitude of the marker.
+ *
+ * @param lat latitude of the marker
+ */
+ public void setLat(double lat) {
+ this.lat = lat;
+ }
+
+ /**
+ * Returns the longitude of the marker.
+ *
+ * @return longitude of the marker
+ */
+ public double getLon() {
+ return lon;
+ }
+
+ /**
+ * Sets the longitude of the marker.
+ *
+ * @param lon longitude of the marker
+ */
+ public void setLon(double lon) {
+ this.lon = lon;
+ }
+
+ /**
+ * Returns the object in string format.
+ *
+ * @return the object in string format
+ */
+ @Override
+ public String toString() {
+ String str = "";
+
+ str += "{";
+
+ return "{ \"path\": " + path + ", \"lat\": " + String.valueOf(lat) +
+ ", \"lng\": " + String.valueOf(lon) + "}";
+ }
+}
diff --git a/app/src/main/java/ville/fi/hikemate/Services/LocationService.java b/app/src/main/java/ville/fi/hikemate/Services/LocationService.java
index e6477d4..5784000 100644
--- a/app/src/main/java/ville/fi/hikemate/Services/LocationService.java
+++ b/app/src/main/java/ville/fi/hikemate/Services/LocationService.java
@@ -1,22 +1,157 @@
package ville.fi.hikemate.Services;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
import android.os.IBinder;
-import android.support.annotation.IntDef;
+import android.support.v4.content.LocalBroadcastManager;
+import com.google.android.gms.maps.model.LatLng;
+
+import java.util.LinkedList;
+
+/**
+ * LocationService tracks the user travelled path.
+ *
+ * LocationService tracks the user travelled path and passes the tracked
+ * locations to the map activity's receiver.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
public class LocationService extends Service {
+
+ /**
+ * Manages the locations.
+ */
+ private LocationManager locationManager;
+
+ /**
+ * Listens to the location changes.
+ */
+ private LocationListener locationListener;
+
+ /**
+ * List of tracked locations.
+ */
+ private LinkedList locations;
+
+ /**
+ * Status of the service (running/not running).
+ */
+ private Boolean isServiceRunning = false;
+
+ /**
+ * Default constructor.
+ */
public LocationService() {
+
}
+ /**
+ * Returns a binder for the service.
+ *
+ * @param intent intent for the service
+ * @return binder for the service
+ */
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
+ /**
+ * Sets up the service.
+ *
+ * Initializes the location manager and location listener and sets the
+ * services status to true (running).
+ *
+ * @param intent
+ * @param flags
+ * @param startId
+ * @return
+ */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
+ if (!isServiceRunning) {
+ isServiceRunning = true;
+
+ locations = new LinkedList<>();
+ locationManager = (LocationManager)
+ getSystemService(Context.LOCATION_SERVICE);
+ initLocationListener();
+
+ try {
+ locationManager.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER,
+ 500, 1, locationListener);
+ } catch(SecurityException e) {
+ e.printStackTrace();
+ }
+ }
+
return START_STICKY;
}
+
+ /**
+ * Sends a location to the broadcast manager.
+ *
+ * @param latitude latitude of the location
+ * @param longitude longitude of the location
+ */
+ private void sendGPSLocations(double latitude, double longitude) {
+ Intent i = new Intent("GPSLocations");
+ locations.add(new LatLng(latitude, longitude));
+ i.putExtra("Locations", locations);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(i);
+ }
+
+ /**
+ * Initializes the service's location listener.
+ *
+ * On location changed sends the new location to the sendGPSLocations
+ * method.
+ */
+ private void initLocationListener() {
+ locationListener = new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ sendGPSLocations(location.getLatitude(),
+ location.getLongitude());
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status,
+ Bundle extras) {}
+
+ @Override
+ public void onProviderEnabled(String provider) {}
+
+ @Override
+ public void onProviderDisabled(String provider) {}
+ };
+ }
+
+ /**
+ * Stops the service.
+ *
+ * Sets the location manager and location listener to null and sets the
+ * service's status to false (not running).
+ *
+ * @param name
+ * @return
+ */
+ @Override
+ public boolean stopService(Intent name) {
+ System.out.println("Stopping LocationService");
+ locationManager = null;
+ locationListener = null;
+ isServiceRunning = false;
+ return super.stopService(name);
+ }
}
diff --git a/app/src/main/java/ville/fi/hikemate/Utils/Debug.java b/app/src/main/java/ville/fi/hikemate/Utils/Debug.java
index f1b34ae..79a56ad 100644
--- a/app/src/main/java/ville/fi/hikemate/Utils/Debug.java
+++ b/app/src/main/java/ville/fi/hikemate/Utils/Debug.java
@@ -5,22 +5,51 @@
import android.widget.Toast;
import ville.fi.hikemate.R;
-
/**
- * Created by Ville on 6.3.2017.
+ * Debug is a helper class for debugging the app.
+ *
+ * Debug toasts debug messages to make the debugging of the app easier.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
*/
public class Debug {
+
+ /**
+ * Debug level.
+ */
private static int DEBUG_LEVEL;
- public static void print(String className, String tag, String message, int level) {
+ /**
+ * Prints a debug message to a log.
+ *
+ * @param className name of the class the message is from
+ * @param tag tag of the message
+ * @param message message of the debug message
+ * @param level level of the message
+ */
+ public static void print(String className, String tag, String message,
+ int level) {
if (level <= DEBUG_LEVEL) {
String logTag = className + "." + tag;
Log.d(logTag, message);
}
}
- public static void print(Context host, String className, String tag, String message, int level) {
+ /**
+ * Prints a more detailed debug message to a log.
+ *
+ * @param host host of the class the message is from
+ * @param className name of the class the message is from
+ * @param tag tag of the message
+ * @param message message of the debug message
+ * @param level level of the message
+ */
+ public static void print(Context host, String className, String tag,
+ String message, int level) {
Log.d("print", "Printing");
+
if (level <= DEBUG_LEVEL) {
String logTag = className + "." + tag;
Log.d(logTag, message);
@@ -28,14 +57,45 @@ public static void print(Context host, String className, String tag, String mess
}
}
+ /**
+ * Sets up the debug level.
+ *
+ * @param host host of the request
+ */
public static void loadDebug(Context host) {
- DEBUG_LEVEL = Integer.parseInt(host.getResources().getString(R.string.debugLevel));
+ DEBUG_LEVEL = Integer.parseInt(host.getResources()
+ .getString(R.string.debugLevel));
print("This", "Level", String.valueOf(DEBUG_LEVEL), 1);
}
+ /**
+ * Shows a toast of the debug message.
+ *
+ * @param host host of the request
+ * @param message message of the debug message
+ */
private static void showToast(Context host, String message) {
Toast toast = Toast.makeText(host, message, Toast.LENGTH_SHORT);
toast.show();
}
-}
+ /**
+ * Toasts a string.
+ *
+ * @param host host of the request
+ * @param message message of the toast
+ */
+ public static void toastThis(Context host, String message) {
+ Toast.makeText(host, message, Toast.LENGTH_SHORT).show();
+ }
+
+ /**
+ * Toasts a string longer.
+ *
+ * @param host host of the request
+ * @param message message of the toast
+ */
+ public static void toastThisLonger(Context host, String message) {
+ Toast.makeText(host, message, Toast.LENGTH_LONG).show();
+ }
+}
diff --git a/app/src/main/java/ville/fi/hikemate/Utils/HikeListAdapter.java b/app/src/main/java/ville/fi/hikemate/Utils/HikeListAdapter.java
index 88a1a73..06612ca 100644
--- a/app/src/main/java/ville/fi/hikemate/Utils/HikeListAdapter.java
+++ b/app/src/main/java/ville/fi/hikemate/Utils/HikeListAdapter.java
@@ -2,6 +2,7 @@
import android.app.Activity;
import android.content.Context;
+import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
@@ -16,23 +17,55 @@
import ville.fi.hikemate.Resources.Hike;
/**
- * Created by Ville on 10.4.2017.
+ * HikeListAdapter is an adapter for the hike list view.
+ *
+ * HikeListAdapter sets a custom view for the hike list.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
*/
-
public class HikeListAdapter extends ArrayAdapter {
+ /**
+ * Host of the adapter.
+ */
private Context host;
+
+ /**
+ * List of the user's hikes.
+ */
private LinkedList hikes;
- public HikeListAdapter(Context host, int resource, LinkedList hikeList) {
+ /**
+ * Constructs a new hike list adapter and sets it's attributes.
+ *
+ * @param host host of the adapter
+ * @param resource resource of the adapter
+ * @param hikeList list of user saved hikes
+ */
+ public HikeListAdapter(Context host, int resource,
+ LinkedList hikeList) {
super(host, resource, hikeList);
this.host = host;
this.hikes = hikeList;
}
+ /**
+ * Returns a view for the hike list.
+ *
+ * Sets the background color of every odd row to a light purple
+ * and sets the title and subtitle of the hike accordingly.
+ *
+ * @param position position of an item on the list
+ * @param convertView convert view of the list
+ * @param parent parent of the list
+ * @return returns a view for the list
+ */
@NonNull
@Override
- public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+ public View getView(int position, @Nullable View convertView,
+ @NonNull ViewGroup parent) {
View view = convertView;
HikeHolder hh;
@@ -41,7 +74,13 @@ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup
LayoutInflater inflater = ((Activity) host).getLayoutInflater();
view = inflater.inflate(R.layout.hike_list_item, parent, false);
- hh.listTitle = (TextView) view.findViewById(R.id.list_item);
+ if (position % 2 == 1) {
+ view.setBackgroundColor(Color.parseColor("#D1C4E9"));
+ }
+
+ hh.listTitle = (TextView) view.findViewById(R.id.list_item_title);
+ hh.listSubtitle = (TextView) view.findViewById(
+ R.id.list_item_subtitle);
view.setTag(hh);
} else {
hh = (HikeHolder) view.getTag();
@@ -49,11 +88,28 @@ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup
Hike hike = hikes.get(position);
hh.listTitle.setText(hike.getName());
+ hh.listSubtitle.setText(hike.getTime());
return view;
}
- class HikeHolder {
+ /**
+ * HikeHolder is a helper class to hold hike's list view data.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
+ public class HikeHolder {
+
+ /**
+ * List item's title.
+ */
public TextView listTitle;
+
+ /**
+ * List item's subtitle.
+ */
+ public TextView listSubtitle;
}
}
diff --git a/app/src/main/java/ville/fi/hikemate/Utils/HikeToLatLng.java b/app/src/main/java/ville/fi/hikemate/Utils/HikeToLatLng.java
index e654e2a..bbaa68a 100644
--- a/app/src/main/java/ville/fi/hikemate/Utils/HikeToLatLng.java
+++ b/app/src/main/java/ville/fi/hikemate/Utils/HikeToLatLng.java
@@ -8,11 +8,20 @@
import ville.fi.hikemate.Resources.HikeLocation;
/**
- * Created by Ville on 25.4.2017.
+ * HikeToLatLng transfers a hike's location list to a LatLng list.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
*/
-
public class HikeToLatLng {
+ /**
+ * Returns a LatLng list of hike's locations.
+ *
+ * @param hike hike to transfer
+ * @return latlng list of hike's locations
+ */
public static LinkedList getLatLng(Hike hike) {
LinkedList locs = new LinkedList<>();
diff --git a/app/src/main/java/ville/fi/hikemate/Utils/MarkerInfoWindowAdapter.java b/app/src/main/java/ville/fi/hikemate/Utils/MarkerInfoWindowAdapter.java
new file mode 100644
index 0000000..17e608a
--- /dev/null
+++ b/app/src/main/java/ville/fi/hikemate/Utils/MarkerInfoWindowAdapter.java
@@ -0,0 +1,102 @@
+package ville.fi.hikemate.Utils;
+
+import android.support.v4.app.FragmentActivity;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.model.Marker;
+import com.nostra13.universalimageloader.core.DisplayImageOptions;
+import com.nostra13.universalimageloader.core.ImageLoader;
+import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
+
+import ville.fi.hikemate.R;
+
+/**
+ * MarkerInfoWindowAdapter is a custom adapter for the photo markers.
+ *
+ * MarkerInfoWindowAdapter sets the info window to contain only the photo
+ * of the specific map marker.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
+ */
+public class MarkerInfoWindowAdapter implements GoogleMap.InfoWindowAdapter {
+
+ /**
+ * View of the info window.
+ */
+ private View mInfoWindow;
+
+ /**
+ * Info window's contents.
+ */
+ private View mInfoContents;
+
+ /**
+ * Image loader for the photo.
+ */
+ private ImageLoader imageLoader;
+
+ /**
+ * Configuration of the image loader.
+ */
+ private ImageLoaderConfiguration configuration;
+
+ /**
+ * Options for the photo.
+ */
+ private DisplayImageOptions options;
+
+ /**
+ * Constructs a new marker info window adapter and sets it's attributes.
+ *
+ * @param host host of the adapter
+ */
+ public MarkerInfoWindowAdapter(FragmentActivity host) {
+ mInfoWindow = host.getLayoutInflater().inflate(
+ R.layout.marker_info_window, null);
+ mInfoContents = host.getLayoutInflater().inflate(
+ R.layout.marker_info_window_contents, null);
+ configuration = new ImageLoaderConfiguration.Builder(host).build();
+ ImageLoader.getInstance().init(configuration);
+ imageLoader = ImageLoader.getInstance();
+ options = new DisplayImageOptions.Builder().build();
+ }
+
+ /**
+ * Returns the view of the info window.
+ *
+ * @param marker info window's marker
+ * @return the view of the info window
+ */
+ @Override
+ public View getInfoWindow(Marker marker) {
+ render(marker, mInfoWindow);
+ return mInfoWindow;
+ }
+
+ /**
+ * Returns the view of the contents of the info window.
+ *
+ * @param marker info window's marker
+ * @return the view of the info window's contents
+ */
+ @Override
+ public View getInfoContents(Marker marker) {
+ render(marker, mInfoContents);
+ return mInfoContents;
+ }
+
+ /**
+ * Renders the image of the photo marker.
+ *
+ * @param marker photo's marker
+ * @param view marker's view
+ */
+ private void render(final Marker marker, View view) {
+ ImageView image = (ImageView) view.findViewById(R.id.marker_photo);
+ imageLoader.displayImage("file:///" + marker.getSnippet(), image, options);
+ }
+}
diff --git a/app/src/main/java/ville/fi/hikemate/Utils/StorageHandler.java b/app/src/main/java/ville/fi/hikemate/Utils/StorageHandler.java
index 60fc2dc..3cd326b 100644
--- a/app/src/main/java/ville/fi/hikemate/Utils/StorageHandler.java
+++ b/app/src/main/java/ville/fi/hikemate/Utils/StorageHandler.java
@@ -13,19 +13,33 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
-import java.util.List;
import ville.fi.hikemate.Resources.Hike;
-import ville.fi.hikemate.Resources.HikeList;
/**
- * Created by Ville on 10.4.2017.
+ * StorageHandler handles the external storage operations.
+ *
+ * @author Ville Haapavaara
+ * @version 10.5.2017
+ * @since 1.8
*/
-
public class StorageHandler {
+ /**
+ * Mapper for the reading and writing.
+ */
ObjectMapper mapper = new ObjectMapper();
+ /**
+ * Returns a list of hikes.
+ *
+ * Tries to read a 'hikes.txt' named file to a string. The mapper then
+ * converts this string to a linked list object that's passed to the
+ * caller.
+ *
+ * @param host host of the request
+ * @return a list of hikes
+ */
public LinkedList readStorage(Context host) {
StringBuilder sb = new StringBuilder();
@@ -35,11 +49,10 @@ public LinkedList readStorage(Context host) {
BufferedReader bufferedReader = new BufferedReader(isr);
String line;
+
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
-
- System.out.println("Read success.");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
@@ -49,7 +62,8 @@ public LinkedList readStorage(Context host) {
LinkedList hikes = new LinkedList<>();
try {
- hikes = mapper.readValue(sb.toString(), new TypeReference>(){});
+ hikes = mapper.readValue(sb.toString(),
+ new TypeReference>(){});
} catch (IOException e) {
e.printStackTrace();
}
@@ -57,16 +71,25 @@ public LinkedList readStorage(Context host) {
return hikes;
}
+ /**
+ * Writes a list of hikes to a file.
+ *
+ * Converts a list of hikes to a string and then writes them to a
+ * 'hikes.txt' file.
+ *
+ * @param host host of the request
+ * @param hikes hikes to be saved
+ */
public void writeStorage(Context host, LinkedList hikes) {
String json = "";
ObjectMapper mapper = new ObjectMapper();
try {
json += mapper.writeValueAsString(hikes);
- FileOutputStream outputStream = host.openFileOutput("hikes.txt", Context.MODE_PRIVATE);
+ FileOutputStream outputStream =
+ host.openFileOutput("hikes.txt", Context.MODE_PRIVATE);
outputStream.write(json.getBytes());
outputStream.close();
- System.out.println("Write success.");
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
diff --git a/app/src/main/res/drawable/custom_button.xml b/app/src/main/res/drawable/custom_button.xml
new file mode 100644
index 0000000..f92b840
--- /dev/null
+++ b/app/src/main/res/drawable/custom_button.xml
@@ -0,0 +1,40 @@
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_map.xml b/app/src/main/res/layout/activity_map.xml
index 627c30b..59c7090 100644
--- a/app/src/main/res/layout/activity_map.xml
+++ b/app/src/main/res/layout/activity_map.xml
@@ -7,24 +7,58 @@
+ android:layout_above="@+id/buttons_cancelAndSave"
+ android:onClick="takeImage"
+ android:background="@drawable/custom_button"
+ android:text="Take Photo"
+ android:textColor="@color/colorPrimaryWhiteText"
+ android:layout_marginTop="4dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginLeft="8dp"/>
-
+ android:id="@+id/buttons_cancelAndSave"
+ android:layout_alignParentBottom="true">
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_stored_map.xml b/app/src/main/res/layout/activity_stored_map.xml
index b8a0c37..c894b21 100644
--- a/app/src/main/res/layout/activity_stored_map.xml
+++ b/app/src/main/res/layout/activity_stored_map.xml
@@ -5,16 +5,9 @@
android:layout_height="match_parent"
tools:context=".Activities.MapActivity">
-
-
diff --git a/app/src/main/res/layout/fragment_hike_list.xml b/app/src/main/res/layout/fragment_hike_list.xml
index 313b973..3f83b3f 100644
--- a/app/src/main/res/layout/fragment_hike_list.xml
+++ b/app/src/main/res/layout/fragment_hike_list.xml
@@ -4,28 +4,20 @@
android:layout_height="match_parent"
tools:context="ville.fi.hikemate.Fragments.HikeListFragment">
-
-
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="4dp"
+ android:layout_marginTop="6dp"
+ android:text="You dont have any hikes. You can start tracking hikes in the 'NEW HIKE' tab."
+ android:textSize="13sp" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_new_hike.xml b/app/src/main/res/layout/fragment_new_hike.xml
index 81c33ad..481e902 100644
--- a/app/src/main/res/layout/fragment_new_hike.xml
+++ b/app/src/main/res/layout/fragment_new_hike.xml
@@ -4,11 +4,30 @@
android:layout_height="match_parent"
tools:context="ville.fi.hikemate.Fragments.NewHikeFragment">
+
-
+
+
diff --git a/app/src/main/res/layout/hike_list_item.xml b/app/src/main/res/layout/hike_list_item.xml
index f712ed0..192d98a 100644
--- a/app/src/main/res/layout/hike_list_item.xml
+++ b/app/src/main/res/layout/hike_list_item.xml
@@ -1,14 +1,25 @@
-
+
+ android:textSize="13sp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="2dp"
+ android:id="@+id/list_item_title"/>
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/marker_info_window.xml b/app/src/main/res/layout/marker_info_window.xml
new file mode 100644
index 0000000..0441bb9
--- /dev/null
+++ b/app/src/main/res/layout/marker_info_window.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/marker_info_window_contents.xml b/app/src/main/res/layout/marker_info_window_contents.xml
new file mode 100644
index 0000000..c83e7ad
--- /dev/null
+++ b/app/src/main/res/layout/marker_info_window_contents.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 3ab3e9c..9579194 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,6 +1,12 @@
- #3F51B5
- #303F9F
- #FF4081
+ #673AB7
+ #512DA8
+ #CDDC39
+ #212121
+ #757575
+ #212121
+ #FAFAFA
+ #26000000
+ #12000000
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index b97380e..7ac06db 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -8,6 +8,7 @@
- @color/colorPrimary
- @color/colorPrimaryDark
- @color/colorAccent
+ - @color/colorPrimaryText