Android Open Source - maps-app-android Map Fragment






From Project

Back to project page maps-app-android.

License

The source code is released under:

Apache License - 2.0 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by...

If you think the Android project maps-app-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/* Copyright 1995-2014 Esri
 */*from   ww  w.j  ava2  s . c  om*/
 * 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.
 *
 * For additional information, contact:
 * Environmental Systems Research Institute, Inc.
 * Attn: Contracts Dept
 * 380 New York Street
 * Redlands, California, USA 92373
 *
 * email: contracts@esri.com
 *
 */

package com.esri.android.mapsapp;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.TextView;
import android.widget.Toast;

import com.esri.android.map.GraphicsLayer;
import com.esri.android.map.LocationDisplayManager;
import com.esri.android.map.LocationDisplayManager.AutoPanMode;
import com.esri.android.map.MapOnTouchListener;
import com.esri.android.map.MapView;
import com.esri.android.map.event.OnPinchListener;
import com.esri.android.map.event.OnStatusChangedListener;
import com.esri.android.mapsapp.account.AccountManager;
import com.esri.android.mapsapp.basemaps.BasemapsDialogFragment.BasemapsDialogListener;
import com.esri.android.mapsapp.dialogs.ProgressDialogFragment;
import com.esri.android.mapsapp.location.DirectionsDialogFragment;
import com.esri.android.mapsapp.location.DirectionsDialogFragment.DirectionsDialogListener;
import com.esri.android.mapsapp.location.RoutingDialogFragment;
import com.esri.android.mapsapp.location.RoutingDialogFragment.RoutingDialogListener;
import com.esri.android.mapsapp.tools.Compass;
import com.esri.android.mapsapp.util.TaskExecutor;
import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.LinearUnit;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.Polyline;
import com.esri.core.geometry.SpatialReference;
import com.esri.core.geometry.Unit;
import com.esri.core.map.Graphic;
import com.esri.core.portal.BaseMap;
import com.esri.core.portal.Portal;
import com.esri.core.portal.WebMap;
import com.esri.core.symbol.PictureMarkerSymbol;
import com.esri.core.symbol.SimpleLineSymbol;
import com.esri.core.symbol.SimpleLineSymbol.STYLE;
import com.esri.core.tasks.geocode.Locator;
import com.esri.core.tasks.geocode.LocatorFindParameters;
import com.esri.core.tasks.geocode.LocatorGeocodeResult;
import com.esri.core.tasks.geocode.LocatorReverseGeocodeResult;
import com.esri.core.tasks.na.NAFeaturesAsFeature;
import com.esri.core.tasks.na.Route;
import com.esri.core.tasks.na.RouteDirection;
import com.esri.core.tasks.na.RouteParameters;
import com.esri.core.tasks.na.RouteResult;
import com.esri.core.tasks.na.RouteTask;
import com.esri.core.tasks.na.StopGraphic;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;

/**
 * Implements the view that shows the map.
 */
