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