/* * Copyright 2014 Artem Chikin * Copyright 2014 Artem Herasymchuk * Copyright 2014 Tom Krywitsky * Copyright 2014 Henry Pabst * Copyright 2014 Bradley Simons * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ca.ualberta.cmput301w14t08.geochan.fragments; import java.util.ArrayList; import org.osmdroid.api.IGeoPoint; import org.osmdroid.bonuspack.overlays.MapEventsOverlay; import org.osmdroid.bonuspack.overlays.MapEventsReceiver; import org.osmdroid.bonuspack.overlays.Marker; import org.osmdroid.bonuspack.overlays.Marker.OnMarkerClickListener; import org.osmdroid.bonuspack.overlays.Marker.OnMarkerDragListener; import org.osmdroid.views.MapView; import android.app.ProgressDialog; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; import ca.ualberta.cmput301w14t08.geochan.R; import ca.ualberta.cmput301w14t08.geochan.adapters.CustomLocationAdapter; import ca.ualberta.cmput301w14t08.geochan.helpers.ErrorDialog; import ca.ualberta.cmput301w14t08.geochan.helpers.LocationListenerService; import ca.ualberta.cmput301w14t08.geochan.helpers.MapDataHelper; import ca.ualberta.cmput301w14t08.geochan.helpers.SortUtil; import ca.ualberta.cmput301w14t08.geochan.managers.ThreadManager; import ca.ualberta.cmput301w14t08.geochan.models.CustomMarker; import ca.ualberta.cmput301w14t08.geochan.models.GeoLocation; import ca.ualberta.cmput301w14t08.geochan.models.GeoLocationLog; /** * This class is a fragment which allows the user to specify a custom location * for their post. It gives the user the ability to select a location on a map, * pick from previously used locations, or simply use the user's current * location. * * @author Brad Simons * */ public class CustomLocationFragment extends Fragment { private int postType; private FragmentManager fragManager; private LocationListenerService locationListenerService; private GeoLocation newLocation; private MapDataHelper mapData; private MapEventsOverlay mapEventsOverlay; private ArrayList<Marker> markers; private CustomMarker currentLocationMarker; // flags for type of post that initiated this fragment public static final int POST = 1; public static final int REPLY = 3; public static final int SORT_THREAD = 4; public static final int SORT_COMMENT = 5; public static final int EDIT = 6; /** * Inflates the custom location fragment view */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHasOptionsMenu(false); return inflater.inflate(R.layout.fragment_custom_location, container, false); } /** * Inflates the menu and adds any action bar items that are present. */ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { MenuItem item = menu.findItem(R.id.action_settings); item.setVisible(true); super.onCreateOptionsMenu(menu, inflater); } /** * Setups up the Location Log to display previous locations, connects the UI * buttons, starts listening for location updates and finally calls setup * map. Gets the proper fragment manager and sets it to fragManager. * */ public void onStart() { super.onStart(); GeoLocationLog log = GeoLocationLog.generateInstance(getActivity()); ArrayList<GeoLocation> logArray = log.getLogEntries(); FavouritesFragment favFrag = (FavouritesFragment) getFragmentManager() .findFragmentByTag("favouritesFrag"); if (favFrag != null) { fragManager = getChildFragmentManager(); } else { fragManager = getFragmentManager(); } ListView lv = (ListView) getView().findViewById( R.id.custom_location_list_view); locationListenerService = new LocationListenerService(getActivity()); locationListenerService.startListening(); markers = new ArrayList<Marker>(); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // clicks a previous location item in the list GeoLocation logEntry = (GeoLocation) parent .getItemAtPosition(position); setBundleArguments(logEntry, "PREVIOUS_LOCATION"); fragManager.popBackStackImmediate(); } }); CustomLocationAdapter customLocationAdapter = new CustomLocationAdapter( getActivity(), logArray); lv.setAdapter(customLocationAdapter); setupMap(getMapEventsReceiver()); mapData.refreshMap(); } /** * Calls onStop in the super class and tells the locationListenerService to * stop listening for location updates */ @Override public void onStop() { super.onStop(); locationListenerService.stopListening(); } /** * Sets up the map view, gets the current location and initiates a new * marker object from it, if valid. Then plots it on the map. * * @param mapEventsReceiver * The MapEventsReceiver for handling click events on the map. */ private void setupMap(MapEventsReceiver mapEventsReceiver) { mapData = new MapDataHelper((MapView) getActivity().findViewById( R.id.map_view)); mapData.setUpMap(); mapEventsOverlay = new MapEventsOverlay(getActivity(), mapEventsReceiver); mapData.addToOverlays(mapEventsOverlay); GeoLocation currentLocation = new GeoLocation(locationListenerService); markers = new ArrayList<Marker>(); // if valid current location, put it on the map if (currentLocation.getLocation() != null) { Drawable icon = getResources().getDrawable( R.drawable.current_location_pin); currentLocationMarker = new CustomMarker(currentLocation, mapData.getMap(), icon); currentLocationMarker.setUpInfoWindow("Current Location", getActivity()); markers.add(currentLocationMarker); setMarkerListeners(currentLocationMarker); mapData.addMarkerToOverlayAndCenter(currentLocationMarker, 13); } else { ErrorDialog.show(getActivity(), "Could not retrieve current location"); mapData.getController().setZoom(3); } } /** * Returns a MapEventsReceiver object which is used to handle click events * on the map. Only the long click is implemented here, for setting a custom * location pin * * @return mapEventsReceiver A MapEventsReceiver object set up for use in * our MapViewFragment. */ private MapEventsReceiver getMapEventsReceiver() { MapEventsReceiver mapEventsReceiver = new MapEventsReceiver() { /** * Called on a single tap */ @Override public boolean singleTapUpHelper(IGeoPoint clickedPoint) { return false; } /** * Called on a long press on the map. A location marker is created * and placed on the map where the user clicked */ @Override public boolean longPressHelper(IGeoPoint clickedPoint) { newLocation = new GeoLocation(clickedPoint.getLatitude(), clickedPoint.getLongitude()); handleNewLocationPressed(newLocation); return false; } }; return mapEventsReceiver; } /** * Sets up listeners for the marker passed in. First sets up the * onMarkerClick listener, which hides all infoWindows and then shows the * infoWindow of the marker that was clicked. Second, if the marker is * draggable, * * @param locationMarker * The CustomMarker to have listeners set up for. */ private void setMarkerListeners(CustomMarker locationMarker) { locationMarker.setOnMarkerClickListener(new OnMarkerClickListener() { /** * called if marker is clicked, hides all other infoWindows */ @Override public boolean onMarkerClick(Marker marker, MapView map) { if (marker.isInfoWindowShown() != true) { hideInfoWindows(); marker.showInfoWindow(); } else { hideInfoWindows(); } return false; } }); if (locationMarker.isDraggable()) { locationMarker.setOnMarkerDragListener(new OnMarkerDragListener() { /** * Called as the marker is being dragged, no implementation * * @param marker * that was dragged */ @Override public void onMarkerDrag(Marker marker) { } /** * Called when the onDragListen action is complete Updates the * location and POI when the drag is finished * * @param marker * that was dragged */ @Override public void onMarkerDragEnd(Marker marker) { GeoLocation geoLocation = new GeoLocation(marker .getPosition().getLatitude(), marker.getPosition() .getLongitude()); ProgressDialog dialog = new ProgressDialog(getActivity()); dialog.setMessage("Retrieving Location"); ThreadManager.startGetPOI(geoLocation, dialog, marker); } /** * Called when the drag operation begins. No implementation at * this time * * @param marker * that was dragged */ @Override public void onMarkerDragStart(Marker marker) { hideInfoWindows(); } }); } } /** * Creates a marker object by taking in latitude and longitude values and * sets its position on the map view. Also adds a listener to the Marker for * dragging the marker around the map * * @param geoLocation * A GeoLocation representing the point that was pressed on the * map. */ private void handleNewLocationPressed(GeoLocation geoLocation) { hideInfoWindows(); Drawable icon = getResources().getDrawable(R.drawable.red_map_pin); CustomMarker newLocationMarker = new CustomMarker(geoLocation, mapData.getMap(), icon); newLocationMarker.setUpInfoWindow("New Location", getActivity()); newLocationMarker.setDraggable(true); setMarkerListeners(newLocationMarker); markers.clear(); markers.add(newLocationMarker); mapData.clearOverlays(); mapData.addToOverlays(mapEventsOverlay); mapData.addToOverlays(newLocationMarker); if (currentLocationMarker != null) { mapData.addToOverlays(currentLocationMarker); markers.add(currentLocationMarker); } mapData.refreshMap(); } /** * Iterates through all markers on the map and hides their infoWindows. */ private void hideInfoWindows() { for (Marker marker : markers) { marker.hideInfoWindow(); } } /** * Called when a user clicks the current location button. Gets the user's * current location, puts it in a bundle and passes it back to the previous * fragment * * @param view * A View for the Button that was pressed. */ public void submitCurrentLocation(View view) { GeoLocation currentGeoLocation = new GeoLocation( locationListenerService); if (currentGeoLocation.getLocation() == null) { ErrorDialog.show(getActivity(), "Could not obtain location"); } else { setBundleArguments(currentGeoLocation, "CURRENT_LOCATION"); } fragManager.popBackStackImmediate(); } /** * Called when a user clicks the submit button. If the user has placed a * location marker on the map, that location is placed in a bundle and * passed back to the previous fragment * * @param view * A View for the Button that was pressed. * */ public void submitNewLocation(View view) { if (newLocation == null) { ErrorDialog.show(getActivity(), "Please select a location on the map"); } else { setBundleArguments(newLocation, "NEW_LOCATION"); fragManager.popBackStackImmediate(); } } /** * Bundles up all arguments required to be passed to previous fragment. This * depends on the post type. It will attach latitude, longitude and * description of the location to be submitted, as well as the type of * location being returned (current location of user or a new location set * on the map) * * @param locationType * A string representing the location type that is being * submitted. */ public void setBundleArguments(GeoLocation locationToSubmit, String locationType) { Bundle bundle = getArguments(); postType = bundle.getInt("postType"); if (postType == POST) { PostFragment fragment = (PostFragment) getFragmentManager() .findFragmentByTag("postFrag"); if (fragment == null) { fragment = (PostFragment) getChildFragmentManager() .findFragmentByTag("postFrag"); } Bundle args = fragment.getArguments(); args.putDouble("LATITUDE", locationToSubmit.getLatitude()); args.putDouble("LONGITUDE", locationToSubmit.getLongitude()); args.putString("LocationType", locationType); args.putString("locationDescription", locationToSubmit.getLocationDescription()); } else if (postType == SORT_THREAD) { SortUtil.setThreadSortGeo(locationToSubmit); } else if (postType == SORT_COMMENT) { SortUtil.setCommentSortGeo(locationToSubmit); } else if (postType == EDIT) { EditFragment fragment = (EditFragment) fragManager .findFragmentByTag("editFrag"); Bundle args = fragment.getArguments(); args.putDouble("LATITUDE", locationToSubmit.getLatitude()); args.putDouble("LONGITUDE", locationToSubmit.getLongitude()); args.putString("LocationType", locationType); args.putString("locationDescription", locationToSubmit.getLocationDescription()); } } }