Android Open Source - Android_OSM_offlinemap Main Activity






From Project

Back to project page Android_OSM_offlinemap.

License

The source code is released under:

GNU General Public License

If you think the Android project Android_OSM_offlinemap 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

package com.vellut.offlinemap;
/* w  w w  .  ja va 2 s  .c o m*/
import static com.vellut.offlinemap.Utils.d;
import static com.vellut.offlinemap.Utils.e;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.mapsforge.android.maps.MapActivity;
import org.mapsforge.android.maps.MapView;
import org.mapsforge.android.maps.overlay.ArrayItemizedOverlay;
import org.mapsforge.android.maps.overlay.ItemizedOverlay;
import org.mapsforge.android.maps.overlay.OverlayItem;
import org.mapsforge.core.GeoPoint;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.vellut.offlinemap.kansai.R;

public class MainActivity extends MapActivity implements ConnectionCallbacks,
    OnConnectionFailedListener, LocationListener, View.OnTouchListener {

  private File mapFile;
  private File dataFile;
  private LocationClient locationClient;
  private CopyrightMapView mapView;
  private boolean zoomToCurrentPositionOnConnected;
  private MarkerFactory markerFactory;

  private MapData mapData;
  private ArrayList<MapAnnotation> mapAnnotations;
  private CurrentPosition currentPosition;
  private boolean hasEditedMapAnnotations;

  private ArrayItemizedOverlay currentPositionOverlay;
  private ArrayItemizedOverlay mapAnnotationsOverlay;
  private ArrayItemizedOverlay bubbleTextOverlay;

  private OverlayItem overlayItemInContext;
  private MapAnnotation mapAnnotationInContext;
  private int indexMapAnnotationInContext;
  private boolean isCreation;

  private boolean isEditingOrCreating;
  private boolean isLocationUpdating;

  private MapAnnotation currentMapAnnotationForBubble;

  private CurrentPositionDrawable currentPositionWithAccuracyMarker;

  private boolean isFirstTimeRun;
  private boolean aBoolean;

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

    d("In OnCreate");

    // fill data from generic.xml
    Utils.fillGenericData(this);

    locationClient = new LocationClient(this, this, this);
    d("BeforeRESTORE");
    restorePreferences();
    d("AfterRESTORE");
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

    init();

    d("BeforeCONFIGURE");
    configureUI(savedInstanceState != null);
    d("AfterCONFIGURE");
    if (savedInstanceState != null) {
      restoreState(savedInstanceState);
    }

  }

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

    locationClient.connect();
  }

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

    GeoPoint center = mapView.getMapPosition().getMapCenter();
    mapData.mapLatitude = center.getLatitude();
    mapData.mapLongitude = center.getLongitude();
    mapData.mapZoom = mapView.getMapPosition().getZoomLevel();

    savePreferences();
    hasEditedMapAnnotations = false;
  }

  @Override
  protected void onStop() {
    super.onStop();

    locationClient.disconnect();
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    MenuItem item = menu.findItem(R.id.action_initial_view);
    String cityName = getString(R.string.city_name);
    item.setTitle(getString(R.string.action_initial_view, cityName));
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.action_current_location:
      zoomToCurrentPosition();
      return true;
    case R.id.action_initial_view:
      zoomToInitialPosition();
      return true;
    case R.id.action_export_import:
      showExportImportDialog();
      return true;
    case R.id.action_help:
      showWelcomeDialog();
      return true;
    default:
      return super.onOptionsItemSelected(item);
    }
  }

  @Override
  public void onCreateContextMenu(ContextMenu menu, View v,
      ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu_context, menu);
    if (mapAnnotationInContext.isBookmarked) {
      menu.getItem(1).setTitle(
          getString(R.string.action_unstar_map_annotation));
    }
  }

  @Override
  public boolean onContextItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.action_edit_map_annotation:
      editMapAnnotation();
      return true;
    case R.id.action_star_map_annotation:
      starMapAnnotation();
      return true;
    case R.id.action_delete_map_annotation:
      deleteMapAnnotation(true);
      return true;
    default:
      return super.onContextItemSelected(item);
    }
  }

  @Override
  public void onContextMenuClosed(Menu menu) {
    super.onContextMenuClosed(menu);
  }

  private void zoomToCurrentPosition() {
    // do not launch if location currently updating
    if (isGooglePlayServicesConnected() && locationClient.isConnected()
        && !isLocationUpdating) {
      setProgressBarIndeterminateVisibility(true);

      LocationRequest lr = new LocationRequest();
      lr.setNumUpdates(1).setFastestInterval(1).setInterval(1)
          .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
          .setExpirationDuration(Utils.LOCATION_UPDATE_TIMEOUT);
      locationClient.requestLocationUpdates(lr, this);

      isLocationUpdating = true;

      new CountDownTimer(Utils.LOCATION_UPDATE_TIMEOUT,
          Utils.LOCATION_UPDATE_TIMEOUT) {
        public void onTick(long millisUntilFinished) {
        }

        public void onFinish() {
          // Timeout reached before we could get a fix
          setProgressBarIndeterminateVisibility(false);
          if (isLocationUpdating) {
            isLocationUpdating = false;
            locationClient.removeLocationUpdates(MainActivity.this);

            Location location = locationClient.getLastLocation();
            if (location != null) {
              setCurrentPosition(location);
            } else {
              showAlertDialog(R.string.timeout_location, false,
                  null, null);
            }
          }
        }
      }.start();

    } else {
      zoomToCurrentPositionOnConnected = true;
    }
  }

  private void zoomToInitialPosition() {
    mapView.getController().setCenter(
        new GeoPoint(Utils.INITIAL_LAT, Utils.INITIAL_LON));
    mapView.getController().setZoom(Utils.INITIAL_ZOOM);

    checkCurrentZoomForMapAnnotationVisibility();
  }

  private void showExportImportDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setItems(R.array.items_export_import,
        new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int itemIndex) {
            if (itemIndex == 0) {
              // export
              startExportMapAnnotationsToFile();
            } else {
              // import
              startImportMapAnnotationsFromFile();
            }
          }
        });
    builder.setNegativeButton(android.R.string.cancel, null);

    // Create the AlertDialog object and return it
    builder.create().show();
  }

  private void init() {
    hasEditedMapAnnotations = false;

    d(getApplicationContext().getPackageName());
    String mapName = Utils.MAP_NAME;
    mapFile = new File(getExternalCacheDir(), mapName);
    if (!mapFile.exists() || hasNewVersion(mapFile)) {
      d("Installing map");
      createFileFromAsset(mapFile, mapName);
    }

    if (Utils.HAS_PRELOADED_DATA) {
      // Data file to preload
      dataFile = new File(getExternalCacheDir(), Utils.DATA_NAME);
      if (!dataFile.exists() || hasNewVersion(dataFile)) {
        d("Installing data file");
        refreshPreLoadedData(Utils.DATA_NAME);
      }
    }

    markerFactory = new MarkerFactory(this);

  }

  private void refreshPreLoadedData(String assetDataFileName) {
    if (TextUtils.equals(Utils.UI_MODE, Utils.UI_MODE_STAR_ONLY)) {
      // Index existing mapAnnotations
      Map<String, MapAnnotation> indexPreviousMapAnnotations = new HashMap<String, MapAnnotation>(mapAnnotations.size());
      // At this point mapAnnotations have already been loaded from 
      // the saved file
      for (MapAnnotation mapAnnotation : mapAnnotations) {
        indexPreviousMapAnnotations
            .put(mapAnnotation.id, mapAnnotation);
      }

      String dataFileContent = null;
      InputStream is = null;
      try {
        AssetManager am = getAssets();
        is = new BufferedInputStream(am.open(assetDataFileName));
        dataFileContent = Utils.convertStreamToString(is);
      } catch (IOException e) {
        Utils.e("Error opening OFM", e);
      } finally {
        try {
          is.close();
        } catch (IOException e) {
        }
      }

      if (dataFileContent != null) {
        ArrayList<MapAnnotation> dataFileMapAnnotations = deserializeMapAnnotationsFromString(dataFileContent);
        
        // We will modify the map annotations below
        // so need to save later on
        hasEditedMapAnnotations = true;
        for (MapAnnotation mapAnnotation : dataFileMapAnnotations) {
          MapAnnotation previousMapAnnotation = indexPreviousMapAnnotations
              .get(mapAnnotation.id);
          if (previousMapAnnotation != null) {
            // Copy bookmark info from previous annotations
            mapAnnotation.isBookmarked = previousMapAnnotation.isBookmarked;
          }
        }
        mapAnnotations = dataFileMapAnnotations;
      }
    } else {
      // FIXME Implement merging of the modified notes and
      // updated note
    }
  }

  @SuppressWarnings("unchecked")
  public void restorePreferences() {
    SharedPreferences settings = PreferenceManager
        .getDefaultSharedPreferences(this);

    d("BeforeMAPDATA ");

    String sMapData = settings.getString(Utils.PREF_MAP_DATA, null);
    if (sMapData != null) {
      try {
        mapData = (MapData) deserializeFromString(sMapData,
            MapData.class);
      } catch (Exception ex) {
        Log.e(Utils.TAG, "Error getting saved MapData", ex);
      }
    } else {
      mapData = new MapData();
    }

    d("BeforeSAVLOCATIONS");

    /*
     * String sSavedLocations =
     * settings.getString(Utils.PREF_SAVED_LOCATIONS, null); if
     * (sSavedLocations != null) { long nano = System.nanoTime();
     * mapAnnotations =
     * deserializeMapAnnotationsFromString(sSavedLocations); long diff =
     * System.nanoTime() - nano; d("DESERMAPANNOT1 " + (diff * 1e-9)); }
     * else { mapAnnotations = new ArrayList<MapAnnotation>(); }
     */

    if (Utils.DATA_NAME != null) {
      dataFile = new File(getExternalCacheDir(), Utils.DATA_NAME);
    } else {
      dataFile = new File(getExternalCacheDir(), "mapAnnotations");
    }

    if (dataFile.exists()) {
      String dataFileContent = null;
      InputStream is = null;
      try {
        is = new BufferedInputStream(new FileInputStream(dataFile));
        dataFileContent = Utils.convertStreamToString(is);
      } catch (IOException e) {
        Utils.e("Error opening OFM", e);
      } finally {
        try {
          is.close();
        } catch (IOException e) {
        }
      }

      if (dataFileContent != null) {
        mapAnnotations = deserializeMapAnnotationsFromString(dataFileContent);
      }
    } else {
      mapAnnotations = new ArrayList<MapAnnotation>();
    }

    String sCurrentPosition = settings.getString(
        Utils.PREF_CURRENT_POSITION, null);
    if (sCurrentPosition != null) {
      try {
        currentPosition = (CurrentPosition) deserializeFromString(
            sCurrentPosition, CurrentPosition.class);
      } catch (Exception ex) {
        Log.e(Utils.TAG, "Error getting saved CurrentPosition", ex);
      }
    } else {
      currentPosition = null;
    }

    isFirstTimeRun = settings
        .getBoolean(Utils.PREF_IS_FIRST_TIME_RUN, true);
  }

  public void savePreferences() {
    SharedPreferences settings = PreferenceManager
        .getDefaultSharedPreferences(this);
    SharedPreferences.Editor editor = settings.edit();

    editor.putString(Utils.PREF_MAP_DATA,
        serializeToString(mapData, MapData.class));

    if (hasEditedMapAnnotations) {
      // Save data inside a file in cache directory
      long nano = System.nanoTime();
      Type type = new TypeToken<ArrayList<MapAnnotation>>() {
      }.getType();

      dataFile = new File(getExternalCacheDir(), Utils.DATA_NAME);
      OutputStream os = null;
      try {
        os = new BufferedOutputStream(new FileOutputStream(dataFile));
        os.write(serializeToString(mapAnnotations, type).getBytes("UTF-8"));
      } catch (IOException e) {
        Utils.e("Error opening OFM", e);
      } finally {
        try {
          os.close();
        } catch (IOException e) {
        }
      }
      
      long diff = System.nanoTime() - nano;
      d("SERIALIZE_MAPANNOT " + (diff * 1e-9));
    }

    editor.putString(Utils.PREF_CURRENT_POSITION,
        serializeToString(currentPosition, CurrentPosition.class));

    editor.putBoolean(Utils.PREF_IS_FIRST_TIME_RUN, isFirstTimeRun);

    editor.apply();
  }

  private String serializeToString(Object object, Type type) {
    GsonBuilder builder = new GsonBuilder();
    Gson gson = builder.create();
    return gson.toJson(object);
  }

  private Object deserializeFromString(String json, Type type) {
    GsonBuilder builder = new GsonBuilder();
    Gson gson = builder.create();
    return gson.fromJson(json, type);
  }

  @SuppressWarnings("unchecked")
  private ArrayList<MapAnnotation> deserializeMapAnnotationsFromString(
      String json) {
    ArrayList<MapAnnotation> deserMapAnnotations = null;
    try {
      Type type = new TypeToken<ArrayList<MapAnnotation>>() {
      }.getType();
      deserMapAnnotations = (ArrayList<MapAnnotation>) deserializeFromString(
          json, type);
    } catch (Exception ex) {
      Log.e(Utils.TAG, "Error deserializing Map Annotations", ex);
      deserMapAnnotations = new ArrayList<MapAnnotation>();
    }
    return deserMapAnnotations;
  }

  private void createMapView() {
    mapView = new CopyrightMapView(this);
    mapView.setClickable(true);
    mapView.setBuiltInZoomControls(false);
    mapView.setMapFile(mapFile);
    setContentView(mapView);

    if (Utils.ZOOM_LEVEL_SHOW_DATA_POINTS > 0) {
      mapView.setOnTouchListener(this);
    }
  }

  private void configureUI(boolean isSavedState) {
    createMapView();
    registerForContextMenu(mapView);

    Drawable circle = markerFactory
        .getNormalMarker(Utils.DEFAULT_MARKER_COLOR);
    mapAnnotationsOverlay = createMapAnnotationsOverlay(circle, false);
    mapView.getOverlays().add(mapAnnotationsOverlay);

    Drawable currentPositionMarker = getResources().getDrawable(
        R.drawable.ic_maps_indicator_current_position_anim1);
    currentPositionWithAccuracyMarker = new CurrentPositionDrawable(
        mapView, currentPositionMarker, true, Color.BLUE);
    currentPositionOverlay = new ArrayItemizedOverlay(
        currentPositionWithAccuracyMarker, false);
    mapView.getOverlays().add(currentPositionOverlay);

    bubbleTextOverlay = new ArrayItemizedOverlay(null, false);
    mapView.getOverlays().add(bubbleTextOverlay);

    if (isFirstTimeRun) {
      isFirstTimeRun = false;

      showWelcomeDialog();

      // Zoom on Tokyo
      zoomToInitialPosition();
      if (Utils.START_CURRENT_POSITION) {
        // async
        zoomToCurrentPosition();
      }
    } else {
      if (mapData.mapZoom == 0) {
        // just in case...
        zoomToInitialPosition();
      } else {
        // mapData is valid: Zoom on previous position
        GeoPoint center = new GeoPoint(mapData.mapLatitude,
            mapData.mapLongitude);
        mapView.getController().setCenter(center);
        mapView.getController().setZoom(mapData.mapZoom);
      }

      if (Utils.START_CURRENT_POSITION && !isSavedState) {
        // async
        zoomToCurrentPosition();
      }
    }

    long nano = System.nanoTime();

    for (MapAnnotation mapAnnotation : mapAnnotations) {
      addMarker(mapAnnotation);
    }

    long diff = System.nanoTime() - nano;
    d("DESERMAPANNOT " + (diff * 1e-9));

    if (isSavedState && currentPosition != null) {
      addCurrentPositionMarker();
    } else {
      currentPosition = null;
    }

    checkCurrentZoomForMapAnnotationVisibility();
  }

  private ArrayItemizedOverlay createMapAnnotationsOverlay(
      Drawable defaultMarker, boolean recenter) {
    return new ArrayItemizedOverlay(defaultMarker, recenter) {
      @Override
      protected boolean onTap(int index) {
        MainActivity.this.showBubbleForMapAnnotationInteractive(index);
        return true;
      }

      @Override
      protected boolean onLongPress(int index) {
        if (TextUtils.equals(Utils.UI_MODE, Utils.UI_MODE_FULL)) {
          MainActivity.this.showContextMenuForMapAnnotation(index);
        } else {
          initializeContextForIndex(index);
          starMapAnnotation();
        }
        return true;
      }

      @Override
      public boolean onTap(GeoPoint geoPoint, MapView mapView) {
        // this will call the onTap(int) method above if overlay
        // is hit
        return checkItemHit(geoPoint, mapView, EventType.TAP);
      }

      @Override
      public boolean onLongPress(GeoPoint geoPoint, MapView mapView) {
        boolean hasHit = checkItemHit(geoPoint, mapView,
            EventType.LONG_PRESS);
        if (!hasHit) {
          if (TextUtils.equals(Utils.UI_MODE, Utils.UI_MODE_FULL)) {
            addMapAnnotationInteractive(geoPoint);
          }
        }
        // Note: No other layer can handle double tap
        return true;
      }

    };
  }

  private void addMapAnnotationInteractive(GeoPoint geoPoint) {
    mapAnnotationInContext = new MapAnnotation(geoPoint.getLatitude(),
        geoPoint.getLongitude(), Utils.DEFAULT_MARKER_COLOR);
    mapAnnotations.add(mapAnnotationInContext);
    overlayItemInContext = addMarker(mapAnnotationInContext);
    indexMapAnnotationInContext = mapAnnotations.size() - 1; // last

    isCreation = true;
    isEditingOrCreating = true;

    launchMapAnnotationEditActivity();
  }

  private OverlayItem addMarker(MapAnnotation mapAnnotation) {
    OverlayItem overlay = new OverlayItem(new GeoPoint(
        mapAnnotation.latitude, mapAnnotation.longitude),
        mapAnnotation.title, mapAnnotation.description);
    overlay.setMarker(markerFactory.getMarker(mapAnnotation));
    mapAnnotationsOverlay.addItem(overlay);
    return overlay;
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
    case Utils.CODE_MAP_ANNOTATION_EDIT_REQUEST:
      if (resultCode == RESULT_OK) {
        mapAnnotationInContext.title = data.getExtras().getString(
            Utils.EXTRA_TITLE);
        mapAnnotationInContext.description = data.getExtras()
            .getString(Utils.EXTRA_DESCRIPTION);
        int color = data.getExtras().getInt(Utils.EXTRA_COLOR);
        if (color != mapAnnotationInContext.color) {
          mapAnnotationInContext.color = color;
          overlayItemInContext.setMarker(markerFactory
              .getMarker(mapAnnotationInContext));
          mapAnnotationsOverlay.requestRedraw();
        }
        hasEditedMapAnnotations = true;
        updateBubbleForMapAnnotation(mapAnnotationInContext);
      } else {
        if (isCreation) {
          mapAnnotations.remove(mapAnnotationInContext);
          mapAnnotationsOverlay.removeItem(overlayItemInContext);
        }
      }

      cleanUpContextInformation();

      break;

    case Utils.CODE_CONNECTION_FAILURE_RESOLUTION_REQUEST:
      if (aBoolean) {
        d("PlayServices Resolved");
      } else {
        d("PlayServices Not Resolved");
      }
      break;

    case Utils.CODE_EXPORT_FILE_EXPLORER_REQUEST:
      if (resultCode == RESULT_OK) {
        String dirPath = data.getExtras().getString(
            Utils.EXTRA_FILE_PATH);
        File file = new File(dirPath);
        if (file.canWrite()) {
          Date now = new Date();
          SimpleDateFormat sdf = new SimpleDateFormat(
              "yyyy-MM-dd-HHmmss");
          String fileName = Utils.OFM_FILE_NAME_FOR_EXPORT + "-"
              + sdf.format(now) + "." + Utils.OFM_FILE_EXTENSION;
          file = new File(dirPath, fileName);
          Type type = new TypeToken<ArrayList<MapAnnotation>>() {
          }.getType();
          String ofmData = serializeToString(mapAnnotations, type);
          OutputStream os = null;
          try {
            os = new BufferedOutputStream(
                new FileOutputStream(file));
            os.write(ofmData.getBytes("UTF-8"));
            Toast.makeText(this, getString(R.string.export_done),
                Toast.LENGTH_SHORT).show();
          } catch (IOException ex) {
            e("Error exporting", ex);
          } finally {
            if (os != null) {
              try {
                os.close();
              } catch (IOException e) {
              }
            }
          }
        } else {
          showAlertDialog(R.string.cannot_write, false, null, null);
        }
      }
      break;

    case Utils.CODE_IMPORT_FILE_EXPLORER_REQUEST:
      if (resultCode == RESULT_OK) {
        String filePath = data.getExtras().getString(
            Utils.EXTRA_FILE_PATH);
        File file = new File(filePath);

        String sSavedLocations;
        InputStream is = null;
        try {
          is = new BufferedInputStream(new FileInputStream(new File(
              filePath)));
          sSavedLocations = Utils.convertStreamToString(is);
          Type type = new TypeToken<ArrayList<MapAnnotation>>() {
          }.getType();

          mapAnnotations = (ArrayList<MapAnnotation>) deserializeFromString(
              sSavedLocations, type);
          hasEditedMapAnnotations = true;

          clearBubble();
          mapAnnotationsOverlay.clear();
          for (MapAnnotation mapAnnotation : mapAnnotations) {
            addMarker(mapAnnotation);
          }
          mapAnnotationsOverlay.requestRedraw();

          Toast.makeText(this, getString(R.string.import_done),
              Toast.LENGTH_SHORT).show();
        } catch (IOException ex) {
          e("Error exporting", ex);
        } finally {
          if (is != null) {
            try {
              is.close();
            } catch (IOException e) {
            }
          }
        }
      }
      break;

    }

    super.onActivityResult(requestCode, resultCode, data);
  }

  public void checkCurrentZoomForMapAnnotationVisibility() {
    final byte currentZoomLevel = mapView.getMapPosition().getZoomLevel();

    if (currentZoomLevel < Utils.ZOOM_LEVEL_SHOW_DATA_POINTS) {
      mapView.getOverlays().remove(bubbleTextOverlay);
      mapView.getOverlays().remove(mapAnnotationsOverlay);
    } else {
      if (!mapView.getOverlays().contains(mapAnnotationsOverlay)) {
        // Mapsforge has issue with reading a previously removed
        // overlay: so copy, replace, add
        Drawable circle = markerFactory
            .getNormalMarker(Utils.DEFAULT_MARKER_COLOR);
        ArrayItemizedOverlay newMapAnnotationsOverlay = createMapAnnotationsOverlay(
            circle, false);
        newMapAnnotationsOverlay.addItems(Utils
            .getItems(mapAnnotationsOverlay));
        mapAnnotationsOverlay = newMapAnnotationsOverlay;
        mapView.getOverlays().add(mapAnnotationsOverlay);
      }

      if (!mapView.getOverlays().contains(bubbleTextOverlay)) {
        ArrayItemizedOverlay newBubbleTextOverlay = new ArrayItemizedOverlay(
            null);
        newBubbleTextOverlay
            .addItems(Utils.getItems(bubbleTextOverlay));
        bubbleTextOverlay = newBubbleTextOverlay;
        mapView.getOverlays().add(bubbleTextOverlay);
      }
    }
  }

  private void startExportMapAnnotationsToFile() {
    Intent intent = new Intent();
    intent.setClass(this, FileExplorerActivity.class);
    intent.putExtra(Utils.EXTRA_CHOOSE_DIRECTORY_ONLY, true);
    startActivityForResult(intent, Utils.CODE_EXPORT_FILE_EXPLORER_REQUEST);
  }

  private void startImportMapAnnotationsFromFile() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage(R.string.confirm_import)
        .setNegativeButton(android.R.string.cancel, null)
        .setPositiveButton(android.R.string.ok, new OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
            Intent intent = new Intent();
            intent.setClass(MainActivity.this,
                FileExplorerActivity.class);
            intent.putExtra(Utils.EXTRA_EXTENSION_FILTER,
                Utils.OFM_FILE_EXTENSION);
            startActivityForResult(intent,
                Utils.CODE_IMPORT_FILE_EXPLORER_REQUEST);
          }
        });
    builder.create().show();
  }

  private void showBubbleForMapAnnotationInteractive(int index) {

    MapAnnotation mapAnnotation = mapAnnotations.get(index);

    d("Index " + index + " currentMA4B " + currentMapAnnotationForBubble
        + " MA " + mapAnnotation);

    if (currentMapAnnotationForBubble == mapAnnotation) {
      // toggle
      clearBubble();
      return;
    }

    showBubbleForMapAnnotation(mapAnnotation);
  }

  private void showBubbleForMapAnnotation(MapAnnotation mapAnnotation) {
    currentMapAnnotationForBubble = mapAnnotation;

    View bubbleView = getLayoutInflater().inflate(
        R.layout.bubble_map_annotation, null);
    Utils.setBackground(bubbleView,
        getResources()
            .getDrawable(R.drawable.balloon_overlay_unfocused));
    TextView textViewTitle = (TextView) bubbleView
        .findViewById(R.id.textViewTitle);
    textViewTitle.setText(mapAnnotation.title);
    TextView textViewDescription = (TextView) bubbleView
        .findViewById(R.id.textViewDescription);
    if (mapAnnotation.description == null
        || mapAnnotation.description.isEmpty()) {
      ((ViewGroup) bubbleView).removeView(textViewDescription);
    } else {
      String text = mapAnnotation.description;
      if (Utils.DESCRIPTION_TEMPLATE != null) {
        // apply template
        Object[] args = (Object[]) deserializeFromString(text,
            String[].class);
        text = String.format(Locale.US, Utils.DESCRIPTION_TEMPLATE,
            args);
      }
      textViewDescription.setText(text);
    }
    Bitmap bitmap = Utils.viewToBitmap(this, bubbleView);
    Drawable bd = new BitmapDrawable(getResources(), bitmap);
    bd = ItemizedOverlay.boundCenterBottom(bd);

    GeoPoint gp = new GeoPoint(mapAnnotation.latitude,
        mapAnnotation.longitude);

    bubbleTextOverlay.clear();
    OverlayItem bubble = new OverlayItem(gp, "", "", bd);
    bubbleTextOverlay.addItem(bubble);
    bubbleTextOverlay.requestRedraw();
  }

  private void updateBubbleForMapAnnotation(MapAnnotation mapAnnotation) {
    if (currentMapAnnotationForBubble == mapAnnotation) {
      showBubbleForMapAnnotation(mapAnnotation);
    }
  }

  private void showContextMenuForMapAnnotation(int index) {
    initializeContextForIndex(index);

    runOnUiThread(new Runnable() {
      public void run() {
        openContextMenu(MainActivity.this.mapView);
      }
    });
  }

  private void editMapAnnotation() {
    isCreation = false;
    isEditingOrCreating = true;

    launchMapAnnotationEditActivity();
  }

  private void deleteMapAnnotation(boolean confirm) {
    if (confirm) {
      showAlertDialog(R.string.confirm_delete_map_annotation, true,
          new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
              MainActivity.this.deleteMapAnnotation(false);
            }
          }, null);
    } else {
      mapAnnotations.remove(mapAnnotationInContext);
      mapAnnotationsOverlay.removeItem(overlayItemInContext);

      hasEditedMapAnnotations = true;

      cleanUpContextInformation();
    }
  }

  private void starMapAnnotation() {
    // toggle
    mapAnnotationInContext.isBookmarked = !mapAnnotationInContext.isBookmarked;
    overlayItemInContext.setMarker(markerFactory
        .getMarker(mapAnnotationInContext));
    mapAnnotationsOverlay.requestRedraw();

    hasEditedMapAnnotations = true;

    cleanUpContextInformation();
  }

  private void initializeContextForIndex(int index) {
    indexMapAnnotationInContext = index;
    mapAnnotationInContext = mapAnnotations.get(index);
    overlayItemInContext = Utils.getItem(mapAnnotationsOverlay, index);
  }

  private void cleanUpContextInformation() {
    d("CLEANUPCONTEXT");

    overlayItemInContext = null;
    mapAnnotationInContext = null;
    isCreation = false;
    isEditingOrCreating = false;
  }

  @Override
  public void onSaveInstanceState(Bundle outstate) {
    d("SAVEINSTANCESTATE " + isEditingOrCreating);
    if (isEditingOrCreating) {
      outstate.putInt(Utils.EXTRA_EDITING_CONTEXT_INDEX,
          indexMapAnnotationInContext);
      outstate.putBoolean(Utils.EXTRA_EDITING_CONTEXT_IS_CREATION,
          isCreation);
    } // else do not care
  }

  private void restoreState(Bundle savedInstanceState) {
    d("RESTORESTATE");
    if (savedInstanceState.keySet().contains(
        Utils.EXTRA_EDITING_CONTEXT_INDEX)) {
      indexMapAnnotationInContext = savedInstanceState
          .getInt(Utils.EXTRA_EDITING_CONTEXT_INDEX);
      isCreation = savedInstanceState
          .getBoolean(Utils.EXTRA_EDITING_CONTEXT_IS_CREATION);
      isEditingOrCreating = true;

      mapAnnotationInContext = mapAnnotations
          .get(indexMapAnnotationInContext);
      overlayItemInContext = Utils.getItem(mapAnnotationsOverlay,
          indexMapAnnotationInContext);
    }
  }

  private void launchMapAnnotationEditActivity() {
    Intent intent = new Intent();
    intent.setClass(this, MapAnnotationEditActivity.class);
    intent.putExtra(Utils.EXTRA_IS_NEW, isCreation);
    intent.putExtra(Utils.EXTRA_TITLE, mapAnnotationInContext.title);
    intent.putExtra(Utils.EXTRA_DESCRIPTION,
        mapAnnotationInContext.description);
    intent.putExtra(Utils.EXTRA_COLOR, mapAnnotationInContext.color);
    startActivityForResult(intent, Utils.CODE_MAP_ANNOTATION_EDIT_REQUEST);
  }

  private void clearBubble() {
    bubbleTextOverlay.clear();
    currentMapAnnotationForBubble = null;
  }

  private File createFileFromAsset(File f, String mapName) {
    try {
      AssetManager am = getAssets();
      InputStream inputStream = am.open(mapName);

      OutputStream outputStream = new FileOutputStream(f);
      byte buffer[] = new byte[1024];
      int length = 0;

      while ((length = inputStream.read(buffer)) > 0) {
        outputStream.write(buffer, 0, length);
      }

      outputStream.close();
      inputStream.close();

      return f;
    } catch (IOException e) {
      // Logging exception
    }

    return null;
  }

  private boolean hasNewVersion(File f) {
    try {
      PackageManager pm = getPackageManager();
      ApplicationInfo appInfo = pm.getApplicationInfo(
          getApplicationContext().getPackageName(), 0);
      String appFile = appInfo.sourceDir;
      long lastInstalled = new File(appFile).lastModified();
      return (f.lastModified() < lastInstalled);
    } catch (NameNotFoundException e) {
      e.printStackTrace();
    }
    return true;
  }

  @Override
  public void onConnectionFailed(ConnectionResult connectionResult) {
    if (connectionResult.hasResolution()) {
      try {
        // Start an Activity that tries to resolve the error
        connectionResult.startResolutionForResult(this,
            Utils.CODE_CONNECTION_FAILURE_RESOLUTION_REQUEST);
      } catch (IntentSender.SendIntentException e) {
        // Log the error
        e.printStackTrace();
      }
    } else {
      // If no resolution is available, display a dialog to the user with
      // the error.
      showGooglePlayServicesErrorDialog(connectionResult.getErrorCode());
    }
  }

  private boolean isGooglePlayServicesConnected() {
    // Check that Google Play services is available
    int resultCode = GooglePlayServicesUtil
        .isGooglePlayServicesAvailable(this);
    // If Google Play services is available
    if (ConnectionResult.SUCCESS == resultCode) {
      d("Google Play services is available.");
      return true;
    } else {
      // Google Play services was not available for some reason
      // Display an error dialog
      showGooglePlayServicesErrorDialog(resultCode);
      return false;
    }
  }

  private void showGooglePlayServicesErrorDialog(int errorCode) {
    // Get the error dialog from Google Play services
    Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(errorCode,
        this, Utils.CODE_CONNECTION_FAILURE_RESOLUTION_REQUEST);
    errorDialog.show();
  }

  private void showAlertDialog(int resourceId, boolean showCancel,
      OnClickListener okListener, OnClickListener cancelListener) {
    showAlertDialog(getString(resourceId), showCancel, okListener,
        cancelListener);
  }

  private void showAlertDialog(String message, boolean showCancel,
      OnClickListener okListener, OnClickListener cancelListener) {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage(message).setPositiveButton(android.R.string.ok,
        okListener);
    if (showCancel) {
      builder.setNegativeButton(android.R.string.cancel, cancelListener);
    }

    // Create the AlertDialog object and return it
    builder.create().show();
  }

  private void showWelcomeDialog() {
    String app_name = getString(R.string.app_name);
    showAlertDialog(getString(R.string.welcome, app_name), false, null,
        null);
  }

  @Override
  public void onConnected(Bundle arg0) {
    if (zoomToCurrentPositionOnConnected) {
      zoomToCurrentPositionOnConnected = false;
      zoomToCurrentPosition();
    }
  }

  @Override
  public void onDisconnected() {

  }

  @Override
  public void onLocationChanged(Location location) {
    setProgressBarIndeterminateVisibility(false);

    if (isLocationUpdating) {
      isLocationUpdating = false;
      setCurrentPosition(location);
    }
  }

  private void setCurrentPosition(Location location) {
    currentPosition = new CurrentPosition();

    currentPosition.latitude = location.getLatitude();
    currentPosition.longitude = location.getLongitude();

    if (location.hasAccuracy()) {
      currentPosition.accuracy = location.getAccuracy();
    } else {
      currentPosition.accuracy = 0;
    }

    showCurrentPosition();
    addCurrentPositionMarker();

    checkCurrentZoomForMapAnnotationVisibility();
  }

  private void showCurrentPosition() {
    GeoPoint position = new GeoPoint(currentPosition.latitude,
        currentPosition.longitude);
    byte zoom = mapView.getMapPosition().getZoomLevel();
    if (zoom < Utils.ZOOM_LEVEL_CURRENT_POSITION) {
      zoom = Utils.ZOOM_LEVEL_CURRENT_POSITION;
    }

    mapView.getController().setCenter(position);
    mapView.getController().setZoom(zoom);
  }

  private void addCurrentPositionMarker() {
    GeoPoint position = new GeoPoint(currentPosition.latitude,
        currentPosition.longitude);

    currentPositionWithAccuracyMarker.setAccuracy(currentPosition.accuracy);

    currentPositionOverlay.clear();
    OverlayItem overlay = new OverlayItem();
    overlay.setPoint(position);
    currentPositionOverlay.addItem(overlay);
  }

  @Override
  public boolean onTouch(View v, MotionEvent event) {
    if (v == mapView) {
      checkCurrentZoomForMapAnnotationVisibility();
    }
    return false;
  }

}




Java Source Code List

com.vellut.offlinemap.CircleDrawable.java
com.vellut.offlinemap.ColorPickerAdapter.java
com.vellut.offlinemap.CopyrightMapView.java
com.vellut.offlinemap.CurrentPositionDrawable.java
com.vellut.offlinemap.CurrentPosition.java
com.vellut.offlinemap.FileExplorerActivity.java
com.vellut.offlinemap.MainActivity.java
com.vellut.offlinemap.MapAnnotationEditActivity.java
com.vellut.offlinemap.MapAnnotation.java
com.vellut.offlinemap.MapData.java
com.vellut.offlinemap.MarkerFactory.java
com.vellut.offlinemap.StarDrawable.java
com.vellut.offlinemap.Utils.java