/*
* GeoSolutions Android Map Library - Digital field mapping on Android based devices
* Copyright (C) 2013 GeoSolutions (www.geo-solutions.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package it.geosolutions.android.map;
import it.geosolutions.android.map.activities.GetFeatureInfoLayerListActivity;
import it.geosolutions.android.map.activities.MBTilesLayerOpacitySettingActivity;
import it.geosolutions.android.map.activities.MapActivityBase;
import it.geosolutions.android.map.activities.about.InfoView;
import it.geosolutions.android.map.control.CoordinateControl;
import it.geosolutions.android.map.control.LocationControl;
import it.geosolutions.android.map.control.MapControl;
import it.geosolutions.android.map.control.MapInfoControl;
import it.geosolutions.android.map.control.MarkerControl;
import it.geosolutions.android.map.database.SpatialDataSourceManager;
import it.geosolutions.android.map.dto.MarkerDTO;
import it.geosolutions.android.map.fragment.GenericMenuFragment;
import it.geosolutions.android.map.fragment.sources.SourcesFragment;
import it.geosolutions.android.map.geostore.activities.GeoStoreResourceDetailActivity;
import it.geosolutions.android.map.geostore.activities.GeoStoreResourcesActivity;
import it.geosolutions.android.map.geostore.model.Resource;
import it.geosolutions.android.map.mapstore.model.MapStoreConfiguration;
import it.geosolutions.android.map.mapstore.utils.MapStoreUtils;
import it.geosolutions.android.map.mbtiles.MbTilesLayer;
import it.geosolutions.android.map.model.Attribute;
import it.geosolutions.android.map.model.Feature;
import it.geosolutions.android.map.model.Layer;
import it.geosolutions.android.map.model.MSMMap;
import it.geosolutions.android.map.overlay.MarkerOverlay;
import it.geosolutions.android.map.overlay.items.DescribedMarker;
import it.geosolutions.android.map.overlay.managers.MultiSourceOverlayManager;
import it.geosolutions.android.map.overlay.managers.OverlayManager;
import it.geosolutions.android.map.overlay.switcher.LayerSwitcherFragment;
import it.geosolutions.android.map.preferences.EditPreferences;
import it.geosolutions.android.map.style.StyleManager;
import it.geosolutions.android.map.utils.MapFilesProvider;
import it.geosolutions.android.map.utils.MarkerUtils;
import it.geosolutions.android.map.utils.SpatialDbUtils;
import it.geosolutions.android.map.utils.StorageUtils;
import it.geosolutions.android.map.view.AdvancedMapView;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.mapsforge.android.maps.BackgroundSourceType;
import org.mapsforge.android.maps.MapView;
import org.mapsforge.android.maps.mapgenerator.MapRenderer;
import org.mapsforge.android.maps.mapgenerator.TileCache;
import org.mapsforge.android.maps.mapgenerator.databaserenderer.DatabaseRenderer;
import org.mapsforge.android.maps.mapgenerator.mbtiles.MbTilesDatabaseRenderer;
import org.mapsforge.core.model.GeoPoint;
import org.mapsforge.core.model.MapPosition;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.Toast;
import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
/**
* This is an implementation of the custom view for the map component.
* Allows to be started in 2 models:
* * MODE_VIEW : does not allow to change the markers position
* * MODE_EDIT : allow to change marker position and select a feature to use to replace current markers
*
*
* @author Lorenzo Natali (lorenzo.natali@geo-solutions.it)
*
*
*/
public class MapsActivity extends MapActivityBase {
// default path for files
private static final File MAP_DIR = MapFilesProvider.getBaseDirectoryFile();
private static final File MAP_FILE = MapFilesProvider.getBackgroundMapFile();
//------------------------------------------------------
// PARAMETERS FOR INTENT
//------------------------------------------------------
/**
* Inner class for references of the managed Extras in the Intent
* @author Lorenzo Natali (www.geo-solutions.it)
*
*/
public final static class PARAMETERS {
public static final String FEATURE_ID_FIELD = "FEATURE_ID_FIELD";
public static final String MARKERS = "MARKERS";
public static final String RES_ID = "ID";
public static final int MODE_VIEW =0;
public static final int MODE_EDIT =1;
public static final int MODE_SCREEN =2;
public static final String LON = "LON";
public static final String LAT = "LAT";
public static final String ZOOM_LEVEL = "ZOOM_LEVEL";
public static final String ZOOM_LEVEL_MIN = "ZOOM_LEVEL_MIN";
public static final String ZOOM_LEVEL_MAX = "ZOOM_LEVEL_MAX";
public static final String RESOURCE = "RESOURCE";
public static final String GEOSTORE_URL = "GEOSTORE_URL";
public static final String CONFIRM_ON_EXIT = "CONFIRM_ON_EXIT";
public static final String CUSTOM_MAPINFO_CONTROL = "CustomMapInfoControlParcel";
public static final String DRAWER_MODE = "DRAWER_MODE";
}
public enum DrawerMode{
BOTH,
ONLY_LEFT,
NONE
}
private DrawerMode mDrawerMode;
//------------------------------------------------------
// PREFERENCES
//------------------------------------------------------
/**
* The default number of tiles in the file system cache.
*/
public static final int FILE_SYSTEM_CACHE_SIZE_DEFAULT = 250;
/**
* The maximum number of tiles in the file system cache.
*/
public static final int FILE_SYSTEM_CACHE_SIZE_MAX = 500;
//------------------------------------------------------
// SAVE INSTANCE STATE BUNDLE PARAMETERS
//------------------------------------------------------
private boolean dbLoaded;
//------------------------------------------------------
//PUBLIC VARIABLES
//------------------------------------------------------
public AdvancedMapView mapView;
public OverlayManager overlayManager;
//------------------------------------------------------
// CONSTANTS
//------------------------------------------------------
public static final int MAPSTORE_REQUEST_CODE = 1;
/** FEATURE_DEFAULT_ID */
private static final String FEATURE_DEFAULT_ID = "OGC_FID";
/** DB_LOADED_FLAG */
private static final String DB_LOADED_FLAG = "dbLoaded";
/** DATAPROPERTIES_REQUEST_CODE */
public static final int DATAPROPERTIES_REQUEST_CODE = 671;
/** ADD LAYERS REQUEST_CODE */
public static final int LAYER_ADD = 98;
private static final String FEATUREIDFIELD_FLAG = "fidField";
/** choosen featureID field */
private String featureIdField;
/** CANCONFRIM_FLAG */
private static final String CANCONFRIM_FLAG = "canConfirm_flag";
public static final String MAPSTORE_CONFIG = "MAPSTORE_CONFIG";
public static final String LAYERS_TO_ADD = "LAYERS_TO_ADD";
public static final String MSM_MAP = "MSM_MAP";
private static boolean canConfirm;
/**
* LAYOUT PARAMETERS
*/
private DrawerLayout mDrawerLayout;
private View mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;
private View mLayerMenu;
private MultiSourceOverlayManager layerManager;
private ActionMode currentActionMode;
/**
* BACK BUTTON PARAMETER
private static final long delay = 2000L;
private boolean mRecentlyBackPressed = false;
private Handler mExitHandler = new Handler();
private Runnable mExitRunnable = new Runnable() {
@Override
public void run() {
mRecentlyBackPressed=false;
}
};
*/
/**
* Initialize Application and restores state
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
// setup loading
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
getSupportActionBar();
setSupportProgressBarIndeterminateVisibility(false);
super.onCreate(savedInstanceState);
if(getIntent() != null && getIntent().hasExtra(PARAMETERS.DRAWER_MODE)){
mDrawerMode = DrawerMode.values()[getIntent().getIntExtra(PARAMETERS.DRAWER_MODE, 0)];
switch (mDrawerMode) {
case BOTH:
setContentView(R.layout.main);
break;
case ONLY_LEFT:
setContentView(R.layout.main_only_left_drawer);
break;
case NONE:
setContentView(R.layout.main_no_drawers);
break;
}
}else{
mDrawerMode = DrawerMode.BOTH;
//
// LAYOUT INITIALIZATION
//
setContentView(R.layout.main);
//Setup the left menu (Drawer)
}
//
// MAP INITIALIZATION
//
//create overlay manager
boolean mapLoaded = initMap(savedInstanceState);
layerManager = new MultiSourceOverlayManager(mapView);
overlayManager=layerManager;
//setup slide menu(es)
setupDrawerLayout();
dbLoaded = initDb();
//if something went wrong durind db and map initialization,
// we should stop
if (!mapLoaded && !dbLoaded) {
//TODO: notify the user the problem
Toast.makeText(this, "DB not loaded", Toast.LENGTH_LONG).show();//TODO i18n
}
//
// LEFT MENU INITIALIZATION
//
setupLeftMenu(savedInstanceState, layerManager);
// CONTEXT MENU
this.registerForContextMenu(mapView);
mapView.getMapScaleBar().setShowMapScaleBar(true);// TODO preferences;
overlayManager.setMarkerOverlay(new MarkerOverlay());
createMarkers(savedInstanceState);
String action = getIntent().getAction();
if(Intent.ACTION_VIEW.equals(action)){
// prevent editing
canConfirm = false;
} else {
// Default edit
canConfirm = true;
this.addConfirmButton();
}
addControls(savedInstanceState);
centerMapFile();
loadFromBundle();
}
/**
* load a map from bundle
*/
private void loadFromBundle() {
Bundle data = getIntent().getExtras();
if(data == null) return;
Resource resource = (Resource) data.getSerializable(PARAMETERS.RESOURCE);
if(resource!=null){
String geoStoreUrl = data.getString(PARAMETERS.GEOSTORE_URL);
loadGeoStoreResource(resource, geoStoreUrl);
}
// if(data.containsKey(MSM_MAP)){
// layerManager.loadMap((MSMMap)data.getSerializable(MSM_MAP));
//
// }
//
// ArrayList<Layer> layersToAdd = (ArrayList<Layer>) data.getSerializable(LAYERS_TO_ADD);
// if(layersToAdd != null){
// addLayers(layersToAdd);
// }
}
/**
* Creates/Restore the layer switcher or restore the old one and add
* all other menu
* @param savedInstanceState
* @param layerManager
*/
private void setupLeftMenu(Bundle savedInstanceState,
MultiSourceOverlayManager layerManager) {
//work on fragment management
FragmentManager fManager = getSupportFragmentManager();
LayerSwitcherFragment osf;
if(savedInstanceState !=null){
osf= (LayerSwitcherFragment)fManager.findFragmentById(R.id.left_drawer_container);
if(osf == null){
Log.e("MAPSACTIVITY", "unable to restore layer switcher");
}
layerManager.setLayerChangeListener(osf);
layerManager.restoreInstanceState(savedInstanceState);
}else{
layerManager.defaultInit();
// @SuppressWarnings("unchecked")
// ArrayList<Layer> layers = (ArrayList<Layer>) LocalPersistence.readObjectFromFile(this, LocalPersistence.CURRENT_MAP);
// if(layers != null){
// layerManager.setLayers(layers);
// }else{
if(getIntent() != null && getIntent().getExtras() != null && getIntent().getExtras().containsKey(MSM_MAP)){
layerManager.loadMap((MSMMap)getIntent().getExtras().getSerializable(MSM_MAP));
}else{
boolean dontLoadMBTileLayer = MapFilesProvider.getBackgroundSourceType() == BackgroundSourceType.MBTILES ? true : false;
MSMMap map = SpatialDbUtils.mapFromDb(dontLoadMBTileLayer);
StorageUtils.setupSources(this);
//This adds layers also if its called loadMap but it will not order layers
//layerManager.loadMap(map);
//so use this instead
addLayersOrdered(map.layers);
}
// }
//setup left drawer fragments
if(mDrawerMode != DrawerMode.NONE){
osf = new LayerSwitcherFragment();
layerManager.setLayerChangeListener(osf);
FragmentTransaction fragmentTransaction = fManager.beginTransaction();
fragmentTransaction.add(R.id.left_drawer_container,osf);
GenericMenuFragment other = new GenericMenuFragment();
//fragmentTransaction.add(R.id.right_drawer, other);
if(mDrawerMode == DrawerMode.BOTH){
SourcesFragment sf = new SourcesFragment();
fragmentTransaction.add(R.id.right_drawer, sf);
}
fragmentTransaction.commit();
}
}
}
/**
* Setup the Drawer as a left menu and layer's menu as the right one
*/
private void setupDrawerLayout() {
switch(mDrawerMode){
case BOTH:
case ONLY_LEFT:
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (View) findViewById(R.id.left_drawer);
if(mDrawerMode == DrawerMode.BOTH){
mLayerMenu = (View) findViewById(R.id.right_drawer);
}
mDrawerToggle = new ActionBarDrawerToggle(
this, /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */
R.string.drawer_open, /* "open drawer" description */
R.string.drawer_close /* "close drawer" description */
) {
private CharSequence mTitle=getTitle();
/** Called when a drawer has settled in a completely closed state. */
public void onDrawerClosed(View view) {
//getSupportActionBar().setTitle(mTitle);
supportInvalidateOptionsMenu();
if (currentActionMode != null){
currentActionMode.finish();
}
}
/** Called when a drawer has settled in a completely open state. */
public void onDrawerOpened(View drawerView) {
mTitle = getSupportActionBar().getTitle();
getSupportActionBar().setTitle(R.string.drawer_title);
supportInvalidateOptionsMenu();
}
};
// Set the drawer toggle as the DrawerListener
mDrawerLayout.setDrawerListener(mDrawerToggle);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
//layerList
break;
case NONE:
break;
}
}
/**
* Resume the state of:
*
* * tile cache
* * Controls
*/
@Override
protected void onResume() {
super.onResume();
loadPersistencePreferences();
checkIfMapViewNeedsBackgroundUpdate();
//Refresh control beacuse any changes can be changed
for(MapControl mic : mapView.getControls()){
mic.refreshControl(GetFeatureInfoLayerListActivity.BBOX_REQUEST, GetFeatureInfoLayerListActivity.BBOX_REQUEST, null);
}
// Some debug
Intent i = getIntent();
if(i!=null){
String a = i.getAction();
Log.v("MapsActivity onResume", "Action:"+a);
}
}
/**
* add the confirm button to the control bar
*/
private void addConfirmButton() {
Log.v("MapsActivity", "adding confirm button");
ImageButton b = (ImageButton)findViewById(R.id.button_confirm_marker_position);
b.setVisibility(View.VISIBLE);
final MapsActivity activity = this;
b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(!canConfirm){
Toast.makeText(activity, R.string.error_unable_getfeature_db, Toast.LENGTH_LONG).show();
return;
}
new AlertDialog.Builder(activity)
.setTitle(R.string.button_confirm_marker_position_title)
.setMessage(R.string.button_confirm_marker_position)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent returnIntent = new Intent();
//get current markers
ArrayList<DescribedMarker> markers = overlayManager.getMarkerOverlay().getMarkers();
//serialize markers in the response
returnIntent.putParcelableArrayListExtra(MapsActivity.PARAMETERS.MARKERS,MarkerUtils.getMarkersDTO(markers));
setResult(RESULT_OK,returnIntent);
finish();
return;
//if you don't want to return data:
// setResult(RESULT_CANCELED, returnIntent);
// finish();
// activity.finish();
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// do nothing
}
})
.show();
}
});
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean(DB_LOADED_FLAG, dbLoaded);
savedInstanceState.putString(FEATUREIDFIELD_FLAG, featureIdField);
savedInstanceState.putBoolean(CANCONFRIM_FLAG, canConfirm);
//MARKERS
//get current markers
overlayManager.saveInstanceState(savedInstanceState);
for(MapControl mc : mapView.getControls()){
mc.saveState(savedInstanceState);
}
}
/**
* Save the layer state
*/
@Override
public void onPause() {
super.onPause();
Log.d(MapsActivity.class.getSimpleName(), "onPause saving layers");
// LocalPersistence.writeObjectToFile(this, layerManager.getLayers() , LocalPersistence.CURRENT_MAP);
}
/**
* Force a double tap to close the app
@Override
public void onBackPressed() {
//You may also add condition if (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount() != 0) // in case of Fragment-based add
if (mRecentlyBackPressed) {
mExitHandler.removeCallbacks(mExitRunnable);
mExitHandler = null;
super.onBackPressed();
}
else
{
mRecentlyBackPressed = true;
Toast.makeText(this, "press again to exit", Toast.LENGTH_SHORT).show();
mExitHandler.postDelayed(mExitRunnable, delay);
}
}
*/
/**
* Ask to confirm when exit
*/
@Override
public void onBackPressed() {
confirmExit();
}
/**
* Show a confirm message to exit
*/
public void confirmExit() {
boolean confirmOnExit = getIntent().getExtras().getBoolean(MapsActivity.PARAMETERS.CONFIRM_ON_EXIT ,true);
if(confirmOnExit){
new AlertDialog.Builder(this)
.setTitle(R.string.button_confirm_exit_title)
.setMessage(R.string.button_confirm_exit)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
return;
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// do nothing
}
})
.show();
}else{
finish();
}
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
dbLoaded = savedInstanceState.getBoolean(DB_LOADED_FLAG);
featureIdField = savedInstanceState.getString(FEATUREIDFIELD_FLAG);
canConfirm = savedInstanceState.getBoolean(CANCONFRIM_FLAG);
//Restore state of the controls?
}
/**
* creates the menu of the map
*/
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.contextmenu_map, (Menu) menu);
switch (mDrawerMode) {
case BOTH:
inflater.inflate(R.menu.actionmenu_map, (Menu) menu);
break;
default:
break;
}
}
/**
* Enable and disable Overlays adding / removing from the map
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//int itemId = item.getItemId();
//Drawer part
if (item.getItemId() == android.R.id.home) {
if (mDrawerList != null && mDrawerLayout.isDrawerOpen(mDrawerList)) {
mDrawerLayout.closeDrawer(mDrawerList);
} else {
if(mDrawerList != null){
mDrawerLayout.openDrawer(mDrawerList);
}
if(mLayerMenu!=null){
mDrawerLayout.closeDrawer(mLayerMenu);
}
}
//layer menu part
}else if(item.getItemId() == R.id.layer_menu_action){
if (mLayerMenu!=null && mDrawerLayout.isDrawerOpen(mLayerMenu)) {
mDrawerLayout.closeDrawer(mLayerMenu);
} else {
if(mLayerMenu!=null){
mDrawerLayout.openDrawer(mLayerMenu);
}
if(mDrawerList != null){
mDrawerLayout.closeDrawer(mDrawerList);
}
}
}else if(item.getItemId() == R.id.settings){
Intent pref = new Intent(this,EditPreferences.class);
startActivity(pref);
}else if(item.getItemId() == R.id.infoview){
Intent info = new Intent(this,InfoView.class);
startActivity(info);
}else if(item.getItemId() == R.id.exitview){
confirmExit();
}
return super.onOptionsItemSelected(item);
}
/**
* Create the markers and add them to the MarkerOverlay
* Gets it from the Intent or from the savedInstanceState
* Assign them the proper <GeoPoint> if missing
* @param savedInstanceState
*/
private void createMarkers(Bundle savedInstanceState) {
List<MarkerDTO> markerDTOs=null;
// add the OverlayItem to the ArrayItemizedOverlay
ArrayList<DescribedMarker> markers= null;
if (savedInstanceState != null) {
markerDTOs = savedInstanceState.getParcelableArrayList(MapsActivity.PARAMETERS.MARKERS);
markers= MarkerUtils.markerDTO2DescribedMarkers(this,markerDTOs);
}else{
markerDTOs = getIntent().getParcelableArrayListExtra(MapsActivity.PARAMETERS.MARKERS);
markers= MarkerUtils.markerDTO2DescribedMarkers(this,markerDTOs);
//retrieve geopoint if missing
if(getIntent().getExtras() == null){
return;
}
featureIdField = getIntent().getExtras().getString(PARAMETERS.FEATURE_ID_FIELD);
if(featureIdField==null){
featureIdField = FEATURE_DEFAULT_ID;
}
if(!MarkerUtils.assignFeaturesFromDb(markers,featureIdField)){
Toast.makeText(this, R.string.error_unable_getfeature_db, Toast.LENGTH_LONG).show();
canConfirm = false;
//TODO dialog : download features for this area?
}
}
// create an ItemizedOverlay with the default marker
overlayManager.getMarkerOverlay().getOverlayItems().addAll(markers);
}
// TODO move this initialization in a better place (config stuff)
/**
* Initializes the database
* @return true if the initialization was successful
*/
private boolean initDb() {
// init styleManager
StyleManager.getInstance().init(this, MAP_DIR);
// init Db
SpatialDataSourceManager dbManager = SpatialDataSourceManager.getInstance();
try {
//Only if not already loaded some tables
if (dbManager.getSpatialVectorTables(false).size() <= 0) {
dbManager.init(MAP_DIR);
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getSupportMenuInflater().inflate(R.menu.contextmenu_map, menu);
switch (mDrawerMode) {
case BOTH:
getSupportMenuInflater().inflate(R.menu.actionmenu_map, menu);
break;
default:
break;
}
return true;
}
/**
* Initialize the map with Controls and background
* @param savedInstanceState
*
* @return
*/
private boolean initMap(Bundle savedInstanceState) {
//setContentView(R.layout.activity_map);
Log.v("MAP","Map Activated");
this.mapView = (AdvancedMapView) findViewById(R.id.advancedMapView);
// TODO configurable controls
mapView.setClickable(true);
mapView.setBuiltInZoomControls(true);
// mapView.setDebugSettings(new DebugSettings(true, true, false));
// TODO parametrize these zoom levels
mapView.getMapZoomControls().setZoomLevelMax((byte) 24);
mapView.getMapZoomControls().setZoomLevelMin((byte) 1);
// TODO d get this path on initialization
final String filePath = PreferenceManager.getDefaultSharedPreferences(this).getString(MapView.MAPSFORGE_BACKGROUND_FILEPATH, null);
final int type = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(this).getString(MapView.MAPSFORGE_BACKGROUND_RENDERER_TYPE, "0"));
File mapfile = null;
//if the map file was edited in the preferences
if(filePath != null && type == 0){
mapfile = new File(filePath);
}
if(mapfile != null && mapfile.exists()){
//use it
mapView.setMapFile(new File(filePath));
}else if (MAP_FILE!=null) {
Log.i("MAP","setting background file");
mapView.setMapFile(MAP_FILE);
loadPersistencePreferences();
} else {
Log.i("MAP","unable to set background file");
//return false;
}
return true;
}
/**
* Add controls to the mapView and to the Buttons
* @param savedInstanceState
*/
private void addControls(Bundle savedInstanceState) {
String action =getIntent().getAction();
Log.v("MapsActivity", "action: "+action);
//Coordinate Control
mapView.addControl(new CoordinateControl(mapView, true));
List<MapControl> group = new ArrayList<MapControl>();
// Info Control
MapInfoControl ic;
if(getIntent().hasExtra(MapsActivity.PARAMETERS.CUSTOM_MAPINFO_CONTROL)){
ic = (MapInfoControl) getIntent().getParcelableExtra(MapsActivity.PARAMETERS.CUSTOM_MAPINFO_CONTROL) ;
ic.activity = this;
ic.mapView = mapView;
ic.instantiateListener();
}else{
ic= new MapInfoControl(mapView,this);
}
ic.setActivationButton( (ImageButton)findViewById(R.id.ButtonInfo) );
mapView.addControl(ic);
if(!Intent.ACTION_VIEW.equals(action)){
Log.v("MapsActivity", "Adding MarkerControl");
//Marker Control
MarkerControl mc =new MarkerControl(mapView);
// activation button
ImageButton mcbmb = (ImageButton)findViewById(R.id.ButtonMarker);
mcbmb.setVisibility(View.VISIBLE);
mc.setActivationButton(mcbmb);
// info button // TODO: do we need this button?
ImageButton mcib = (ImageButton)findViewById(R.id.marker_info_button);
mcib.setVisibility(View.VISIBLE);
mc.setInfoButton(mcib);
mapView.addControl(mc);
group.add(mc);
mc.setGroup(group);
mc.setMode(MarkerControl.MODE_EDIT);
}
//My location Control
LocationControl lc =new LocationControl(mapView);
lc.setActivationButton((ImageButton)findViewById(R.id.ButtonLocation));
mapView.addControl(lc);
//create and add group
group.add(ic);
ic.setGroup(group);
//TODO move this in a control
//Set modes for controls
if(Intent.ACTION_VIEW.equals(action)){
ic.setMode(MapInfoControl.MODE_VIEW);
}else if(Intent.ACTION_EDIT.equals(action)){
ic.setMode(MapInfoControl.MODE_EDIT);
//Default edit mode
}else{
ic.setMode(MapInfoControl.MODE_EDIT);
}
if(savedInstanceState!=null){
for(MapControl c:mapView.getControls()){
c.restoreState(savedInstanceState);
}
}
}
/**
* center the map on the markers
*/
public void centerMapFile() {
MarkerOverlay mo = mapView.getMarkerOverlay();
MapPosition mp = mapView.getMapViewPosition().getMapPosition();
Intent intent = getIntent();
if(intent.hasExtra(PARAMETERS.LAT) && intent.hasExtra(PARAMETERS.LON) && intent.hasExtra(PARAMETERS.ZOOM_LEVEL)){
double lat = intent.getDoubleExtra(PARAMETERS.LAT, 43.68411);
double lon = intent.getDoubleExtra(PARAMETERS.LON, 10.84899);
byte zoom_level = intent.getByteExtra(PARAMETERS.ZOOM_LEVEL, (byte) 13);
byte zoom_level_min = intent.getByteExtra(PARAMETERS.ZOOM_LEVEL_MIN, (byte) 0);
byte zoom_level_max = intent.getByteExtra(PARAMETERS.ZOOM_LEVEL_MAX, (byte) 30);
/*ArrayList<MarkerDTO> list_marker = intent.getParcelableArrayListExtra(PARAMETERS.MARKERS);
MarkerDTO mark = list_marker.get(0);*/
mp = new MapPosition(new GeoPoint(lat,lon),zoom_level);
mapView.getMapViewPosition().setMapPosition(mp);
mapView.getMapZoomControls().setZoomLevelMin(zoom_level_min);
mapView.getMapZoomControls().setZoomLevelMax(zoom_level_max);
} else {
if(mo!=null){
//support only one marker
MapPosition newMp = MarkerUtils.getMarkerCenterZoom(mo.getMarkers(),mp);
if(newMp!=null){
mapView.getMapViewPosition().setMapPosition(newMp);
}
}
}
}
/**
* Center the map to a point and zoomLevel
* @param pp
* @param zoomlevel
*/
public void setPosition(GeoPoint pp, byte zoomlevel ){
mapView.getMapViewPosition().setMapPosition(new MapPosition(pp,zoomlevel));
}
/**
* Opena the Data List activity
* @param item
* @return
*/
/* (non-Javadoc)
* @see android.app.Activity#onActivityResult(int, int, android.content.Intent)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(MapsActivity.class.getSimpleName(), "MapsActivity onActivityResult");
if(requestCode == LayerSwitcherFragment.OPACITY_SETTIN_REQUEST_ID){
final int newValue = PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getInt(MBTilesLayerOpacitySettingActivity.MBTILES_OPACITY_ID, 192);
ArrayList<Layer> layers = layerManager.getLayers();
for(Layer l : layers){
if(l instanceof MbTilesLayer){
l.setOpacity(newValue);
layerManager.redrawLayer(l);
}
}
//its not necessary to handle the other stuff
return;
}
if(requestCode == GetFeatureInfoLayerListActivity.BBOX_REQUEST && resultCode == RESULT_OK){
//the response can contain a feature to use to replace the current marker
//on the map
manageMarkerSubstitutionAction(data);
}
//controls can be refreshed getting the result of an intent, in this case
// each control knows which intent he sent with their requestCode/resultCode
for(MapControl control : mapView.getControls()){
control.refreshControl(requestCode,resultCode, data);
}
// reload stores in the panel (we do it everyTime, maybe there is a better way
SourcesFragment sf = (SourcesFragment) getSupportFragmentManager().findFragmentById(R.id.right_drawer);
if(sf!=null){
sf.reloadStores();
}
//manager mapstore configuration load
//TODO: with the new interface this will load a map instead of the mapstoreconfig
if(data==null)return;
Bundle b = data.getExtras();
if(requestCode==DATAPROPERTIES_REQUEST_CODE){
mapView.getOverlayController().redrawOverlays();
// close right drawer
if(mLayerMenu!=null){
if (mDrawerLayout.isDrawerOpen(mLayerMenu)) {
mDrawerLayout.closeDrawer(mLayerMenu);
}
}
}
Resource resource = (Resource) data.getSerializableExtra(GeoStoreResourceDetailActivity.PARAMS.RESOURCE);
if(resource!=null){
String geoStoreUrl = data.getStringExtra(GeoStoreResourcesActivity.PARAMS.GEOSTORE_URL);
loadGeoStoreResource(resource, geoStoreUrl);
}
if(b.containsKey(MAPSTORE_CONFIG)){
overlayManager.loadMapStoreConfig((MapStoreConfiguration)b.getSerializable(MAPSTORE_CONFIG));
}
if(b.containsKey(MSM_MAP)){
layerManager.loadMap((MSMMap)b.getSerializable(MSM_MAP));
}
ArrayList<Layer> layersToAdd = (ArrayList<Layer>) b.getSerializable(LAYERS_TO_ADD);
if(layersToAdd != null){
addLayers(layersToAdd);
}
}
/**
* Add layers to the map
* @param layersToAdd
*/
private void addLayers(ArrayList<Layer> layersToAdd) {
ArrayList<Layer> layers =new ArrayList<Layer> (layerManager.getLayers());
layers.addAll(layersToAdd);
layerManager.setLayers(layers);
// close right drawer
if(mLayerMenu!=null){
if (mDrawerLayout.isDrawerOpen(mLayerMenu)) {
mDrawerLayout.closeDrawer(mLayerMenu);
}
}
}
/**
* Load a geostore resource on the map
* @param resource the resource id
* @param geoStoreUrl
*/
private void loadGeoStoreResource(Resource resource, String geoStoreUrl) {
MapStoreUtils.loadMapStoreConfig(geoStoreUrl, resource, this);
// close right drawer
if(mLayerMenu!=null){
if (mDrawerLayout.isDrawerOpen(mLayerMenu)) {
mDrawerLayout.closeDrawer(mLayerMenu);
}
}
}
/**
* Manages the marker substitution
*/
private void manageMarkerSubstitutionAction(Intent data) {
@SuppressWarnings("unchecked")
ArrayList<Attribute> arrayList = (ArrayList<Attribute>) data.getExtras().getSerializable(GetFeatureInfoLayerListActivity.RESULT_FEATURE_EXTRA);
Feature f = new Feature(arrayList);
String layer = data.getExtras().getString(GetFeatureInfoLayerListActivity.LAYER_FEATURE_EXTRA);
//TODO parameterize id column name
Attribute a = f.getAttribute(featureIdField);
String attributeValue=null;
if(a!=null){
attributeValue = a.getValue();
}
replaceMarker(layer, featureIdField, attributeValue,f);
}
/**
* Replace the default marker with position and properties from the arguments
* @param layer
* @param attributeName
* @param attributeValue
* @param f
*/
private void replaceMarker(String layer, String attributeName, String attributeValue, Feature f) {
DescribedMarker marker = getDefaultMarker();
if(marker != null){
setMarkerProperties(layer, attributeName, attributeValue, attributeValue, marker,f);
}
}
/**
* @param layer
* @param attributeName
* @param a
* @param attributeValue
* @param marker
* @param f
*/
private void setMarkerProperties(String layer, String attributeName,
String id, String attributeValue, DescribedMarker marker, Feature f) {
GeoPoint p = SpatialDbUtils.getGeopointFromLayer(layer, attributeName, attributeValue);
//get Only the first
if(p!=null){
//TODO ask if you want to change
//if yes move and center map
marker.setGeoPoint(p);
marker.setFeatureId(id);
marker.setFeature(f);
mapView.redraw();
canConfirm = true;
}else{
Toast.makeText(this, R.string.error_getting_data_from_database, Toast.LENGTH_LONG).show();
}
}
/**
* get a marker from markerOverlay.
* The one highlighted or the first one
* @return
*/
private DescribedMarker getDefaultMarker() {
MarkerOverlay m = mapView.getMarkerOverlay();
//add the marker overlay if not present
if(m==null){
overlayManager.toggleOverlayVisibility(R.id.markers, true);
m = mapView.getMarkerOverlay();
}
DescribedMarker marker = m.getHighlighted();
if (marker == null) {
List<DescribedMarker> markers = m.getMarkers();
if (markers.size() > 0) {
marker = markers.get(0);
}else{
//TODO add a new marker
}
}
return marker;
}
@Override
public void onPostCreate(Bundle savedInstanceState){
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
TileCache fileSystemTileCache = this.mapView.getFileSystemTileCache();
Log.v("PERSISTENCE","capacity"+fileSystemTileCache.getCapacity()+",persistence:"+fileSystemTileCache.isPersistent());
if(mDrawerToggle != null){
mDrawerToggle.syncState();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(mDrawerToggle != null){
mDrawerToggle.onConfigurationChanged(newConfig);
}
// Checks the orientation of the screen for landscape and portrait and set portrait mode always
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}
/**
* Load tile caching preferences
* used sharedPreferences :
* * TileCachePersistence
* * TileCacheSize
*/
public void loadPersistencePreferences(){
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean persistent = sharedPreferences.getBoolean("TileCachePersistence", true);
Log.v("PERSISTENCE","cache size:"+sharedPreferences.getInt("TileCacheSize", FILE_SYSTEM_CACHE_SIZE_DEFAULT)+",persistent"+persistent);
int capacity = Math.min(sharedPreferences.getInt("TileCacheSize", FILE_SYSTEM_CACHE_SIZE_DEFAULT),
FILE_SYSTEM_CACHE_SIZE_MAX);
TileCache fileSystemTileCache = this.mapView.getFileSystemTileCache();
fileSystemTileCache.setPersistent(persistent);
fileSystemTileCache.setCapacity(capacity);
// text size
String textScaleDefault = getString(R.string.preferences_text_scale_default);
this.mapView.setTextScale(Float.parseFloat(sharedPreferences.getString("mapTextScale", textScaleDefault)));
}
/**
* checks if the preferences of the background renderer changed
* if so, the mapview is informed and is cleared and redrawed
*/
public void checkIfMapViewNeedsBackgroundUpdate()
{
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
final boolean thingsChanged = prefs.getBoolean(MapView.MAPSFORGE_BACKGROUND_FILEPATH_CHANGED, false);
if(!thingsChanged)return;
final BackgroundSourceType currentMapRendererType = this.mapView.getMapRendererType();
String filePath = prefs.getString(MapView.MAPSFORGE_BACKGROUND_FILEPATH, null);
final String defaultType = getApplicationContext().getPackageName().equals("it.geosolutions.geocollect.android.app") ? "1" : "0";
BackgroundSourceType type = BackgroundSourceType.values()[Integer.parseInt(prefs.getString(MapView.MAPSFORGE_BACKGROUND_RENDERER_TYPE, defaultType))];
final Editor ed = prefs.edit();
ed.putBoolean(MapView.MAPSFORGE_BACKGROUND_FILEPATH_CHANGED, false);
ed.commit();
File mapFile = new File(filePath);
if(mapFile == null || !mapFile.exists()){
mapFile = MapFilesProvider.getBackgroundMapFile();
filePath = mapFile.getPath();
type = BackgroundSourceType.MAPSFORGE;
}
//1. renderer changed
if(type != currentMapRendererType){
MapRenderer mapRenderer = null;
switch (type) {
case MAPSFORGE:
if(filePath == null){
throw new IllegalArgumentException("no filepath selected to change to mapsforge renderer");
}
mapView.setMapFile(new File(filePath));
mapRenderer = new DatabaseRenderer(mapView.getMapDatabase());
//TODO it was MBTILES with no or dimmed mbtiles layer, add MBTiles layer ?
MSMMap map = SpatialDbUtils.mapFromDb(false);
Log.d(MapsActivity.class.getSimpleName(), "to mapsforge maps includes "+map.layers.size()+" layers");
addLayersOrdered(map.layers);
break;
case MBTILES:
mapRenderer = new MbTilesDatabaseRenderer(getBaseContext(), filePath);
MSMMap map2 = SpatialDbUtils.mapFromDb(true);
layerManager.setLayers(map2.layers);
break;
case GEOCOLLECT:
// TODO
break;
default:
break;
}
if(mDrawerToggle != null){
mDrawerToggle.syncState();
}
mapView.setRenderer(mapRenderer, true);
mapView.clearAndRedrawMapView();
MapFilesProvider.setBackgroundSourceType(type);
}else if(filePath != null && !filePath.equals(mapView.getMapRenderer().getFileName())){
//2.renderer is the same but file changed
switch (type) {
case MAPSFORGE:
mapView.setMapFile(new File(filePath));
break;
case MBTILES:
mapView.setRenderer(new MbTilesDatabaseRenderer(getBaseContext(), filePath), true);
break;
case GEOCOLLECT:
// TODO
break;
default:
break;
}
mapView.clearAndRedrawMapView();
}
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
// mDrawerLayout.openDrawer(mDrawerList);
// return true;
}
return super.onKeyUp(keyCode, event);
}
/**
* Get the current action mode if present
*/
@Override
public void onActionModeStarted(ActionMode mode) {
currentActionMode =mode;
}
@Override
public void onActionModeFinished(ActionMode mode) {
currentActionMode = null;
}
public void addLayersOrdered(final ArrayList<Layer> layers){
ArrayList<Layer> originalLayers = layers;
ArrayList<Layer> orderedLayers = new ArrayList<Layer>();
//check if there is a MBTiles layer which needs to be ordered
boolean layersContainMBTilesLayer = false;
for(Layer l : originalLayers){
if(l instanceof MbTilesLayer){
layersContainMBTilesLayer = true;
break;
}
}
//if there is, add this flag to wait until it has been added
boolean mbTilesAdded = !layersContainMBTilesLayer;
while(!originalLayers.isEmpty()){
final Layer layer = originalLayers.get(originalLayers.size() - 1); // get last
if(layer instanceof MbTilesLayer){
final int currentValue = PreferenceManager.getDefaultSharedPreferences(getBaseContext()).getInt(MBTilesLayerOpacitySettingActivity.MBTILES_OPACITY_ID, 192);
layer.setOpacity(currentValue);
orderedLayers.add(layer);
mbTilesAdded = true;
originalLayers.remove(layer);
Log.d(MapsActivity.class.getSimpleName(), "mbtiles layer added , size "+orderedLayers.size());
}else if(mbTilesAdded == true){
orderedLayers.add(layer);
originalLayers.remove(layer);
Log.d(MapsActivity.class.getSimpleName(), "other added , size "+orderedLayers.size());
}
}
layerManager.setLayers(orderedLayers);
}
public View getLayerMenu(){
return mLayerMenu;
}
public View getDrawerList(){
return mDrawerList;
}
}