ca.ualberta.cmput301w14t08.geochan.fragments.CustomLocationFragment.java Source code

Java tutorial

Introduction

Here is the source code for ca.ualberta.cmput301w14t08.geochan.fragments.CustomLocationFragment.java

Source

/*
 * 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());
        }
    }
}