public class MapFragment extends Fragment implements BasemapsDialogListener,
    RoutingDialogListener, OnCancelListener {
  public static final String TAG = MapFragment.class.getSimpleName();

  private static final String KEY_PORTAL_ITEM_ID = "KEY_PORTAL_ITEM_ID";

  private static final String KEY_BASEMAP_ITEM_ID = "KEY_BASEMAP_ITEM_ID";

  private static final String KEY_IS_LOCATION_TRACKING = "IsLocationTracking";

  private static final int REQUEST_CODE_PROGRESS_DIALOG = 1;

  private static final String SEARCH_HINT = "Search";

  private static FrameLayout.LayoutParams mlayoutParams;

  // Margins parameters for search view
  private static int TOP_MARGIN_SEARCH = 55;

  private static int LEFT_MARGIN_SEARCH = 15;

  private static int RIGHT_MARGIN_SEARCH = 15;

  private static int BOTTOM_MARGIN_SEARCH = 0;

  // Margin parameters for compass
  private static int TOP_MARGIN_COMPASS = 15;

  private static int LEFT_MARGIN_COMPASS = 0;

  private static int BOTTOM_MARGIN_COMPASS = 0;

  private static int RIGHT_MARGIN_COMPASS = 0;

  // Height and Width for the compass image
  private static int HEIGHT = 140;

  private static int WIDTH = 140;

  // The circle area specified by search_radius and input lat/lon serves
  // searching purpose.
  // It is also used to construct the extent which map zooms to after the
  // first
  // GPS fix is retrieved.
  private final static double SEARCH_RADIUS = 10;

  private String mPortalItemId;

  private String mBasemapPortalItemId;

  private FrameLayout mMapContainer;

  public static MapView mMapView;

  private String mMapViewState;

  // GPS location tracking
  private boolean mIsLocationTracking;

  private Point mLocation = null;

  // Graphics layer to show geocode and reverse geocode results
  private GraphicsLayer mLocationLayer;

  private Point mLocationLayerPoint;

  private String mLocationLayerPointString;

  // Graphics layer to show routes
  private GraphicsLayer mRouteLayer;

  private List<RouteDirection> mRoutingDirections;

  // Spatial references used for projecting points
  private final SpatialReference mWm = SpatialReference.create(102100);

  private final SpatialReference mEgs = SpatialReference.create(4326);

  Compass mCompass;

  LayoutParams compassFrameParams;

  private MotionEvent mLongPressEvent;

  @SuppressWarnings("rawtypes")
  // - using this only to cancel pending tasks in a generic way
  private AsyncTask mPendingTask;

  private View mSearchBox;

  private View mSearchResult;

  private LayoutInflater mInflater;

  private String mStartLocation, mEndLocation;

  int width, height;

  LayoutParams gpsFrameParams;

  public static MapFragment newInstance(String portalItemId,
      String basemapPortalItemId) {
    MapFragment mapFragment = new MapFragment();

    Bundle args = new Bundle();
    args.putString(KEY_PORTAL_ITEM_ID, portalItemId);
    args.putString(KEY_BASEMAP_ITEM_ID, basemapPortalItemId);

    mapFragment.setArguments(args);
    return mapFragment;
  }

  public MapFragment() {
    // make MapFragment ctor private - use newInstance() instead
  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setHasOptionsMenu(true);

    Bundle args = savedInstanceState != null ? savedInstanceState
        : getArguments();
    if (args != null) {
      mIsLocationTracking = args.getBoolean(KEY_IS_LOCATION_TRACKING);
      mPortalItemId = args.getString(KEY_PORTAL_ITEM_ID);
      mBasemapPortalItemId = args.getString(KEY_BASEMAP_ITEM_ID);
    }

  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {

    mMapContainer = (FrameLayout) inflater.inflate(
        R.layout.map_fragment_layout, null);

    if (mPortalItemId != null) {
      // load the WebMap
      loadWebMapIntoMapView(mPortalItemId, mBasemapPortalItemId,
          AccountManager.getInstance().getPortal());
    } else {
      if (mBasemapPortalItemId != null) {
        // show a map with the basemap represented by
        // mBasemapPortalItemId
        loadWebMapIntoMapView(mBasemapPortalItemId, null,
            AccountManager.getInstance().getAGOLPortal());
      } else {
        // show the default map
        String defaultBaseMapURL = getString(R.string.default_basemap_url);
        MapView mapView = new MapView(getActivity(), defaultBaseMapURL,
            "", "");

        // Set the MapView to allow the user to rotate the map when as
        // part of a pinch gesture.
        // mapView.setAllowRotationByPinch(true);

        setMapView(mapView);

        mapView.zoomin();

      }
    }

    return mMapContainer;
  }

  @Override
  public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);

    // Inflate the menu items for use in the action bar
    inflater.inflate(R.menu.action, menu);

  }

  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {

    case R.id.location:
      // Toggle location tracking on or off
      if (mIsLocationTracking) {
        item.setIcon(R.drawable.ic_action_compass_mode);
        mMapView.getLocationDisplayManager().setAutoPanMode(
            AutoPanMode.COMPASS);
        mCompass.start();
        mCompass.setVisibility(View.VISIBLE);
        mIsLocationTracking = false;
      } else {
        startLocationTracking();
        item.setIcon(android.R.drawable.ic_menu_mylocation);
        if (mMapView.getRotationAngle() != 0) {
          mCompass.setVisibility(View.VISIBLE);
          mCompass.setRotationAngle(mMapView.getRotationAngle());
        } else {
          mCompass.setVisibility(View.GONE);
        }

      }
      return true;
    default:
      return super.onOptionsItemSelected(item);
    }
  }

  @Override
  public void onPause() {
    super.onPause();

    // Pause the MapView and stop the LocationDisplayManager to save battery
    if (mMapView != null) {
      if (mIsLocationTracking) {
        mMapView.getLocationDisplayManager().stop();
        mCompass.stop();
      }
      mMapViewState = mMapView.retainState();

      mMapView.pause();
    }
  }

  @Override
  public void onResume() {
    super.onResume();

    // Start the MapView and LocationDisplayManager running again
    if (mMapView != null) {
      // mCompass.start();
      mMapView.unpause();
      if (mMapViewState != null) {
        mMapView.restoreState(mMapViewState);
      }
      if (mIsLocationTracking) {
        mMapView.getLocationDisplayManager().start();
      }
    }
  }

  @Override
  public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    outState.putBoolean(KEY_IS_LOCATION_TRACKING, mIsLocationTracking);
    outState.putString(KEY_PORTAL_ITEM_ID, mPortalItemId);
    outState.putString(KEY_BASEMAP_ITEM_ID, mBasemapPortalItemId);
  }

  /**
   * Loads a WebMap and creates a MapView from it which is set into the
   * fragment's layout.
   * 
   * @param portalItemId
   *            The portal item id that represents the web map.
   * @param basemapPortalItemId
   *            The portal item id that represents the basemap.
   * @throws Exception
   *             if WebMap loading failed.
   */
  private void loadWebMapIntoMapView(final String portalItemId,
      final String basemapPortalItemId, final Portal portal) {

    TaskExecutor.getInstance().getThreadPool().submit(new Callable<Void>() {

      @Override
      public Void call() throws Exception {

        // load a WebMap instance from the portal item
        final WebMap webmap = WebMap.newInstance(portalItemId, portal);

        // load the WebMap that represents the basemap if one was
        // specified
        WebMap basemapWebMap = null;
        if (basemapPortalItemId != null
            && !basemapPortalItemId.isEmpty()) {
          basemapWebMap = WebMap.newInstance(basemapPortalItemId,
              portal);
        }
        final BaseMap basemap = basemapWebMap != null ? basemapWebMap
            .getBaseMap() : null;

        if (webmap != null) {
          getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
              MapView mapView = new MapView(getActivity(),
                  webmap, basemap, null, null);

              // mapView.setAllowRotationByPinch(true);

              setMapView(mapView);
              mapView.zoomin();

            }
          });
        } else {
          throw new Exception("Failed to load web map.");
        }
        return null;
      }
    });
  }

  /**
   * Takes a MapView that has already been instantiated to show a WebMap,
   * completes its setup by setting various listeners and attributes, and sets
   * it as the activity's content view.
   * 
   * @param mapView
   */
  private void setMapView(final MapView mapView) {

    mMapView = mapView;
    mMapView.setEsriLogoVisible(true);
    mMapView.enableWrapAround(true);
    mapView.setAllowRotationByPinch(true);

    // Creating an inflater
    mInflater = (LayoutInflater) getActivity().getSystemService(
        Context.LAYOUT_INFLATER_SERVICE);

    // Setting up the layout params for the searchview and searchresult
    // layout
    mlayoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
        LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP);
    mlayoutParams.setMargins(LEFT_MARGIN_SEARCH, TOP_MARGIN_SEARCH,
        RIGHT_MARGIN_SEARCH, BOTTOM_MARGIN_SEARCH);

    // set MapView into the activity layout
    mMapContainer.addView(mMapView);

    // Displaying the searchbox layout
    showSearchBoxLayout();

    mMapView.setOnPinchListener(new OnPinchListener() {

      /**
       * Default value
       */
      private static final long serialVersionUID = 1L;

      @Override
      public void postPointersDown(float x1, float y1, float x2,
          float y2, double factor) {
      }

      @Override
      public void postPointersMove(float x1, float y1, float x2,
          float y2, double factor) {
      }

      @Override
      public void postPointersUp(float x1, float y1, float x2, float y2,
          double factor) {
      }

      @Override
      public void prePointersDown(float x1, float y1, float x2, float y2,
          double factor) {
      }

      @Override
      public void prePointersMove(float x1, float y1, float x2, float y2,
          double factor) {
        if (mMapView.getRotationAngle() > 5
            || mMapView.getRotationAngle() < -5) {
          mCompass.setVisibility(View.VISIBLE);
          mCompass.sensorManager.unregisterListener(mCompass.sensorEventListener);
          mCompass.setRotationAngle(mMapView.getRotationAngle());
        }
      }

      @Override
      public void prePointersUp(float x1, float y1, float x2, float y2,
          double factor) {
      }

    });

    // Setup listener for map initialized
    mMapView.setOnStatusChangedListener(new OnStatusChangedListener() {

      private static final long serialVersionUID = 1L;

      @Override
      public void onStatusChanged(Object source, STATUS status) {

        if (source == mMapView && status == STATUS.INITIALIZED) {
          if (mMapViewState == null) {
            // Starting location tracking will cause zoom to My
            // Location
            startLocationTracking();
          } else {
            mMapView.restoreState(mMapViewState);
          }
          // add search and routing layers
          addGraphicLayers();
        }
      }
    });

    // Setup use of magnifier on a long press on the map
    mMapView.setShowMagnifierOnLongPress(true);
    mLongPressEvent = null;

    // Setup OnTouchListener to detect and act on long-press
    mMapView.setOnTouchListener(new MapOnTouchListener(getActivity(),
        mMapView) {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
          // Start of a new gesture. Make sure mLongPressEvent is
          // cleared.
          mLongPressEvent = null;
        }
        return super.onTouch(v, event);
      }

      @Override
      public void onLongPress(MotionEvent point) {
        // Set mLongPressEvent to indicate we are processing a
        // long-press
        mLongPressEvent = point;
        super.onLongPress(point);

      }

      @Override
      public boolean onDragPointerUp(MotionEvent from,
          final MotionEvent to) {
        if (mLongPressEvent != null) {
          // This is the end of a long-press that will have displayed
          // the
          // magnifier.
          // Perform reverse-geocoding of the point that was pressed
          Point mapPoint = mMapView.toMapPoint(to.getX(), to.getY());
          ReverseGeocodingAsyncTask reverseGeocodeTask = new ReverseGeocodingAsyncTask();
          reverseGeocodeTask.execute(mapPoint);
          mPendingTask = reverseGeocodeTask;

          mLongPressEvent = null;
          // Remove any previous graphics
          resetGraphicsLayers();
        }
        return super.onDragPointerUp(from, to);
      }

    });

  }

  /**
   * Adds the compass as per the height of the layout
   * 
   * @param height
   */
  private void addCompass(int height) {

    mMapContainer.removeView(mCompass);

    // Create the Compass custom view, and add it onto
    // the MapView.
    mCompass = new Compass(mMapView.getContext());
    mCompass.setAlpha(1f);
    mCompass.setRotationAngle(45);
    compassFrameParams = new FrameLayout.LayoutParams(HEIGHT, WIDTH,
        Gravity.RIGHT);

    TOP_MARGIN_COMPASS = TOP_MARGIN_SEARCH + height + 15;

    ((MarginLayoutParams) compassFrameParams).setMargins(
        LEFT_MARGIN_COMPASS, TOP_MARGIN_COMPASS, RIGHT_MARGIN_COMPASS,
        BOTTOM_MARGIN_COMPASS);

    mCompass.setLayoutParams(compassFrameParams);

    mCompass.setVisibility(View.GONE);

    mCompass.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        mCompass.setVisibility(View.GONE);
        mMapView.setRotationAngle(0);
      }
    });

    // Add the compass on the map
    mMapContainer.addView(mCompass);

  }

  /**
   * 
   * Displays the Dialog Fragment which allows users to route
   */
  private void showRoutingDialogFragment() {
    // Show RoutingDialogFragment to get routing start and end points.
    // This calls back to onGetRoute() to do the routing.
    RoutingDialogFragment routingFrag = new RoutingDialogFragment();
    routingFrag.setRoutingDialogListener(this);
    Bundle arguments = new Bundle();
    if (mLocationLayerPoint != null) {
      arguments.putString(RoutingDialogFragment.ARG_END_POINT_DEFAULT,
          mLocationLayerPointString);
    }
    routingFrag.setArguments(arguments);
    routingFrag.show(getFragmentManager(), null);

  }

  /**
   * Displays the Directions Dialog Fragment
   */
  private void showDirectionsDialogFragment() {
    // Launch a DirectionsListFragment to display list of directions
    final DirectionsDialogFragment frag = new DirectionsDialogFragment();
    frag.setRoutingDirections(mRoutingDirections,
        new DirectionsDialogListener() {

          @Override
          public void onDirectionSelected(int position) {
            // User has selected a particular direction -
            // dismiss the dialog and
            // zoom to the selected direction
            frag.dismiss();
            RouteDirection direction = mRoutingDirections
                .get(position);
            mMapView.setExtent(direction.getGeometry());
          }

        });
    getFragmentManager().beginTransaction().add(frag, null).commit();

  }

  /**
   * Displays the search view layout
   * 
   */
  private void showSearchBoxLayout() {

    // Inflating the layout from the xml file
    mSearchBox = mInflater.inflate(R.layout.searchview, null);

    // Setting the layout parameters to the layout
    mSearchBox.setLayoutParams(mlayoutParams);

    // Initializing the searchview and the image view
    final SearchView mSearchview = (SearchView) mSearchBox
        .findViewById(R.id.searchView1);

    ImageView iv_route = (ImageView) mSearchBox
        .findViewById(R.id.imageView1);

    mSearchview.setIconifiedByDefault(false);
    mSearchview.setQueryHint(SEARCH_HINT);

    // Adding the layout to the map conatiner
    mMapContainer.addView(mSearchBox);

    // Setup the listener for the route onclick
    iv_route.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        showRoutingDialogFragment();

      }
    });

    // Setup the listener when the search button is pressed on the keyboard
    mSearchview.setOnQueryTextListener(new OnQueryTextListener() {

      @Override
      public boolean onQueryTextSubmit(String query) {
        onSearchButtonClicked(query);
        mSearchview.clearFocus();
        return true;
      }

      @Override
      public boolean onQueryTextChange(String newText) {
        return false;
      }
    });

    // Add the compass after getting the height of the layout
    mSearchBox.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
          @Override
          public void onGlobalLayout() {
            addCompass(mSearchBox.getHeight());
            mSearchBox.getViewTreeObserver().removeGlobalOnLayoutListener(this);
          }

        });

  }

  /**
   * Clears all graphics out of the location layer and the route layer.
   */
  void resetGraphicsLayers() {
    mLocationLayer.removeAll();
    mRouteLayer.removeAll();
    mLocationLayerPoint = null;
    mLocationLayerPointString = null;
    mRoutingDirections = null;
  }

  /**
   * Adds location layer and the route layer to the MapView.
   */
  void addGraphicLayers() {
    // Add location layer
    if (mLocationLayer == null) {
      mLocationLayer = new GraphicsLayer();
    }
    mMapView.addLayer(mLocationLayer);

    // Add the route graphic layer
    if (mRouteLayer == null) {
      mRouteLayer = new GraphicsLayer();
    }
    mMapView.addLayer(mRouteLayer);
  }

  /**
   * Starts tracking GPS location.
   */
  void startLocationTracking() {
    LocationDisplayManager locDispMgr = mMapView
        .getLocationDisplayManager();
    mCompass.start();
    locDispMgr.setAutoPanMode(AutoPanMode.LOCATION);
    locDispMgr.setAllowNetworkLocation(true);

    locDispMgr.setLocationListener(new LocationListener() {

      boolean locationChanged = false;

      // Zooms to the current location when first GPS fix arrives
      @Override
      public void onLocationChanged(Location loc) {
        double locy = loc.getLatitude();
        double locx = loc.getLongitude();
        Point wgspoint = new Point(locx, locy);
        mLocation = (Point) GeometryEngine.project(wgspoint,
            SpatialReference.create(4326),
            mMapView.getSpatialReference());
        if (!locationChanged) {
          locationChanged = true;
          Unit mapUnit = mMapView.getSpatialReference().getUnit();
          double zoomWidth = Unit.convertUnits(SEARCH_RADIUS,
              Unit.create(LinearUnit.Code.MILE_US), mapUnit);
          Envelope zoomExtent = new Envelope(mLocation,
              zoomWidth / 10, zoomWidth / 10);
          mMapView.setExtent(zoomExtent);
        }
      }

      @Override
      public void onProviderDisabled(String arg0) {
      }

      @Override
      public void onProviderEnabled(String arg0) {
      }

      @Override
      public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
      }
    });
    locDispMgr.start();
    mIsLocationTracking = true;
  }

  @Override
  public void onBasemapChanged(String basemapPortalItemId) {
    ((MapsAppActivity) getActivity()).showMap(mPortalItemId,
        basemapPortalItemId);
  }

  /**
   * Called from search_layout.xml when user presses Search button.
   * 
   * @param view
   */
  public void onSearchButtonClicked(String address) {

    // Hide virtual keyboard
    InputMethodManager inputManager = (InputMethodManager) getActivity()
        .getSystemService(Context.INPUT_METHOD_SERVICE);
    inputManager.hideSoftInputFromWindow(getActivity().getCurrentFocus()
        .getWindowToken(), 0);

    // Remove any previous graphics and routes
    resetGraphicsLayers();

    executeLocatorTask(address);
  }

  /**
   * Set up the search parameters and execute the Locator task.
   * 
   * @param address
   */
  private void executeLocatorTask(String address) {
    // Create Locator parameters from single line address string
    LocatorFindParameters findParams = new LocatorFindParameters(address);

    // Use the centre of the current map extent as the find location point
    findParams.setLocation(mMapView.getCenter(),
        mMapView.getSpatialReference());

    // Calculate distance for find operation
    Envelope mapExtent = new Envelope();
    mMapView.getExtent().queryEnvelope(mapExtent);
    // assume map is in metres, other units wont work, double current
    // envelope
    double distance = (mapExtent != null && mapExtent.getWidth() > 0) ? mapExtent
        .getWidth() * 2 : 10000;
    findParams.setDistance(distance);
    findParams.setMaxLocations(2);

    // Set address spatial reference to match map
    findParams.setOutSR(mMapView.getSpatialReference());

    // Execute async task to find the address
    LocatorAsyncTask locatorTask = new LocatorAsyncTask();
    locatorTask.execute(findParams);
    mPendingTask = locatorTask;

    mLocationLayerPointString = address;
  }

  /**
   * Called by RoutingDialogFragment when user presses Get Route button.
   * 
   * @param startPoint
   *            String entered by user to define start point.
   * @param endPoint
   *            String entered by user to define end point.
   * @return true if routing task executed, false if parameters rejected. If
   *         this method rejects the parameters it must display an explanatory
   *         Toast to the user before returning.
   */
  @Override
  public boolean onGetRoute(String startPoint, String endPoint) {
    // Check if we need a location fix
    if (startPoint.equals(getString(R.string.my_location))
        && mLocation == null) {
      Toast.makeText(getActivity(),
          getString(R.string.need_location_fix), Toast.LENGTH_LONG)
          .show();
      return false;
    }
    // Remove any previous graphics and routes
    resetGraphicsLayers();

    // Do the routing
    executeRoutingTask(startPoint, endPoint);
    return true;
  }

  /**
   * Set up Route Parameters to execute RouteTask
   * 
   * @param start
   * @param end
   */
  @SuppressWarnings("unchecked")
  private void executeRoutingTask(String start, String end) {
    // Create a list of start end point params
    LocatorFindParameters routeStartParams = new LocatorFindParameters(
        start);
    LocatorFindParameters routeEndParams = new LocatorFindParameters(end);
    List<LocatorFindParameters> routeParams = new ArrayList<LocatorFindParameters>();

    // Add params to list
    routeParams.add(routeStartParams);
    routeParams.add(routeEndParams);

    // Execute async task to do the routing
    RouteAsyncTask routeTask = new RouteAsyncTask();
    routeTask.execute(routeParams);
    mPendingTask = routeTask;
  }

  @Override
  public void onCancel(DialogInterface dialog) {
    // a pending task needs to be canceled
    if (mPendingTask != null) {
      mPendingTask.cancel(true);
    }
  }

  /**
   * Shows the search result in the layout after successful geocoding and
   * reverse geocoding
   * 
   */

  private void showSearchResultLayout(String address) {
    // Remove the layouts
    mMapContainer.removeView(mSearchBox);
    mMapContainer.removeView(mSearchResult);

    // Inflate the new layout from the xml file
    mSearchResult = mInflater.inflate(R.layout.search_result, null);

    // Set layout parameters
    mSearchResult.setLayoutParams(mlayoutParams);

    // Initialize the textview and set its text
    TextView tv = (TextView) mSearchResult.findViewById(R.id.textView1);
    tv.setTypeface(null, Typeface.BOLD);
    tv.setText(address);

    // Adding the search result layout to the map container
    mMapContainer.addView(mSearchResult);

    // Setup the listener for the "cancel" icon
    ImageView iv_cancel = (ImageView) mSearchResult
        .findViewById(R.id.imageView3);
    iv_cancel.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        // Remove the search result view
        mMapContainer.removeView(mSearchResult);

        // Add the search box view
        showSearchBoxLayout();

        // Remove all graphics from the map
        resetGraphicsLayers();

      }
    });

    // Set up the listener for the "Get Directions" icon
    ImageView iv_route = (ImageView) mSearchResult
        .findViewById(R.id.imageView2);
    iv_route.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        onGetRoute(getString(R.string.my_location),
            mLocationLayerPointString);
      }
    });

    // Add the compass after getting the height of the layout
    mSearchResult.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
          @Override
          public void onGlobalLayout() {
            addCompass(mSearchResult.getHeight());
            mSearchResult.getViewTreeObserver().removeGlobalOnLayoutListener(this);
          }

        });

  }

  /**
   * Shows the Routing result layout after successful routing
   * 
   * @param time
   * @param distance
   * 
   */

  private void showRoutingResultLayout(double distance, double time) {

    // Remove the layours
    mMapContainer.removeView(mSearchResult);
    mMapContainer.removeView(mSearchBox);

    // Inflate the new layout from the xml file
    mSearchResult = mInflater.inflate(R.layout.routing_result, null);

    mSearchResult.setLayoutParams(mlayoutParams);

    // Shorten the start and end location by finding the first comma if
    // present
    int index_from = mStartLocation.indexOf(",");
    int index_to = mEndLocation.indexOf(",");
    if (index_from != -1)
      mStartLocation = mStartLocation.substring(0, index_from);
    if (index_to != -1)
      mEndLocation = mEndLocation.substring(0, index_to);

    // Initialize the textvieww and display the text
    TextView tv_from = (TextView) mSearchResult.findViewById(R.id.tv_from);
    tv_from.setTypeface(null, Typeface.BOLD);
    tv_from.setText(" " + mStartLocation);

    TextView tv_to = (TextView) mSearchResult.findViewById(R.id.tv_to);
    tv_to.setTypeface(null, Typeface.BOLD);
    tv_to.setText(" " + mEndLocation);

    // Rounding off the values
    distance = Math.round(distance * 10.0) / 10.0;
    time = Math.round(time * 10.0) / 10.0;

    TextView tv_time = (TextView) mSearchResult.findViewById(R.id.tv_time);
    tv_time.setTypeface(null, Typeface.BOLD);
    tv_time.setText(time + " mins");

    TextView tv_dist = (TextView) mSearchResult.findViewById(R.id.tv_dist);
    tv_dist.setTypeface(null, Typeface.BOLD);
    tv_dist.setText(" (" + distance + " miles)");

    // Adding the layout
    mMapContainer.addView(mSearchResult);

    // Setup the listener for the "Cancel" icon
    ImageView iv_cancel = (ImageView) mSearchResult
        .findViewById(R.id.imageView3);
    iv_cancel.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        // Remove the search result view
        mMapContainer.removeView(mSearchResult);
        // Add the default search box view
        showSearchBoxLayout();
        // Remove all graphics from the map
        resetGraphicsLayers();

      }
    });

    // Set up the listener for the "Show Directions" icon
    ImageView iv_directions = (ImageView) mSearchResult
        .findViewById(R.id.imageView2);
    iv_directions.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        showDirectionsDialogFragment();
      }
    });

    // Add the compass after getting the height of the layout
    mSearchResult.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
          @Override
          public void onGlobalLayout() {
            addCompass(mSearchResult.getHeight());
            mSearchResult.getViewTreeObserver().removeGlobalOnLayoutListener(this);
          }

        });

  }

  /*
   * This class provides an AsyncTask that performs a geolocation request on a
   * background thread and displays the first result on the map on the UI
   * thread.
   */
  private class LocatorAsyncTask extends
      AsyncTask<LocatorFindParameters, Void, List<LocatorGeocodeResult>> {
    private static final String TAG_LOCATOR_PROGRESS_DIALOG = "TAG_LOCATOR_PROGRESS_DIALOG";

    private Exception mException;

    private ProgressDialogFragment mProgressDialog;

    public LocatorAsyncTask() {
    }

    @Override
    protected void onPreExecute() {
      mProgressDialog = ProgressDialogFragment.newInstance(getActivity()
          .getString(R.string.address_search));
      // set the target fragment to receive cancel notification
      mProgressDialog.setTargetFragment(MapFragment.this,
          REQUEST_CODE_PROGRESS_DIALOG);
      mProgressDialog.show(getActivity().getFragmentManager(),
          TAG_LOCATOR_PROGRESS_DIALOG);
    }

    @Override
    protected List<LocatorGeocodeResult> doInBackground(
        LocatorFindParameters... params) {
      // Perform routing request on background thread
      mException = null;
      List<LocatorGeocodeResult> results = null;

      // Create locator using default online geocoding service and tell it
      // to
      // find the given address
      Locator locator = Locator.createOnlineLocator();
      try {
        results = locator.find(params[0]);
      } catch (Exception e) {
        mException = e;
      }
      return results;
    }

    @Override
    protected void onPostExecute(List<LocatorGeocodeResult> result) {
      // Display results on UI thread
      mProgressDialog.dismiss();
      if (mException != null) {
        Log.w(TAG, "LocatorSyncTask failed with:");
        mException.printStackTrace();
        Toast.makeText(getActivity(),
            getString(R.string.addressSearchFailed),
            Toast.LENGTH_LONG).show();
        return;
      }

      if (result.size() == 0) {
        Toast.makeText(getActivity(),
            getString(R.string.noResultsFound), Toast.LENGTH_LONG)
            .show();
      } else {
        // Use first result in the list
        LocatorGeocodeResult geocodeResult = result.get(0);

        // get return geometry from geocode result
        Point resultPoint = geocodeResult.getLocation();
        // create marker symbol to represent location
        Drawable drawable = getActivity().getResources().getDrawable(
            R.drawable.pin_circle_red);
        PictureMarkerSymbol resultSymbol = new PictureMarkerSymbol(
            getActivity(), drawable);
        // create graphic object for resulting location
        Graphic resultLocGraphic = new Graphic(resultPoint,
            resultSymbol);
        // add graphic to location layer
        mLocationLayer.addGraphic(resultLocGraphic);

        // Get the address
        String address = geocodeResult.getAddress();

        mLocationLayerPoint = resultPoint;

        // Zoom map to geocode result location
        mMapView.zoomToResolution(geocodeResult.getLocation(), 2);
        showSearchResultLayout(address);
      }
    }

  }

  /**
   * This class provides an AsyncTask that performs a routing request on a
   * background thread and displays the resultant route on the map on the UI
   * thread.
   */
  private class RouteAsyncTask extends
      AsyncTask<List<LocatorFindParameters>, Void, RouteResult> {
    private static final String TAG_ROUTE_SEARCH_PROGRESS_DIALOG = "TAG_ROUTE_SEARCH_PROGRESS_DIALOG";

    private Exception mException;

    private ProgressDialogFragment mProgressDialog;

    public RouteAsyncTask() {
    }

    @Override
    protected void onPreExecute() {
      mProgressDialog = ProgressDialogFragment.newInstance(getActivity()
          .getString(R.string.route_search));
      // set the target fragment to receive cancel notification
      mProgressDialog.setTargetFragment(MapFragment.this,
          REQUEST_CODE_PROGRESS_DIALOG);
      mProgressDialog.show(getActivity().getFragmentManager(),
          TAG_ROUTE_SEARCH_PROGRESS_DIALOG);
    }

    @Override
    protected RouteResult doInBackground(
        List<LocatorFindParameters>... params) {
      // Perform routing request on background thread
      mException = null;

      // Define route objects
      List<LocatorGeocodeResult> geocodeStartResult = null;
      List<LocatorGeocodeResult> geocodeEndResult = null;
      Point startPoint = null;
      Point endPoint = null;

      // Create a new locator to geocode start/end points;
      // by default uses ArcGIS online world geocoding service
      Locator locator = Locator.createOnlineLocator();

      try {
        // Geocode start position, or use My Location (from GPS)
        LocatorFindParameters startParam = params[0].get(0);
        if (startParam.getText()
            .equals(getString(R.string.my_location))) {
          mStartLocation = getString(R.string.my_location);
          startPoint = (Point) GeometryEngine.project(mLocation, mWm,
              mEgs);
        } else {
          geocodeStartResult = locator.find(startParam);
          startPoint = geocodeStartResult.get(0).getLocation();
          mStartLocation = geocodeStartResult.get(0).getAddress();

          if (isCancelled()) {
            return null;
          }
        }

        // Geocode the destination
        LocatorFindParameters endParam = params[0].get(1);
        if (endParam.getText().equals(getString(R.string.my_location))) {
          mEndLocation = getString(R.string.my_location);
          endPoint = (Point) GeometryEngine.project(mLocation, mWm,
              mEgs);
        } else {
          geocodeEndResult = locator.find(endParam);
          endPoint = geocodeEndResult.get(0).getLocation();
          mEndLocation = geocodeEndResult.get(0).getAddress();
        }

      } catch (Exception e) {
        mException = e;
        return null;
      }
      if (isCancelled()) {
        return null;
      }

      // Create a new routing task pointing to an ArcGIS Network Analysis
      // Service
      RouteTask routeTask;
      RouteParameters routeParams = null;
      try {
        routeTask = RouteTask.createOnlineRouteTask(
            getString(R.string.routingservice_url), null);
        // Retrieve default routing parameters
        routeParams = routeTask.retrieveDefaultRouteTaskParameters();
      } catch (Exception e) {
        mException = e;
        return null;
      }
      if (isCancelled()) {
        return null;
      }

      // Customize the route parameters
      NAFeaturesAsFeature routeFAF = new NAFeaturesAsFeature();
      StopGraphic sgStart = new StopGraphic(startPoint);
      StopGraphic sgEnd = new StopGraphic(endPoint);
      routeFAF.setFeatures(new Graphic[] { sgStart, sgEnd });
      routeFAF.setCompressedRequest(true);
      routeParams.setStops(routeFAF);
      routeParams.setOutSpatialReference(mMapView.getSpatialReference());

      // Solve the route
      RouteResult routeResult;
      try {
        routeResult = routeTask.solve(routeParams);
      } catch (Exception e) {
        mException = e;
        return null;
      }
      if (isCancelled()) {
        return null;
      }
      return routeResult;
    }

    @Override
    protected void onPostExecute(RouteResult result) {
      // Display results on UI thread
      mProgressDialog.dismiss();
      if (mException != null) {
        Log.w(TAG, "RouteSyncTask failed with:");
        mException.printStackTrace();
        Toast.makeText(getActivity(),
            getString(R.string.routingFailed), Toast.LENGTH_LONG)
            .show();
        return;
      }

      // Get first item in list of routes provided by server
      Route route = result.getRoutes().get(0);

      // Create polyline graphic of the full route
      SimpleLineSymbol lineSymbol = new SimpleLineSymbol(Color.RED, 2,
          STYLE.SOLID);
      Graphic routeGraphic = new Graphic(route.getRouteGraphic()
          .getGeometry(), lineSymbol);

      // Create point graphic to mark start of route
      Point startPoint = ((Polyline) routeGraphic.getGeometry())
          .getPoint(0);
      Graphic startGraphic = createMarkerGraphic(startPoint, false);

      // Create point graphic to mark end of route
      int endPointIndex = ((Polyline) routeGraphic.getGeometry())
          .getPointCount() - 1;
      Point endPoint = ((Polyline) routeGraphic.getGeometry())
          .getPoint(endPointIndex);
      Graphic endGraphic = createMarkerGraphic(endPoint, true);

      // Add these graphics to route layer
      mRouteLayer.addGraphics(new Graphic[] { routeGraphic, startGraphic,
          endGraphic });

      // Zoom to the extent of the entire route with a padding
      mMapView.setExtent(route.getEnvelope(), 100);

      // Save routing directions so user can display them later
      mRoutingDirections = route.getRoutingDirections();

      // Show Routing Result Layout
      showRoutingResultLayout(route.getTotalMiles(),
          route.getTotalMinutes());

    }

    Graphic createMarkerGraphic(Point point, boolean endPoint) {
      Drawable marker = getResources().getDrawable(
          endPoint ? R.drawable.pin_circle_blue
              : R.drawable.pin_circle_red);
      PictureMarkerSymbol destinationSymbol = new PictureMarkerSymbol(
          mMapView.getContext(), marker);
      // NOTE: marker's bounds not set till marker is used to create
      // destinationSymbol
      float offsetY = convertPixelsToDp(getActivity(),
          marker.getBounds().bottom);
      destinationSymbol.setOffsetY(offsetY);
      return new Graphic(point, destinationSymbol);
    }
  }

  /**
   * This class provides an AsyncTask that performs a reverse geocoding
   * request on a background thread and displays the resultant point on the
   * map on the UI thread.
   */
  public class ReverseGeocodingAsyncTask extends
      AsyncTask<Point, Void, LocatorReverseGeocodeResult> {
    private static final String TAG_REVERSE_GEOCODING_PROGRESS_DIALOG = "TAG_REVERSE_GEOCODING_PROGRESS_DIALOG";

    private Exception mException;

    private ProgressDialogFragment mProgressDialog;

    private Point mPoint;

    @Override
    protected void onPreExecute() {
      mProgressDialog = ProgressDialogFragment.newInstance(getActivity()
          .getString(R.string.reverse_geocoding));
      // set the target fragment to receive cancel notification
      mProgressDialog.setTargetFragment(MapFragment.this,
          REQUEST_CODE_PROGRESS_DIALOG);
      mProgressDialog.show(getActivity().getFragmentManager(),
          TAG_REVERSE_GEOCODING_PROGRESS_DIALOG);
    }

    @Override
    protected LocatorReverseGeocodeResult doInBackground(Point... params) {
      // Perform reverse geocoding request on background thread
      mException = null;
      LocatorReverseGeocodeResult result = null;
      mPoint = params[0];

      // Create locator using default online geocoding service and tell it
      // to
      // find the given point
      Locator locator = Locator.createOnlineLocator();
      try {
        // Our input and output spatial reference will be the same as
        // the map
        SpatialReference mapRef = mMapView.getSpatialReference();
        result = locator.reverseGeocode(mPoint, 100.0, mapRef, mapRef);
        mLocationLayerPoint = mPoint;
      } catch (Exception e) {
        mException = e;
      }
      // return the resulting point(s)
      return result;
    }

    @Override
    protected void onPostExecute(LocatorReverseGeocodeResult result) {
      // Display results on UI thread
      mProgressDialog.dismiss();
      if (mException != null) {
        Log.w(TAG, "LocatorSyncTask failed with:");
        mException.printStackTrace();
        Toast.makeText(getActivity(),
            getString(R.string.addressSearchFailed),
            Toast.LENGTH_LONG).show();
        return;
      }

      // Construct a nicely formatted address from the results
      StringBuilder address = new StringBuilder();
      if (result != null && result.getAddressFields() != null) {
        Map<String, String> addressFields = result.getAddressFields();
        address.append(String.format("%s\n%s, %s %s",
            addressFields.get("Address"),
            addressFields.get("City"), addressFields.get("Region"),
            addressFields.get("Postal")));

        // Draw marker on map.
        // create marker symbol to represent location
        Drawable drawable = getActivity().getResources().getDrawable(
            R.drawable.pin_circle_red);
        PictureMarkerSymbol symbol = new PictureMarkerSymbol(
            getActivity(), drawable);
        mLocationLayer.addGraphic(new Graphic(mPoint, symbol));

        // Address string is saved for use in routing
        mLocationLayerPointString = address.toString();
        // center the map to result location
        mMapView.centerAt(mPoint, true);

        // Show the result on the search result layout
        showSearchResultLayout(address.toString());
      }
    }
  }

  /**
   * Converts device specific pixels to density independent pixels.
   * 
   * @param context
   * @param px
   *            number of device specific pixels
   * @return number of density independent pixels
   */
  private float convertPixelsToDp(Context context, float px) {
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return dp;
  }
}




Java Source Code List

com.esri.android.mapsapp.ContentBrowserFragment.java
com.esri.android.mapsapp.DrawerItem.java
com.esri.android.mapsapp.MapFragment.java
com.esri.android.mapsapp.MapsAppActivity.java
com.esri.android.mapsapp.account.AccountManager.java
com.esri.android.mapsapp.account.SignInActivity.java
com.esri.android.mapsapp.basemaps.BasemapItem.java
com.esri.android.mapsapp.basemaps.BasemapsAdapter.java
com.esri.android.mapsapp.basemaps.BasemapsDialogFragment.java
com.esri.android.mapsapp.dialogs.ProgressDialogFragment.java
com.esri.android.mapsapp.location.DirectionsDialogFragment.java
com.esri.android.mapsapp.location.RoutingDialogFragment.java
com.esri.android.mapsapp.tools.Compass.java
com.esri.android.mapsapp.util.StringUtils.java
com.esri.android.mapsapp.util.TaskExecutor.java
com.esri.android.mapsapp.util.UiUtils.java