package org.osmdroid.forge.app; import android.Manifest; import android.annotation.TargetApi; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; import android.widget.Toast; import org.mapsforge.map.android.rendertheme.AssetsRenderTheme; import org.mapsforge.map.rendertheme.XmlRenderTheme; import org.osmdroid.api.IGeoPoint; import org.osmdroid.events.MapListener; import org.osmdroid.events.ScrollEvent; import org.osmdroid.events.ZoomEvent; import org.osmdroid.mapsforge.MapsForgeTileProvider; import org.osmdroid.mapsforge.MapsForgeTileSource; import org.osmdroid.tileprovider.MapTileProviderArray; import org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants; import org.osmdroid.tileprovider.util.SimpleRegisterReceiver; import org.osmdroid.tileprovider.util.StorageUtils; import org.osmdroid.util.GeoPoint; import org.osmdroid.views.MapView; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * A simple, sample application, to use MapsForge https://github.com/mapsforge/mapsforge * with osmdroid https://github.com/osmdroid/osmdroid map engine. * @author Alex O'Ree */ public class MainActivity extends AppCompatActivity { TextView currentCenter; MapView mMap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /** * super important to configure some of the mapsforge settings first */ MapsForgeTileSource.createInstance(this.getApplication()); /* not sure how important these are.... MapFile.wayFilterEnabled = true; MapFile.wayFilterDistance = 20; MapWorkerPool.DEBUG_TIMING = true; MapWorkerPool.NUMBER_OF_THREADS = MapWorkerPool.DEFAULT_NUMBER_OF_THREADS; */ //enable these for additional log output //OpenStreetMapTileProviderConstants.DEBUG_TILE_PROVIDERS = true; //OpenStreetMapTileProviderConstants.DEBUGMODE = true; // Request permissions to support Android Marshmallow and above devices (api-23) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { checkPermissions(); } setContentView(R.layout.activity_main); mMap = (MapView) findViewById(R.id.mapview); currentCenter=(TextView) findViewById(R.id.currentCenter); //first let's up our map source, mapsforge needs you to explicitly specify which map files to load //this bit does some basic file system scanning Set<File> mapfiles = findMapFiles(); //do a simple scan of local storage for .map files. File[] maps = new File[mapfiles.size()]; maps = mapfiles.toArray(maps); if (maps.length==0){ //show a warning that no map files were found AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder( this); // set title alertDialogBuilder.setTitle("No Mapsforge files found"); // set dialog message alertDialogBuilder .setMessage("In order to render map tiles, you'll need to either create or obtain mapsforge .map files. See https://github.com/mapsforge/mapsforge for more info.") .setCancelable(false) .setPositiveButton("Yes",new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog,int id) { } }); // create alert dialog AlertDialog alertDialog = alertDialogBuilder.create(); // show it alertDialog.show(); } else Toast.makeText(this, "Loaded " + maps.length + " map files", Toast.LENGTH_LONG).show(); //this creates the forge provider and tile sources //that's it! //note this example does not using caching yet, so each tile is rendered on the fly, every time //the user browses to an area. This needs to be updated to support sqlite raster image caches //protip: when changing themes, you should also change the tile source name to prevent cached tiles //null is ok here, uses the default rendering theme if it's not set XmlRenderTheme theme = null; try { theme = new AssetsRenderTheme(getApplicationContext(), "renderthemes/","rendertheme-v4.xml"); }catch (Exception ex){ ex.printStackTrace(); } MapsForgeTileProvider forge = new MapsForgeTileProvider( new SimpleRegisterReceiver(this), MapsForgeTileSource.createFromFiles(maps,theme, "rendertheme-v4")); mMap.setTileProvider(forge); mMap.setUseDataConnection(false); mMap.setMultiTouchControls(true); mMap.setBuiltInZoomControls(true); } @Override public void onStart() { super.onStart(); mMap.getController().setCenter(new GeoPoint(0d, 0d)); mMap.getController().setZoom(5); mMap.getController().zoomTo(5); mMap.setMapListener(new MapListener() { @Override public boolean onScroll(ScrollEvent event) { IGeoPoint mapCenter = mMap.getMapCenter(); currentCenter.setText(mapCenter.getLatitude() + "," + mapCenter.getLongitude() + " " + mMap.getZoomLevel()); return false; } @Override public boolean onZoom(ZoomEvent event) { return false; } }); } /** * simple function to scan for paths that match /something/osmdroid/*.map to find mapforge database files * @return */ protected static Set<File> findMapFiles() { Set<File> maps = new HashSet<>(); List<StorageUtils.StorageInfo> storageList = StorageUtils.getStorageList(); for (int i = 0; i < storageList.size(); i++) { File f = new File(storageList.get(i).path + File.separator + "osmdroid" + File.separator); if (f.exists()) { maps.addAll(scan(f)); } } return maps; } static private Collection<? extends File> scan(File f) { List<File> ret = new ArrayList<>(); File[] files = f.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { if (pathname.getName().toLowerCase().endsWith(".map")) return true; return false; } }); if (files != null) { for (int i = 0; i < files.length; i++) { ret.add(files[i]); } } return ret; } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } // START PERMISSION CHECK final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124; // Request permissions to support Android Marshmallow and above devices (api-23) @TargetApi(Build.VERSION_CODES.M) private void checkPermissions() { List<String> permissions = new ArrayList<>(); String message = "OSMDroid permissions:"; if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { permissions.add(Manifest.permission.ACCESS_FINE_LOCATION); message += "\nStorage access to store map tiles."; } if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); message += "\nLocation to show user location."; } if (!permissions.isEmpty()) { Toast.makeText(this, message, Toast.LENGTH_LONG).show(); String[] params = permissions.toArray(new String[permissions.size()]); requestPermissions(params, REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); } // else: We already have permissions, so handle as normal } // Request permissions to support Android Marshmallow and above devices. (api-23) @TargetApi(Build.VERSION_CODES.M) @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: { Map<String, Integer> perms = new HashMap<>(); // Initial perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED); perms.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED); // Fill with results for (int i = 0; i < permissions.length; i++) perms.put(permissions[i], grantResults[i]); // Check for ACCESS_FINE_LOCATION and WRITE_EXTERNAL_STORAGE Boolean location = perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; Boolean storage = perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; if (location && storage) { // All Permissions Granted Toast.makeText(MainActivity.this, "All permissions granted", Toast.LENGTH_SHORT).show(); } else if (location) { Toast.makeText(this, "Storage permission is required to store map tiles to reduce data usage and for offline usage.", Toast.LENGTH_LONG).show(); } else if (storage) { Toast.makeText(this, "Location permission is required to show the user's location on map.", Toast.LENGTH_LONG).show(); } else { // !location && !storage case // Permission Denied Toast.makeText(MainActivity.this, "Storage permission is required to store map tiles to reduce data usage and for offline usage." + "\nLocation permission is required to show the user's location on map.", Toast.LENGTH_SHORT).show(); } } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } // END PERMISSION CHECK }