vtm-app: revive / update with latest VTM, closes #90

This commit is contained in:
Emux
2016-07-21 20:22:22 +03:00
parent c67b35a277
commit 436b66be82
133 changed files with 10436 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2012 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import org.oscim.android.MapView;
import org.oscim.map.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class App extends Application {
public final static Logger log = LoggerFactory.getLogger(App.class);
public static Map map;
public static MapView view;
public static Resources res;
public static TileMap activity;
public static POISearch poiSearch;
public static RouteSearch routeSearch;
@Override
public void onCreate() {
super.onCreate();
res = getResources();
}
public static void lockOrientation(Activity activity) {
Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int rotation = display.getRotation();
int tempOrientation = activity.getResources().getConfiguration().orientation;
int orientation = 0;
switch (tempOrientation) {
case Configuration.ORIENTATION_LANDSCAPE:
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90)
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
else
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
break;
case Configuration.ORIENTATION_PORTRAIT:
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_270)
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
else
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
}
activity.setRequestedOrientation(orientation);
}
}

View File

@@ -0,0 +1,31 @@
package org.oscim.app;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.widget.Toast;
public class ConnectionHandler extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
// NetworkInfo mobNetInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE );
if (activeNetInfo != null) {
if (activeNetInfo.isConnected()) {
Toast.makeText(context, "Active Network Type : " + activeNetInfo.getTypeName(),
Toast.LENGTH_SHORT).show();
//if (App.map != null)
// App.map.redrawMap();
}
//Toast.makeText( context, "Active Network Type : " + activeNetInfo.getTypeName(), Toast.LENGTH_SHORT ).show();
}
// if( mobNetInfo != null )
// {
// Toast.makeText( context, "Mobile Network Type : " + mobNetInfo.getTypeName(), Toast.LENGTH_SHORT ).show();
// }
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app;
import android.content.res.Resources;
import java.text.DecimalFormat;
final class FileUtils {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.00 ");
private static final double ONE_GIGABYTE = 1000000000;
private static final double ONE_KILOBYTE = 1000;
private static final double ONE_MEGABYTE = 1000000;
/**
* Formats the given file size as a human readable string, using SI
* prefixes.
*
* @param fileSize the file size to be formatted.
* @param resources a reference to the application resources.
* @return a human readable file size.
* @throws IllegalArgumentException if the given file size is negative.
*/
static String formatFileSize(long fileSize, Resources resources) {
if (fileSize < 0) {
throw new IllegalArgumentException("invalid file size: " + fileSize);
} else if (fileSize < 1000) {
if (fileSize == 1) {
// singular
return "1 " + resources.getString(R.string.file_size_byte);
}
// plural, including zero
return fileSize + " " + resources.getString(R.string.file_size_bytes);
} else {
if (fileSize < ONE_MEGABYTE) {
return DECIMAL_FORMAT.format(fileSize / ONE_KILOBYTE)
+ resources.getString(R.string.file_size_kb);
} else if (fileSize < ONE_GIGABYTE) {
return DECIMAL_FORMAT.format(fileSize / ONE_MEGABYTE)
+ resources.getString(R.string.file_size_mb);
}
return DECIMAL_FORMAT.format(fileSize / ONE_GIGABYTE)
+ resources.getString(R.string.file_size_gb);
}
}
private FileUtils() {
throw new IllegalStateException();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app;
import android.app.Activity;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.WindowManager;
import android.webkit.WebView;
/**
* Simple activity to display the info web page from the assets folder.
*/
public class InfoView extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView webView = new WebView(this);
webView.loadUrl("file:///android_asset/info.xml");
setContentView(webView);
}
@Override
protected void onResume() {
super.onResume();
// check if the full screen mode should be activated
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fullscreen", false)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
}
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2013 Hannes Janetzek
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import org.oscim.android.MapView;
import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
import org.oscim.map.Map;
/**
* MapActivity is the abstract base class which can be extended in order to use
* a {@link MapView}. There are no abstract methods in this implementation which
* subclasses need to override and no API key or registration is required.
* <p/>
* A subclass may create a MapView either via one of the MapView constructors or
* by inflating an XML layout file.
* <p/>
* When the MapActivity is shut down, the current center position, zoom level
* and map file of the MapView are saved in a preferences file and restored in
* the next startup process.
*/
public abstract class MapActivity extends Activity {
private static final String KEY_LATITUDE = "latitude";
private static final String KEY_LONGITUDE = "longitude";
private static final String KEY_MAP_SCALE = "map_scale";
private static final String PREFERENCES_FILE = "MapActivity";
private static boolean containsViewport(SharedPreferences sharedPreferences) {
return sharedPreferences.contains(KEY_LATITUDE)
&& sharedPreferences.contains(KEY_LONGITUDE)
&& sharedPreferences.contains(KEY_MAP_SCALE);
}
protected Map mMap;
protected MapView mMapView;
public Map map() {
return mMap;
}
@Override
protected void onDestroy() {
super.onDestroy();
mMap.destroy();
}
@Override
protected void onPause() {
super.onPause();
Editor editor = getSharedPreferences(PREFERENCES_FILE, MODE_PRIVATE).edit();
editor.clear();
// save the map position
MapPosition mapPosition = new MapPosition();
mMap.viewport().getMapPosition(mapPosition);
GeoPoint geoPoint = mapPosition.getGeoPoint();
editor.putInt(KEY_LATITUDE, geoPoint.latitudeE6);
editor.putInt(KEY_LONGITUDE, geoPoint.longitudeE6);
editor.putFloat(KEY_MAP_SCALE, (float) mapPosition.scale);
editor.commit();
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onStop() {
super.onStop();
mMapView.onPause();
}
/**
* This method is called once by each MapView during its setup process.
*
* @param mapView the calling MapView.
*/
public final void registerMapView(MapView mapView) {
mMapView = mapView;
mMap = mapView.map();
SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCES_FILE,
MODE_PRIVATE);
if (containsViewport(sharedPreferences)) {
// get and set the map position and zoom level
int latitudeE6 = sharedPreferences.getInt(KEY_LATITUDE, 0);
int longitudeE6 = sharedPreferences.getInt(KEY_LONGITUDE, 0);
float scale = sharedPreferences.getFloat(KEY_MAP_SCALE, 1);
MapPosition mapPosition = new MapPosition();
mapPosition.setPosition(latitudeE6 / 1E6, longitudeE6 / 1E6);
mapPosition.setScale(scale);
mMap.setMapPosition(mapPosition);
}
}
}

View File

@@ -0,0 +1,189 @@
package org.oscim.app;
import android.content.SharedPreferences;
import org.oscim.android.cache.TileCache;
import org.oscim.layers.GenericLayer;
import org.oscim.layers.Layer;
import org.oscim.layers.TileGridLayer;
import org.oscim.layers.tile.bitmap.BitmapTileLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.theme.ThemeFile;
import org.oscim.theme.VtmThemes;
import org.oscim.tiling.ITileCache;
import org.oscim.tiling.TileSource;
import org.oscim.tiling.source.UrlTileSource;
import org.oscim.tiling.source.bitmap.DefaultSources;
import org.oscim.tiling.source.mapfile.MapFileTileSource;
import org.oscim.tiling.source.mapnik.MapnikVectorTileSource;
import org.oscim.tiling.source.oscimap4.OSciMap4TileSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MapLayers {
final static Logger log = LoggerFactory.getLogger(MapLayers.class);
private static final String CACHE_DIRECTORY = "/Android/data/org.oscim.app/cache/";
abstract static class Config {
final String name;
public Config(String name) {
this.name = name;
}
abstract TileSource init();
}
static Config[] configs = new Config[]{new Config("OPENSCIENCEMAP4") {
TileSource init() {
return new OSciMap4TileSource();
}
}, new Config("MAPSFORGE") {
TileSource init() {
return new MapFileTileSource().setOption("file",
"/storage/sdcard0/germany.map");
}
}, new Config("MAPNIK_VECTOR") {
TileSource init() {
return new MapnikVectorTileSource();
}
}};
private VectorTileLayer mBaseLayer;
private String mMapDatabase;
private ITileCache mCache;
private GenericLayer mGridOverlay;
private boolean mGridEnabled;
// FIXME -> implement LayerGroup
private int mBackgroundId = -2;
private Layer mBackroundPlaceholder;
private Layer mBackgroundLayer;
public MapLayers() {
mBackroundPlaceholder = new Layer(null) {
};
setBackgroundMap(-1);
}
void setBaseMap(SharedPreferences preferences) {
String dbname = preferences.getString("mapDatabase", "OPENSCIENCEMAP4");
if (dbname.equals(mMapDatabase) && mBaseLayer != null)
return;
TileSource tileSource = null;
for (Config c : configs)
if (c.name.equals(dbname))
tileSource = c.init();
if (tileSource == null) {
tileSource = configs[0].init();
dbname = configs[0].name;
preferences.edit().putString("mapDatabase", dbname).commit();
}
if (tileSource instanceof UrlTileSource) {
mCache = new TileCache(App.activity, CACHE_DIRECTORY, dbname);
mCache.setCacheSize(512 * (1 << 10));
tileSource.setCache(mCache);
} else {
mCache = null;
}
if (mBaseLayer == null) {
mBaseLayer = App.map.setBaseMap(tileSource);
App.map.layers().add(2, new BuildingLayer(App.map, mBaseLayer));
App.map.layers().add(3, new LabelLayer(App.map, mBaseLayer));
} else
mBaseLayer.setTileSource(tileSource);
mMapDatabase = dbname;
}
void setPreferences(SharedPreferences preferences) {
setBaseMap(preferences);
ThemeFile theme = VtmThemes.DEFAULT;
if (preferences.contains("theme")) {
String name = preferences.getString("theme", "DEFAULT");
try {
theme = VtmThemes.valueOf(name);
} catch (IllegalArgumentException e) {
theme = VtmThemes.DEFAULT;
}
}
App.map.setTheme(theme);
// default cache size 20MB
int cacheSize = preferences.getInt("cacheSize", 20);
if (mCache != null)
mCache.setCacheSize(cacheSize * (1 << 20));
}
void enableGridOverlay(boolean enable) {
if (mGridEnabled == enable)
return;
if (enable) {
if (mGridOverlay == null)
mGridOverlay = new TileGridLayer(App.map);
App.map.layers().add(mGridOverlay);
} else {
App.map.layers().remove(mGridOverlay);
}
mGridEnabled = enable;
App.map.updateMap(true);
}
boolean isGridEnabled() {
return mGridEnabled;
}
void setBackgroundMap(int id) {
if (id == mBackgroundId)
return;
App.map.layers().remove(mBackgroundLayer);
mBackgroundLayer = null;
switch (id) {
case R.id.menu_layer_openstreetmap:
mBackgroundLayer = new BitmapTileLayer(App.map, DefaultSources.OPENSTREETMAP.build());
break;
case R.id.menu_layer_naturalearth:
mBackgroundLayer = new BitmapTileLayer(App.map, DefaultSources.NE_LANDCOVER.build());
break;
default:
mBackgroundLayer = mBackroundPlaceholder;
id = -1;
}
if (mBackgroundLayer instanceof BitmapTileLayer)
App.map.setBaseMap((BitmapTileLayer) mBackgroundLayer);
else
App.map.layers().add(1, mBackroundPlaceholder);
mBackgroundId = id;
}
int getBackgroundId() {
return mBackgroundId;
}
public void deleteCache() {
if (mCache != null)
mCache.setCacheSize(0);
}
}

View File

@@ -0,0 +1,309 @@
/*
* Copyright 2012 osmdroidbonuspack: M.Kergall
* Copyright 2012 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import org.osmdroid.location.FourSquareProvider;
import org.osmdroid.location.POI;
import java.util.List;
/**
* Activity showing POIs as a list.
*
* @author M.Kergall
*/
// TODO implement:
// http://codehenge.net/blog/2011/06/android-development-tutorial-
// asynchronous-lazy-loading-and-caching-of-listview-images/
public class POIActivity extends Activity {
AutoCompleteTextView poiTagText;
POIAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.items_list);
ListView list = (ListView) findViewById(R.id.items);
Intent myIntent = getIntent();
final List<POI> pois = App.poiSearch.getPOIs();
final int currentNodeId = myIntent.getIntExtra("ID", -1);
POIAdapter adapter = new POIAdapter(this, pois);
mAdapter = adapter;
list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long index) {
//log.debug("poi on click: " + position);
Intent intent = new Intent();
intent.putExtra("ID", position);
setResult(RESULT_OK, intent);
finish();
}
});
list.setAdapter(adapter);
list.setSelection(currentNodeId);
// POI search interface:
String[] poiTags = getResources().getStringArray(R.array.poi_tags);
poiTagText = (AutoCompleteTextView) findViewById(R.id.poiTag);
ArrayAdapter<String> textadapter = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line,
poiTags);
poiTagText.setAdapter(textadapter);
// Button setPOITagButton = (Button) findViewById(R.id.buttonSetPOITag);
// setPOITagButton.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// hideKeyboard();
// //Start search:
// App.poiSearch.getPOIAsync(poiTagText.getText().toString());
// }
// });
// FIXME!
Button btn = (Button) findViewById(R.id.pois_btn_flickr);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hideKeyboard();
App.poiSearch.getPOIAsync(POISearch.TAG_FLICKR);
}
});
btn = (Button) findViewById(R.id.pois_btn_nominatim);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hideKeyboard();
String text = poiTagText.getText().toString();
if (text == null || text.length() == 0)
App.poiSearch.getPOIAsync("bremen");
else
App.poiSearch.getPOIAsync(text);
}
});
btn = (Button) findViewById(R.id.pois_btn_wikipedia);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hideKeyboard();
App.poiSearch.getPOIAsync(POISearch.TAG_WIKIPEDIA);
}
});
btn = (Button) findViewById(R.id.pois_btn_foursquare);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hideKeyboard();
App.poiSearch.getPOIAsync(POISearch.TAG_FOURSQUARE
+ poiTagText.getText().toString());
}
});
registerForContextMenu(list);
// only show keyboard when nothing in the list yet
if (pois == null || pois.size() == 0) {
poiTagText.postDelayed(new Runnable() {
@Override
public void run() {
InputMethodManager keyboard = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
keyboard.showSoftInput(poiTagText, 0);
}
}, 200);
}
}
private void hideKeyboard() {
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(poiTagText.getWindowToken(), 0);
}
@Override
protected void onNewIntent(Intent intent) {
// from SearchableDictionary Example:
// Because this activity has set launchMode="singleTop", the system calls this method
// to deliver the intent if this activity is currently the foreground activity when
// invoked again (when the user executes a search from this activity, we don't create
// a new instance of this activity, so the system delivers the search intent here)
// handleIntent(intent);
// final ArrayList<POI> pois = intent.getParcelableArrayListExtra("POI");
// final int currentNodeId = intent.getIntExtra("ID", -1);
// POIAdapter adapter = new POIAdapter(this, pois);
// mAdapter.setPOI(pois);
mAdapter.notifyDataSetChanged();
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
if (v.getId() == R.id.items) {
//AdapterView.AdapterContextMenuInfo info =
// (AdapterView.AdapterContextMenuInfo) menuInfo;
//log.debug("list context menu created " + info.position);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.poi_menu, menu);
}
super.onCreateContextMenu(menu, v, menuInfo);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
//log.debug("context menu item selected " + item.getItemId());
if (item.getItemId() == R.id.menu_link) {
AdapterView.AdapterContextMenuInfo info =
(AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
POI poi = (POI) mAdapter.getItem(info.position);
if (poi == null || poi.url == null)
return false;
if (poi.serviceId == POI.POI_SERVICE_4SQUARE) {
FourSquareProvider.browse(this, poi);
return true;
} else {
Intent i = new Intent(Intent.ACTION_VIEW);
i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
i.setData(Uri.parse(poi.url));
startActivity(i);
}
return true;
}
return super.onContextItemSelected(item);
}
class POIObserver extends DataSetObserver {
@Override
public void onChanged() {
mAdapter.notifyDataSetChanged();
}
}
}
class POIAdapter extends BaseAdapter implements OnClickListener {
private Context mContext;
private final List<POI> mPois;
public POIAdapter(Context context, List<POI> pois) {
mContext = context;
mPois = pois;
}
@Override
public int getCount() {
if (mPois == null)
return 0;
return mPois.size();
}
@Override
public Object getItem(int position) {
if (mPois == null)
return null;
return mPois.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View view, ViewGroup viewGroup) {
POI entry = (POI) getItem(position);
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_layout, null);
ViewHolder holder = new ViewHolder();
holder.title = (TextView) view.findViewById(R.id.title);
holder.details = (TextView) view.findViewById(R.id.details);
holder.thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
view.setTag(holder);
}
ViewHolder holder = (ViewHolder) view.getTag();
holder.title.setText((entry.url == null ? "" : "[link] ") + entry.type);
holder.details.setText(entry.description);
entry.fetchThumbnail(holder.thumbnail);
return view;
}
@Override
public void onClick(View arg0) {
//nothing to do.
}
class ViewHolder {
public TextView title;
public TextView details;
public ImageView thumbnail;
}
}

View File

@@ -0,0 +1,320 @@
/*
* Copyright 2012 osmdroid: M.Kergall
* Copyright 2012 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import org.oscim.android.canvas.AndroidGraphics;
import org.oscim.core.BoundingBox;
import org.oscim.core.GeoPoint;
import org.oscim.layers.marker.MarkerItem.HotspotPlace;
import org.oscim.layers.marker.MarkerSymbol;
import org.oscim.map.Map;
import org.osmdroid.location.FlickrPOIProvider;
import org.osmdroid.location.FourSquareProvider;
import org.osmdroid.location.GeoNamesPOIProvider;
import org.osmdroid.location.NominatimPOIProvider;
import org.osmdroid.location.POI;
import org.osmdroid.location.PicasaPOIProvider;
import org.osmdroid.overlays.DefaultInfoWindow;
import org.osmdroid.overlays.ExtendedMarkerItem;
import org.osmdroid.overlays.ItemizedOverlayWithBubble;
import java.util.ArrayList;
import java.util.List;
public class POISearch {
private final ArrayList<POI> mPOIs;
ItemizedOverlayWithBubble<ExtendedMarkerItem> poiMarkers;
MarkerSymbol[] mMarkers;
private final static int MDEFAULT = 0;
private final static int MFLICKR = 1;
private final static int MPICASA = 2;
private final static int MWIKI16 = 3;
private final static int MWIKI32 = 4;
POISearch() {
mPOIs = new ArrayList<POI>();
//POI markers:
final ArrayList<ExtendedMarkerItem> poiItems = new ArrayList<ExtendedMarkerItem>();
poiMarkers = new ItemizedOverlayWithBubble<ExtendedMarkerItem>(App.map,
App.activity,
null,
poiItems,
new POIInfoWindow(App.map));
App.map.layers().add(poiMarkers);
mMarkers = new MarkerSymbol[5];
mMarkers[MDEFAULT] = AndroidGraphics
.makeMarker(App.res.getDrawable(R.drawable.pin), HotspotPlace.BOTTOM_CENTER);
mMarkers[MFLICKR] = AndroidGraphics
.makeMarker(App.res.getDrawable(R.drawable.marker_poi_flickr), null);
mMarkers[MPICASA] = AndroidGraphics
.makeMarker(App.res.getDrawable(R.drawable.marker_poi_picasa_24), null);
mMarkers[MWIKI16] = AndroidGraphics
.makeMarker(App.res.getDrawable(R.drawable.marker_poi_wikipedia_16), null);
mMarkers[MWIKI32] = AndroidGraphics
.makeMarker(App.res.getDrawable(R.drawable.marker_poi_wikipedia_32), null);
}
public List<POI> getPOIs() {
return mPOIs;
}
final static String TAG_WIKIPEDIA = "wikipedia";
final static String TAG_FLICKR = "flickr";
final static String TAG_PICASA = "picasa";
final static String TAG_FOURSQUARE = "foursquare";
//private final static String TAG_NOMINATIM = "nominatim";
class POITask extends AsyncTask<Object, Void, List<POI>> {
String mTag;
@Override
protected List<POI> doInBackground(Object... params) {
mTag = (String) params[0];
if (mTag == null || mTag.equals("")) {
return null;
}
BoundingBox bb = App.map.getBoundingBox(0);
if (mTag.equals(TAG_WIKIPEDIA)) {
GeoNamesPOIProvider poiProvider = new GeoNamesPOIProvider("mkergall");
//ArrayList<POI> pois = poiProvider.getPOICloseTo(point, 30, 20.0);
//Get POI inside the bounding box of the current map view:
return poiProvider.getPOIInside(bb, 30);
//OverpassPOIProvider poiProvider = new OverpassPOIProvider();
//return poiProvider.getPOIInside(bb, "", 0);
} else if (mTag.equals(TAG_FLICKR)) {
FlickrPOIProvider poiProvider = new FlickrPOIProvider("c39be46304a6c6efda8bc066c185cd7e");
return poiProvider.getPOIInside(bb, null, 20);
} else if (mTag.startsWith(TAG_PICASA)) {
PicasaPOIProvider poiProvider = new PicasaPOIProvider(null);
String q = mTag.substring(7);
return poiProvider.getPOIInside(bb, q, 20);
} else if (mTag.startsWith(TAG_FOURSQUARE)) {
FourSquareProvider poiProvider = new FourSquareProvider(null, null);
String q = mTag.substring(10);
// String q = mTag.substring("picasa".length());
return poiProvider.getPOIInside(bb, q, 40);
} else {
NominatimPOIProvider poiProvider = new NominatimPOIProvider();
//poiProvider.setService(NominatimPOIProvider.NOMINATIM_POI_SERVICE);
poiProvider.setService(NominatimPOIProvider.MAPQUEST_POI_SERVICE);
//pois = poiProvider.getPOIAlong(mRoute.getRouteLow(), mTag, 100, 2.0);
return poiProvider.getPOIInside(bb, mTag, 10);
}
}
@Override
protected void onPostExecute(List<POI> pois) {
if (mTag.equals("")) {
//no search, no message
} else if (pois == null) {
Toast.makeText(App.activity,
"Technical issue when getting " + mTag + " POI.",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(App.activity,
pois.size() + " " + mTag + " entries found",
Toast.LENGTH_SHORT).show();
// if (mTag.equals("flickr") || mTag.startsWith("picasa") || mTag.equals("wikipedia"))
// startAsyncThumbnailsLoading(mPOIs);
}
updateUIWithPOI(pois);
}
}
void updateUIWithPOI(List<POI> pois) {
mPOIs.clear();
if (pois == null) {
showPOIActivity(true);
App.map.updateMap(true);
return;
}
mPOIs.addAll(pois);
for (POI poi : pois) {
String desc = null;
String name = null;
if (poi.serviceId == POI.POI_SERVICE_NOMINATIM) {
name = poi.description;
String[] split = name.split(", ");
if (split != null && split.length > 1) {
name = split[0];
desc = split[1];
for (int i = 2; i < 3 && i < split.length; i++)
desc += "," + split[i];
}
} else {
desc = poi.description;
}
ExtendedMarkerItem poiMarker =
new ExtendedMarkerItem(poi.type + (name == null ? "" : ": " + name), desc,
poi.location);
MarkerSymbol marker = null;
if (poi.serviceId == POI.POI_SERVICE_NOMINATIM) {
marker = mMarkers[MDEFAULT];
} else if (poi.serviceId == POI.POI_SERVICE_GEONAMES_WIKIPEDIA) {
if (poi.rank < 90)
marker = mMarkers[MWIKI16];
else
marker = mMarkers[MWIKI32];
} else if (poi.serviceId == POI.POI_SERVICE_FLICKR) {
marker = mMarkers[MFLICKR];
} else if (poi.serviceId == POI.POI_SERVICE_PICASA) {
marker = mMarkers[MPICASA];
poiMarker.setSubDescription(poi.category);
} else if (poi.serviceId == POI.POI_SERVICE_4SQUARE) {
marker = mMarkers[MDEFAULT];
poiMarker.setSubDescription(poi.category);
}
poiMarker.setMarker(marker);
//thumbnail loading moved in POIInfoWindow.onOpen for better performances.
poiMarker.setRelatedObject(poi);
poiMarkers.addItem(poiMarker);
}
showPOIActivity(true);
App.map.updateMap(true);
}
private void showPOIActivity(boolean setNew) {
// show or update
Intent intent = new Intent(App.activity, POIActivity.class);
intent.putExtra("ID", poiMarkers.getBubbledItemId());
if (setNew)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
App.activity.startActivityForResult(intent, TileMap.POIS_REQUEST);
}
void getPOIAsync(String tag) {
poiMarkers.removeAllItems();
new POITask().execute(tag);
}
class POIInfoWindow extends DefaultInfoWindow {
private Button mButton;
private ImageView mImage;
public POIInfoWindow(Map map) {
super(R.layout.bonuspack_bubble, App.view);
mButton = (Button) mView.findViewById(R.id.bubble_moreinfo);
mImage = (ImageView) mView.findViewById(R.id.bubble_image);
//bonuspack_bubble layouts already contain a "more info" button.
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
POI poi = (POI) view.getTag();
if (poi == null)
return;
if (poi.serviceId == POI.POI_SERVICE_4SQUARE) {
FourSquareProvider.browse(view.getContext(), poi);
} else if (poi.url != null) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(poi.url));
i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
view.getContext().startActivity(i);
}
}
});
getView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
POI poi = (POI) view.getTag();
if (poi != null)
showPOIActivity(false);
}
});
}
@Override
public void onOpen(ExtendedMarkerItem item) {
POI poi = (POI) item.getRelatedObject();
super.onOpen(item);
poi.fetchThumbnail(mImage);
//Show or hide "more info" button:
if (poi.url != null)
mButton.setVisibility(View.VISIBLE);
else
mButton.setVisibility(View.GONE);
mButton.setTag(poi);
getView().setTag(poi);
}
}
public boolean onContextItemSelected(MenuItem item, GeoPoint geoPoint) {
switch (item.getItemId()) {
case R.id.menu_poi_nearby:
Intent intent = new Intent(App.activity, POIActivity.class);
intent.putExtra("ID", poiMarkers.getBubbledItemId());
App.activity.startActivityForResult(intent, TileMap.POIS_REQUEST);
return true;
case R.id.menu_poi_clear:
poiMarkers.removeAllItems();
mPOIs.clear();
App.map.updateMap(true);
return true;
default:
}
return false;
}
}

View File

@@ -0,0 +1,452 @@
/*
* Copyright 2012 osmdroid: M.Kergall
* Copyright 2012 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app;
import android.app.Activity;
import android.location.Address;
import android.os.AsyncTask;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.oscim.android.canvas.AndroidGraphics;
import org.oscim.core.GeoPoint;
import org.oscim.layers.PathLayer;
import org.oscim.layers.marker.MarkerItem.HotspotPlace;
import org.oscim.layers.marker.MarkerSymbol;
import org.osmdroid.location.GeocoderNominatim;
import org.osmdroid.overlays.DefaultInfoWindow;
import org.osmdroid.overlays.ExtendedMarkerItem;
import org.osmdroid.overlays.ItemizedOverlayWithBubble;
import org.osmdroid.routing.Route;
import org.osmdroid.routing.RouteProvider;
import org.osmdroid.routing.provider.OSRMRouteProvider;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
public class RouteSearch {
private static int START_INDEX = -2, DEST_INDEX = -1;
private final PathLayer mRouteOverlay;
//private final ItemizedOverlayWithBubble<ExtendedOverlayItem> mRouteMarkers;
private final ItemizedOverlayWithBubble<ExtendedMarkerItem> mItineraryMarkers;
private final RouteBar mRouteBar;
private GeoPoint mStartPoint, mDestinationPoint;
private final ArrayList<GeoPoint> mViaPoints;
private ExtendedMarkerItem markerStart, markerDestination;
private UpdateRouteTask mRouteTask;
RouteSearch() {
mViaPoints = new ArrayList<GeoPoint>();
// Itinerary markers:
ArrayList<ExtendedMarkerItem> waypointsItems = new ArrayList<ExtendedMarkerItem>();
mItineraryMarkers = new ItemizedOverlayWithBubble<ExtendedMarkerItem>(App.map,
App.activity,
null,
waypointsItems,
new ViaPointInfoWindow(R.layout.itinerary_bubble));
//updateIternaryMarkers();
//Route and Directions
//ArrayList<ExtendedOverlayItem> routeItems = new ArrayList<ExtendedOverlayItem>();
//mRouteMarkers = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(App.map, App.activity,
// null, routeItems);
mRouteOverlay = new PathLayer(App.map, 0xAA0000FF, 3);
// TODO use LayerGroup
App.map.layers().add(mRouteOverlay);
//App.map.getOverlays().add(mRouteMarkers);
App.map.layers().add(mItineraryMarkers);
mRouteBar = new RouteBar(App.activity);
}
/**
* Retrieve route between p1 and p2 and update overlays.
*/
public void showRoute(GeoPoint p1, GeoPoint p2) {
clearOverlays();
mStartPoint = p1;
markerStart = putMarkerItem(markerStart, mStartPoint, START_INDEX,
R.string.departure, R.drawable.marker_departure, -1);
mDestinationPoint = p2;
markerDestination = putMarkerItem(markerDestination, mDestinationPoint, DEST_INDEX,
R.string.destination,
R.drawable.marker_destination, -1);
getRouteAsync();
}
/**
* Reverse Geocoding
*/
public String getAddress(GeoPoint p) {
GeocoderNominatim geocoder = new GeocoderNominatim(App.activity);
String theAddress;
try {
double dLatitude = p.getLatitude();
double dLongitude = p.getLongitude();
List<Address> addresses = geocoder.getFromLocation(dLatitude, dLongitude, 1);
StringBuilder sb = new StringBuilder();
if (addresses.size() > 0) {
Address address = addresses.get(0);
int n = address.getMaxAddressLineIndex();
for (int i = 0; i <= n; i++) {
if (i != 0)
sb.append(", ");
sb.append(address.getAddressLine(i));
}
theAddress = new String(sb.toString());
} else {
theAddress = null;
}
} catch (IOException e) {
theAddress = null;
}
if (theAddress != null) {
return theAddress;
}
return "";
}
// Async task to reverse-geocode the marker position in a separate thread:
class GeocodingTask extends AsyncTask<Object, Void, String> {
ExtendedMarkerItem marker;
@Override
protected String doInBackground(Object... params) {
marker = (ExtendedMarkerItem) params[0];
return getAddress(marker.getPoint());
}
@Override
protected void onPostExecute(String result) {
marker.setDescription(result);
}
}
/* add (or replace) an item in markerOverlays. p position. */
public ExtendedMarkerItem putMarkerItem(ExtendedMarkerItem item, GeoPoint p, int index,
int titleResId, int markerResId, int iconResId) {
if (item != null)
mItineraryMarkers.removeItem(item);
MarkerSymbol marker = AndroidGraphics.makeMarker(App.res.getDrawable(markerResId),
HotspotPlace.BOTTOM_CENTER);
ExtendedMarkerItem overlayItem =
new ExtendedMarkerItem(App.res.getString(titleResId), "", p);
overlayItem.setMarker(marker);
if (iconResId != -1)
overlayItem.setImage(App.res.getDrawable(iconResId));
overlayItem.setRelatedObject(Integer.valueOf(index));
mItineraryMarkers.addItem(overlayItem);
App.map.updateMap(true);
//Start geocoding task to update the description of the marker with its address:
new GeocodingTask().execute(overlayItem);
return overlayItem;
}
public void addViaPoint(GeoPoint p) {
mViaPoints.add(p);
putMarkerItem(null, p, mViaPoints.size() - 1,
R.string.viapoint, R.drawable.marker_via, -1);
}
public void removePoint(int index) {
if (index == START_INDEX) {
mStartPoint = null;
} else if (index == DEST_INDEX) {
mDestinationPoint = null;
} else
mViaPoints.remove(index);
getRouteAsync();
updateIternaryMarkers();
}
public void updateIternaryMarkers() {
mItineraryMarkers.removeAllItems();
//Start marker:
if (mStartPoint != null) {
markerStart = putMarkerItem(null, mStartPoint, START_INDEX,
R.string.departure, R.drawable.marker_departure, -1);
}
//Via-points markers if any:
for (int index = 0; index < mViaPoints.size(); index++) {
putMarkerItem(null, mViaPoints.get(index), index,
R.string.viapoint, R.drawable.marker_via, -1);
}
//Destination marker if any:
if (mDestinationPoint != null) {
markerDestination = putMarkerItem(null, mDestinationPoint, DEST_INDEX,
R.string.destination,
R.drawable.marker_destination, -1);
}
}
//------------ Route and Directions
private void updateOverlays(Route route) {
//mRouteMarkers.removeAllItems();
mRouteOverlay.clearPath();
if (route == null || route.status == Route.STATUS_DEFAULT) {
App.activity.showToastOnUiThread(App.res.getString(R.string.route_lookup_error));
return;
}
mRouteOverlay.setPoints(route.routeHigh);
//OverlayMarker marker = AndroidGraphics.makeMarker(App.res, R.drawable.marker_node, null);
//int n = route.nodes.size();
//for (int i = 0; i < n; i++) {
// RouteNode node = route.nodes.get(i);
// String instructions = (node.instructions == null ? "" : node.instructions);
// ExtendedOverlayItem nodeMarker = new ExtendedOverlayItem(
// "Step " + (i + 1), instructions, node.location);
//
// nodeMarker.setSubDescription(route.getLengthDurationText(node.length, node.duration));
// nodeMarker.setMarkerHotspot(OverlayItem.HotspotPlace.CENTER);
// nodeMarker.setMarker(marker);
//
// mRouteMarkers.addItem(nodeMarker);
//}
App.map.updateMap(true);
}
void clearOverlays() {
//mRouteMarkers.removeAllItems(true);
mItineraryMarkers.removeAllItems(true);
mRouteOverlay.clearPath();
mStartPoint = null;
mDestinationPoint = null;
mViaPoints.clear();
App.map.updateMap(true);
}
/**
* Async task to get the route in a separate thread.
*/
class UpdateRouteTask extends AsyncTask<List<GeoPoint>, Void, Route> {
@Override
protected Route doInBackground(List<GeoPoint>... wp) {
List<GeoPoint> waypoints = wp[0];
//RouteProvider routeProvider = new MapQuestRouteProvider();
//Locale locale = Locale.getDefault();
//routeProvider.addRequestOption("locale=" + locale.getLanguage() + "_"
// + locale.getCountry());
//routeProvider.addRequestOption("routeType=pedestrian");
//RouteProvider routeProvider = new GoogleRouteProvider();
RouteProvider routeProvider = new OSRMRouteProvider();
return routeProvider.getRoute(waypoints);
}
@Override
protected void onPostExecute(Route result) {
updateOverlays(result);
mRouteBar.set(result);
mRouteTask = null;
}
}
@SuppressWarnings("unchecked")
public void getRouteAsync() {
if (mRouteTask != null) {
mRouteTask.cancel(true);
mRouteTask = null;
}
if (mStartPoint == null || mDestinationPoint == null) {
mRouteOverlay.clearPath();
return;
}
List<GeoPoint> waypoints = new ArrayList<GeoPoint>();
waypoints.add(mStartPoint);
//add intermediate via points:
for (GeoPoint p : mViaPoints) {
waypoints.add(p);
}
waypoints.add(mDestinationPoint);
mRouteTask = new UpdateRouteTask();
mRouteTask.execute(waypoints);
}
boolean onContextItemSelected(MenuItem item, GeoPoint geoPoint) {
switch (item.getItemId()) {
case R.id.menu_route_departure:
mStartPoint = geoPoint;
markerStart = putMarkerItem(markerStart, mStartPoint, START_INDEX,
R.string.departure, R.drawable.marker_departure, -1);
getRouteAsync();
return true;
case R.id.menu_route_destination:
mDestinationPoint = geoPoint;
markerDestination = putMarkerItem(markerDestination, mDestinationPoint, DEST_INDEX,
R.string.destination,
R.drawable.marker_destination, -1);
getRouteAsync();
return true;
case R.id.menu_route_viapoint:
GeoPoint viaPoint = geoPoint;
addViaPoint(viaPoint);
getRouteAsync();
return true;
case R.id.menu_route_clear:
clearOverlays();
return true;
default:
}
return false;
}
public boolean isEmpty() {
return (mItineraryMarkers.size() == 0);
}
class ViaPointInfoWindow extends DefaultInfoWindow {
int mSelectedPoint;
public ViaPointInfoWindow(int layoutResId) {
super(layoutResId, App.view);
Button btnDelete = (Button) (mView.findViewById(R.id.bubble_delete));
btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
removePoint(mSelectedPoint);
close();
}
});
}
@Override
public void onOpen(ExtendedMarkerItem item) {
mSelectedPoint = ((Integer) item.getRelatedObject()).intValue();
super.onOpen(item);
}
}
class RouteBar {
TextView mDistance = null;
TextView mRouteLength = null;
TextView mTravelTime = null;
ImageView mClearButton = null;
RelativeLayout mRouteBarView = null;
RouteBar(Activity activity) {
mRouteBarView = (RelativeLayout) activity.findViewById(R.id.route_bar);
mDistance = (TextView) activity.findViewById(R.id.route_bar_distance);
mRouteLength = (TextView) activity.findViewById(R.id.route_bar_route_length);
mTravelTime = (TextView) activity.findViewById(R.id.route_bar_travel_time);
mClearButton = (ImageView) activity.findViewById(R.id.route_bar_clear);
mRouteBarView.setVisibility(View.INVISIBLE);
mClearButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRouteBarView.setVisibility(View.INVISIBLE);
clearOverlays();
}
});
}
public void set(Route result) {
DecimalFormat twoDForm = new DecimalFormat("#.#");
DecimalFormat oneDForm = new DecimalFormat("#");
int hour = ((int) result.duration / 3600);
int minute = ((int) result.duration % 3600) / 60;
String time = "";
if (hour == 0 && minute == 0) {
time = "?";
} else if (hour == 0 && minute != 0) {
time = minute + "m";
} else {
time = hour + "h " + minute + "m";
}
double dis = ((double) (mStartPoint.distanceTo(mDestinationPoint))) / 1000;
String distance;
String shortpath;
if (dis < 100) {
distance = twoDForm.format(dis);
} else {
distance = oneDForm.format(dis);
}
if (result.length == 0) {
shortpath = "?";
} else if (result.length < 100) {
shortpath = twoDForm.format(result.length);
} else {
shortpath = oneDForm.format(result.length);
}
mRouteBarView.setVisibility(View.VISIBLE);
mDistance.setText(distance + " km");
mTravelTime.setText(time);
mRouteLength.setText(shortpath + " km");
}
}
}

View File

@@ -0,0 +1,518 @@
/* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2012 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import org.oscim.android.MapView;
import org.oscim.app.location.Compass;
import org.oscim.app.location.LocationDialog;
import org.oscim.app.location.LocationHandler;
import org.oscim.app.preferences.EditPreferences;
import org.oscim.core.GeoPoint;
import org.oscim.overlay.DistanceTouchOverlay;
import org.osmdroid.location.POI;
import org.osmdroid.overlays.MapEventsReceiver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
public class TileMap extends MapActivity implements MapEventsReceiver {
final static Logger log = LoggerFactory.getLogger(TileMap.class);
private static final int DIALOG_ENTER_COORDINATES = 0;
private static final int DIALOG_LOCATION_PROVIDER_DISABLED = 2;
//private static final int SELECT_RENDER_THEME_FILE = 1;
protected static final int POIS_REQUEST = 2;
private LocationHandler mLocation;
private Compass mCompass;
private Menu mMenu = null;
private MapLayers mMapLayers;
public MapLayers getMapLayers() {
return mMapLayers;
}
private DistanceTouchOverlay mDistanceTouch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tilemap);
App.view = (MapView) findViewById(R.id.mapView);
registerMapView(App.view);
App.map = mMap;
App.activity = this;
mMapLayers = new MapLayers();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
mMapLayers.setBaseMap(prefs);
if (!prefs.contains("distanceTouch"))
prefs.edit().putBoolean("distanceTouch", true).apply();
if (prefs.getBoolean("distanceTouch", true)) {
mDistanceTouch = new DistanceTouchOverlay(mMap, this);
mMap.layers().add(mDistanceTouch);
}
mCompass = new Compass(this, mMap);
mMap.layers().add(mCompass);
mLocation = new LocationHandler(this, mCompass);
App.poiSearch = new POISearch();
App.routeSearch = new RouteSearch();
registerForContextMenu(App.view);
handleIntent(getIntent(), true);
}
public Compass getCompass() {
return mCompass;
}
public LocationHandler getLocationHandler() {
return mLocation;
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent, false);
}
private void handleIntent(Intent intent, boolean start) {
if (intent == null)
return;
Uri uri = intent.getData();
if (uri != null) {
String scheme = uri.getSchemeSpecificPart();
log.debug("got intent: " + (scheme == null ? "" : scheme));
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options_menu, menu);
mMenu = menu;
toggleMenuCheck();
return true;
}
@SuppressWarnings("deprecation")
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_info_about:
startActivity(new Intent(this, InfoView.class));
break;
case R.id.menu_position:
break;
case R.id.menu_poi_nearby:
Intent intent = new Intent(this, POIActivity.class);
startActivityForResult(intent, TileMap.POIS_REQUEST);
break;
case R.id.menu_compass_2d:
if (!item.isChecked()) {
// FIXME
//mMapView.getMapViewPosition().setTilt(0);
mCompass.setMode(Compass.Mode.C2D);
} else {
mCompass.setMode(Compass.Mode.OFF);
}
break;
case R.id.menu_compass_3d:
if (!item.isChecked()) {
mCompass.setMode(Compass.Mode.C3D);
} else {
mCompass.setMode(Compass.Mode.OFF);
}
break;
case R.id.menu_position_my_location_enable:
if (!item.isChecked()) {
mLocation.setMode(LocationHandler.Mode.SHOW);
mLocation.setCenterOnFirstFix();
} else {
mLocation.setMode(LocationHandler.Mode.OFF);
}
break;
case R.id.menu_position_follow_location:
if (!item.isChecked()) {
mLocation.setMode(LocationHandler.Mode.SNAP);
} else {
mLocation.setMode(LocationHandler.Mode.OFF);
}
break;
case R.id.menu_layer_openstreetmap:
case R.id.menu_layer_naturalearth:
int bgId = item.getItemId();
// toggle if already enabled
if (bgId == mMapLayers.getBackgroundId())
bgId = -1;
mMapLayers.setBackgroundMap(bgId);
mMap.updateMap(true);
break;
case R.id.menu_layer_grid:
mMapLayers.enableGridOverlay(!mMapLayers.isGridEnabled());
mMap.updateMap(true);
break;
case R.id.menu_position_enter_coordinates:
showDialog(DIALOG_ENTER_COORDINATES);
break;
//case R.id.menu_position_map_center:
// MapPosition mapCenter = mBaseLayer.getMapFileCenter();
// if (mapCenter != null)
// mMap.setCenter(mapCenter.getGeoPoint());
// break;
case R.id.menu_preferences:
startActivity(new Intent(this, EditPreferences.class));
overridePendingTransition(R.anim.slide_right, R.anim.slide_left2);
break;
default:
return false;
}
toggleMenuCheck();
return true;
}
private void toggleMenuCheck() {
mMenu.findItem(R.id.menu_compass_2d)
.setChecked(mCompass.getMode() == Compass.Mode.C2D);
mMenu.findItem(R.id.menu_compass_3d)
.setChecked(mCompass.getMode() == Compass.Mode.C3D);
mMenu.findItem(R.id.menu_position_my_location_enable)
.setChecked(mLocation.getMode() == LocationHandler.Mode.SHOW);
mMenu.findItem(R.id.menu_position_follow_location)
.setChecked(mLocation.getMode() == LocationHandler.Mode.SNAP);
int bgId = mMapLayers.getBackgroundId();
mMenu.findItem(R.id.menu_layer_naturalearth)
.setChecked(bgId == R.id.menu_layer_naturalearth);
mMenu.findItem(R.id.menu_layer_openstreetmap)
.setChecked(bgId == R.id.menu_layer_openstreetmap);
mMenu.findItem(R.id.menu_layer_grid)
.setChecked(mMapLayers.isGridEnabled());
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (!isPreHoneyComb()) {
menu.clear();
onCreateOptionsMenu(menu);
}
menu.findItem(R.id.menu_position_map_center).setVisible(false);
return super.onPrepareOptionsMenu(menu);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
switch (requestCode) {
case POIS_REQUEST:
log.debug("result: POIS_REQUEST");
if (resultCode == RESULT_OK) {
int id = intent.getIntExtra("ID", 0);
log.debug("result: POIS_REQUEST: " + id);
App.poiSearch.poiMarkers.showBubbleOnItem(id);
POI poi = App.poiSearch.getPOIs().get(id);
if (poi.bbox != null)
mMap.animator().animateTo(poi.bbox);
else
mMap.animator().animateTo(poi.location);
}
break;
default:
break;
}
}
static boolean isPreHoneyComb() {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
}
@Override
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
if (id == DIALOG_ENTER_COORDINATES) {
if (mLocationDialog == null)
mLocationDialog = new LocationDialog();
return mLocationDialog.createDialog(this);
} else if (id == DIALOG_LOCATION_PROVIDER_DISABLED) {
builder.setIcon(android.R.drawable.ic_menu_info_details);
builder.setTitle(R.string.error);
builder.setMessage(R.string.no_location_provider_available);
builder.setPositiveButton(R.string.ok, null);
return builder.create();
} else {
// no dialog will be created
return null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
protected void onPause() {
super.onPause();
mCompass.pause();
mLocation.pause();
}
LocationDialog mLocationDialog;
@SuppressWarnings("deprecation")
@Override
protected void onPrepareDialog(int id, final Dialog dialog) {
if (id == DIALOG_ENTER_COORDINATES) {
mLocationDialog.prepareDialog(mMap, dialog);
} else {
super.onPrepareDialog(id, dialog);
}
}
@Override
protected void onResume() {
super.onResume();
mCompass.resume();
mLocation.resume();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
mMapLayers.setPreferences(preferences);
if (preferences.getBoolean("fullscreen", false)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
}
App.lockOrientation(this);
boolean distanceTouch = preferences.getBoolean("distanceTouch", true);
if (distanceTouch) {
if (mDistanceTouch == null) {
mDistanceTouch = new DistanceTouchOverlay(mMap, this);
mMap.layers().add(mDistanceTouch);
}
} else {
mMap.layers().remove(mDistanceTouch);
mDistanceTouch = null;
}
mMap.updateMap(true);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
/**
* Uses the UI thread to display the given text message as toast
* notification.
*
* @param text the text message to display
*/
public void showToastOnUiThread(final String text) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast toast = Toast.makeText(TileMap.this, text, Toast.LENGTH_SHORT);
toast.show();
}
});
}
private enum Mode {
DEFAULT,
SHOW_LOCATION,
SNAP_LOCATION,
COMPASS_2D,
COMPASS_3D,
}
private int mMapMode = 0;
public void toggleLocation(View V) {
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50);
mMapMode += 1;
mMapMode %= Mode.values().length;
setInteractionMode(mMapMode);
}
private void setInteractionMode(int mapMode) {
Mode m = Mode.values()[mapMode];
switch (m) {
case DEFAULT:
mLocation.setMode(LocationHandler.Mode.OFF);
mCompass.setMode(Compass.Mode.OFF);
App.activity.showToastOnUiThread("Manual");
break;
case SHOW_LOCATION:
mLocation.setMode(LocationHandler.Mode.SHOW);
mCompass.setMode(Compass.Mode.OFF);
App.activity.showToastOnUiThread(App.activity
.getString(R.string.menu_position_my_location_enable));
break;
case SNAP_LOCATION:
mLocation.setMode(LocationHandler.Mode.SNAP);
mCompass.setMode(Compass.Mode.OFF);
App.activity.showToastOnUiThread(App.activity
.getString(R.string.menu_position_follow_location));
break;
case COMPASS_2D:
// FIXME
//mMapView.getMapViewPosition().setTilt(0);
mLocation.setMode(LocationHandler.Mode.SHOW);
mCompass.setMode(Compass.Mode.C2D);
App.activity.showToastOnUiThread("Compass 2D");
break;
case COMPASS_3D:
mLocation.setMode(LocationHandler.Mode.SHOW);
mCompass.setMode(Compass.Mode.C3D);
App.activity.showToastOnUiThread("Compass 3D");
break;
default:
break;
}
App.map.updateMap(true);
}
/**
* Context Menu when clicking on the {@link Map}
*/
private GeoPoint mLongPressGeoPoint;
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.map_menu, menu);
if (App.poiSearch.getPOIs().isEmpty())
menu.removeItem(R.id.menu_poi_clear);
if (App.routeSearch.isEmpty())
menu.removeItem(R.id.menu_route_clear);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
if (App.poiSearch.onContextItemSelected(item, mLongPressGeoPoint))
return true;
if (App.routeSearch.onContextItemSelected(item, mLongPressGeoPoint))
return true;
return super.onContextItemSelected(item);
}
/**
* MapEventsReceiver implementation
*/
@Override
public boolean singleTapUpHelper(GeoPoint p) {
return false;
}
@Override
public boolean longPressHelper(GeoPoint p) {
mLongPressGeoPoint = p;
openContextMenu(App.view);
return true;
}
@Override
public boolean longPressHelper(final GeoPoint p1, final GeoPoint p2) {
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50);
showToastOnUiThread("Distance Touch!");
App.routeSearch.showRoute(p1, p2);
return true;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.filefilter;
import java.io.File;
import java.io.FileFilter;
/**
* Accepts all readable directories and all readable files with a given
* extension.
*/
public class FilterByFileExtension implements FileFilter {
private final String extension;
/**
* @param extension the allowed file name extension.
*/
public FilterByFileExtension(String extension) {
this.extension = extension;
}
@Override
public boolean accept(File file) {
// accept only readable files
if (file.canRead()) {
if (file.isDirectory()) {
// accept all directories
return true;
} else if (file.isFile() && file.getName().endsWith(this.extension)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.filefilter;
import org.oscim.tiling.TileSource.OpenResult;
import java.io.FileFilter;
/**
* An extension of the {@link FileFilter} interface.
*/
public interface ValidFileFilter extends FileFilter {
/**
* @return the result of the last {@link #accept} call (might be null).
*/
OpenResult getFileOpenResult();
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.filefilter;
import org.oscim.tiling.TileSource.OpenResult;
import org.oscim.tiling.source.mapfile.MapFileTileSource;
import java.io.File;
/**
* Accepts all valid map files.
*/
public final class ValidMapFile implements ValidFileFilter {
private OpenResult openResult;
@Override
public boolean accept(File file) {
MapFileTileSource mapFileSource = new MapFileTileSource();
mapFileSource.setMapFile(file.getAbsolutePath());
this.openResult = mapFileSource.open();
mapFileSource.close();
return this.openResult.isSuccess();
}
@Override
public OpenResult getFileOpenResult() {
return this.openResult;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2016 devemux86
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.filefilter;
import org.oscim.theme.XmlThemeBuilder;
import org.oscim.tiling.TileSource.OpenResult;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
/**
* Accepts all valid render theme XML files.
*/
public final class ValidRenderTheme implements ValidFileFilter {
private OpenResult openResult;
@Override
public boolean accept(File file) {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
XmlThemeBuilder renderThemeHandler = new XmlThemeBuilder(file.getParent());
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
xmlReader.setContentHandler(renderThemeHandler);
xmlReader.parse(new InputSource(inputStream));
this.openResult = OpenResult.SUCCESS;
} catch (ParserConfigurationException e) {
this.openResult = new OpenResult(e.getMessage());
} catch (SAXException e) {
this.openResult = new OpenResult(e.getMessage());
} catch (IOException e) {
this.openResult = new OpenResult(e.getMessage());
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
this.openResult = new OpenResult(e.getMessage());
}
}
return this.openResult.isSuccess();
}
@Override
public OpenResult getFileOpenResult() {
return this.openResult;
}
}

View File

@@ -0,0 +1,262 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.filepicker;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import org.oscim.app.R;
import org.oscim.app.filefilter.ValidFileFilter;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Comparator;
/**
* A FilePicker displays the contents of directories. The user can navigate
* within the file system and select a single
* file whose path is then returned to the calling activity. The ordering of
* directory contents can be specified via
* {@link #setFileComparator(Comparator)}. By default subfolders and files are
* grouped and each group is ordered
* alphabetically.
* <p/>
* A {@link FileFilter} can be activated via
* {@link #setFileDisplayFilter(FileFilter)} to restrict the displayed files and
* folders. By default all files and folders are visible.
* <p/>
* Another <code>FileFilter</code> can be applied via
* {@link #setFileSelectFilter(ValidFileFilter)} to check if a selected file is
* valid before its path is returned. By default all files are considered as
* valid and can be selected.
*/
public class FilePicker extends Activity implements AdapterView.OnItemClickListener {
/**
* The name of the extra data in the result {@link Intent}.
*/
public static final String SELECTED_FILE = "selectedFile";
private static final String CURRENT_DIRECTORY = "currentDirectory";
private static final String DEFAULT_DIRECTORY = "/";
private static final int DIALOG_FILE_INVALID = 0;
// private static final int DIALOG_FILE_SELECT = 1;
private static Comparator<File> fileComparator = getDefaultFileComparator();
private static FileFilter fileDisplayFilter;
private static ValidFileFilter fileSelectFilter;
private static final String PREFERENCES_FILE = "FilePicker";
/**
* Sets the file comparator which is used to order the contents of all
* directories before displaying them. If set to
* null, subfolders and files will not be ordered.
*
* @param fileComparator the file comparator (may be null).
*/
public static void setFileComparator(Comparator<File> fileComparator) {
FilePicker.fileComparator = fileComparator;
}
/**
* Sets the file display filter. This filter is used to determine which
* files and subfolders of directories will be
* displayed. If set to null, all files and subfolders are shown.
*
* @param fileDisplayFilter the file display filter (may be null).
*/
public static void setFileDisplayFilter(FileFilter fileDisplayFilter) {
FilePicker.fileDisplayFilter = fileDisplayFilter;
}
/**
* Sets the file select filter. This filter is used when the user selects a
* file to determine if it is valid. If set
* to null, all files are considered as valid.
*
* @param fileSelectFilter the file selection filter (may be null).
*/
public static void setFileSelectFilter(ValidFileFilter fileSelectFilter) {
FilePicker.fileSelectFilter = fileSelectFilter;
}
/**
* Creates the default file comparator.
*
* @return the default file comparator.
*/
private static Comparator<File> getDefaultFileComparator() {
// order all files by type and alphabetically by name
return new Comparator<File>() {
@Override
public int compare(File file1, File file2) {
if (file1.isDirectory() && !file2.isDirectory()) {
return -1;
} else if (!file1.isDirectory() && file2.isDirectory()) {
return 1;
} else {
return file1.getName().compareToIgnoreCase(file2.getName());
}
}
};
}
private File currentDirectory;
private FilePickerIconAdapter filePickerIconAdapter;
private File[] files;
private File[] filesWithParentFolder;
@SuppressWarnings("deprecation")
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
File selectedFile = this.files[(int) id];
if (selectedFile.isDirectory()) {
this.currentDirectory = selectedFile;
browseToCurrentDirectory();
} else if (fileSelectFilter == null || fileSelectFilter.accept(selectedFile)) {
setResult(RESULT_OK,
new Intent().putExtra(SELECTED_FILE, selectedFile.getAbsolutePath()));
finish();
} else {
showDialog(DIALOG_FILE_INVALID);
}
}
/**
* Browses to the current directory.
*/
private void browseToCurrentDirectory() {
setTitle(this.currentDirectory.getAbsolutePath());
// read the subfolders and files from the current directory
if (fileDisplayFilter == null) {
this.files = this.currentDirectory.listFiles();
} else {
this.files = this.currentDirectory.listFiles(fileDisplayFilter);
}
if (this.files == null) {
this.files = new File[0];
} else {
// order the subfolders and files
Arrays.sort(this.files, fileComparator);
}
// if a parent directory exists, add it at the first position
if (this.currentDirectory.getParentFile() != null) {
this.filesWithParentFolder = new File[this.files.length + 1];
this.filesWithParentFolder[0] = this.currentDirectory.getParentFile();
System.arraycopy(this.files, 0, this.filesWithParentFolder, 1,
this.files.length);
this.files = this.filesWithParentFolder;
this.filePickerIconAdapter.setFiles(this.files, true);
} else {
this.filePickerIconAdapter.setFiles(this.files, false);
}
this.filePickerIconAdapter.notifyDataSetChanged();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_picker);
this.filePickerIconAdapter = new FilePickerIconAdapter(this);
GridView gridView = (GridView) findViewById(R.id.filePickerView);
gridView.setOnItemClickListener(this);
gridView.setAdapter(this.filePickerIconAdapter);
// if (savedInstanceState == null) {
// // first start of this instance
// showDialog(DIALOG_FILE_SELECT);
// }
}
@Override
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
switch (id) {
case DIALOG_FILE_INVALID:
builder.setIcon(android.R.drawable.ic_menu_info_details);
builder.setTitle(R.string.error);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(getString(R.string.file_invalid));
stringBuilder.append("\n\n");
stringBuilder.append(FilePicker.fileSelectFilter.getFileOpenResult()
.getErrorMessage());
builder.setMessage(stringBuilder.toString());
builder.setPositiveButton(R.string.ok, null);
return builder.create();
// case DIALOG_FILE_SELECT:
// builder.setMessage(R.string.file_select);
// builder.setPositiveButton(R.string.ok, null);
// return builder.create();
default:
// do dialog will be created
return null;
}
}
@Override
protected void onPause() {
super.onPause();
// save the current directory
Editor editor = getSharedPreferences(PREFERENCES_FILE, MODE_PRIVATE).edit();
editor.clear();
if (this.currentDirectory != null) {
editor.putString(CURRENT_DIRECTORY, this.currentDirectory.getAbsolutePath());
}
editor.commit();
}
@TargetApi(11)
@Override
protected void onResume() {
super.onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
getActionBar().hide();
// check if the full screen mode should be activated
// if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fullscreen",
// false)) {
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
// } else {
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
// }
// restore the current directory
SharedPreferences preferences = getSharedPreferences(PREFERENCES_FILE,
MODE_PRIVATE);
this.currentDirectory = new File(preferences.getString(CURRENT_DIRECTORY,
DEFAULT_DIRECTORY));
if (!this.currentDirectory.exists() || !this.currentDirectory.canRead()) {
this.currentDirectory = new File(DEFAULT_DIRECTORY);
}
browseToCurrentDirectory();
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.filepicker;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import org.oscim.app.R;
import java.io.File;
/**
* An adapter for the FilePicker GridView.
*/
class FilePickerIconAdapter extends BaseAdapter {
private final Context context;
private File currentFile;
private File[] files;
private boolean hasParentFolder;
private TextView textView;
/**
* Creates a new FilePickerIconAdapter with the given context.
*
* @param context the context of this adapter, through which new Views are
* created.
*/
FilePickerIconAdapter(Context context) {
super();
this.context = context;
}
@Override
public int getCount() {
if (this.files == null) {
return 0;
}
return this.files.length;
}
@Override
public Object getItem(int index) {
return this.files[index];
}
@Override
public long getItemId(int index) {
return index;
}
@Override
public View getView(int index, View convertView, ViewGroup parent) {
if (convertView instanceof TextView) {
// recycle the old view
this.textView = (TextView) convertView;
} else {
// create a new view object
this.textView = new TextView(this.context);
this.textView.setLines(2);
this.textView.setGravity(Gravity.CENTER_HORIZONTAL);
this.textView.setPadding(5, 10, 5, 10);
}
if (index == 0 && this.hasParentFolder) {
// the parent directory of the current folder
this.textView.setCompoundDrawablesWithIntrinsicBounds(0,
R.drawable.file_picker_back, 0, 0);
this.textView.setText("..");
} else {
this.currentFile = this.files[index];
if (this.currentFile.isDirectory()) {
this.textView.setCompoundDrawablesWithIntrinsicBounds(0,
R.drawable.file_picker_folder,
0,
0);
} else {
this.textView.setCompoundDrawablesWithIntrinsicBounds(0,
R.drawable.file_picker_file,
0,
0);
}
this.textView.setText(this.currentFile.getName());
}
return this.textView;
}
/**
* Sets the data of this adapter.
*
* @param files the new files for this adapter.
* @param newHasParentFolder true if the file array has a parent folder at index 0, false
* otherwise.
*/
void setFiles(File[] files, boolean newHasParentFolder) {
this.files = files.clone();
this.hasParentFolder = newHasParentFolder;
}
}

View File

@@ -0,0 +1,392 @@
/*
* Copyright 2013 Ahmad Saleem
* Copyright 2013 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.location;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import org.oscim.app.App;
import org.oscim.app.R;
import org.oscim.core.MapPosition;
import org.oscim.event.Event;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
@SuppressWarnings("deprecation")
public class Compass extends Layer implements SensorEventListener,
Map.UpdateListener {
// final static Logger log = LoggerFactory.getLogger(Compass.class);
public enum Mode {
OFF, C2D, C3D,
}
private final SensorManager mSensorManager;
private final ImageView mArrowView;
// private final float[] mRotationM = new float[9];
private final float[] mRotationV = new float[3];
// private float[] mAccelV = new float[3];
// private float[] mMagnetV = new float[3];
// private boolean mLastAccelerometerSet;
// private boolean mLastMagnetometerSet;
private float mCurRotation;
private float mCurTilt;
private boolean mControlOrientation;
private Mode mMode = Mode.OFF;
private int mListeners;
@Override
public void onMapEvent(Event e, MapPosition mapPosition) {
if (!mControlOrientation) {
float rotation = -mapPosition.bearing;
adjustArrow(rotation, rotation);
}
}
public Compass(Context context, Map map) {
super(map);
mSensorManager = (SensorManager) context
.getSystemService(Context.SENSOR_SERVICE);
// List<Sensor> s = mSensorManager.getSensorList(Sensor.TYPE_ALL);
// for (Sensor sensor : s)
// log.debug(sensor.toString());
mArrowView = (ImageView) App.activity.findViewById(R.id.compass);
setEnabled(false);
}
public synchronized float getRotation() {
return mCurRotation;
}
public void controlView(boolean enable) {
mControlOrientation = enable;
}
public boolean controlView() {
return mControlOrientation;
}
public void setMode(Mode mode) {
if (mode == mMode)
return;
if (mode == Mode.OFF) {
setEnabled(false);
mMap.getEventLayer().enableRotation(true);
mMap.getEventLayer().enableTilt(true);
} else if (mMode == Mode.OFF) {
setEnabled(true);
}
if (mode == Mode.C3D) {
mMap.getEventLayer().enableRotation(false);
mMap.getEventLayer().enableTilt(false);
} else if (mode == Mode.C2D) {
mMap.getEventLayer().enableRotation(false);
mMap.getEventLayer().enableTilt(true);
}
mMode = mode;
}
public Mode getMode() {
return mMode;
}
@Override
public void setEnabled(boolean enabled) {
mListeners += enabled ? 1 : -1;
if (mListeners == 1) {
resume();
} else if (mListeners == 0) {
pause();
} else if (mListeners < 0) {
// then bad
mListeners = 0;
}
}
public void resume() {
if (mListeners <= 0)
return;
super.setEnabled(true);
Sensor sensor;
// Sensor sensor =
// mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
// Sensor sensor =
// mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
// mSensorManager.registerListener(this, sensor,
// SensorManager.SENSOR_DELAY_UI);
// sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
// mSensorManager.registerListener(this, sensor,
// SensorManager.SENSOR_DELAY_UI);
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mSensorManager.registerListener(this, sensor,
SensorManager.SENSOR_DELAY_UI);
// mLastAccelerometerSet = false;
// mLastMagnetometerSet = false;
}
public void pause() {
if (mListeners <= 0)
return;
super.setEnabled(false);
mSensorManager.unregisterListener(this);
}
public void adjustArrow(float prev, float cur) {
Animation an = new RotateAnimation(-prev,
-cur,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
0.5f);
an.setDuration(100);
an.setRepeatCount(0);
an.setFillAfter(true);
mArrowView.startAnimation(an);
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ORIENTATION)
return;
System.arraycopy(event.values, 0, mRotationV, 0, event.values.length);
// SensorManager.getRotationMatrixFromVector(mRotationM, event.values);
// SensorManager.getOrientation(mRotationM, mRotationV);
// int type = event.sensor.getType();
// if (type == Sensor.TYPE_ACCELEROMETER) {
// System.arraycopy(event.values, 0, mAccelV, 0, event.values.length);
// mLastAccelerometerSet = true;
// } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
// System.arraycopy(event.values, 0, mMagnetV, 0, event.values.length);
// mLastMagnetometerSet = true;
// } else {
// return;
// }
// if (!mLastAccelerometerSet || !mLastMagnetometerSet)
// return;
// SensorManager.getRotationMatrix(mRotationM, null, mAccelV, mMagnetV);
// SensorManager.getOrientation(mRotationM, mRotationV);
// float rotation = (float) Math.toDegrees(mRotationV[0]);
float rotation = mRotationV[0];
// handle(event);
// if (!mOrientationOK)
// return;
// float rotation = (float) Math.toDegrees(mAzimuthRadians);
float change = rotation - mCurRotation;
if (change > 180)
change -= 360;
else if (change < -180)
change += 360;
// low-pass
change *= 0.05;
rotation = mCurRotation + change;
if (rotation > 180)
rotation -= 360;
else if (rotation < -180)
rotation += 360;
// float tilt = (float) Math.toDegrees(mRotationV[1]);
// float tilt = (float) Math.toDegrees(mPitchAxisRadians);
float tilt = mRotationV[1];
mCurTilt = mCurTilt + 0.2f * (tilt - mCurTilt);
if (mMode != Mode.OFF) {
boolean redraw = false;
if (Math.abs(change) > 0.01) {
adjustArrow(mCurRotation, rotation);
mMap.viewport().setRotation(-rotation);
redraw = true;
}
if (mMode == Mode.C3D)
redraw |= mMap.viewport().setTilt(-mCurTilt * 1.5f);
if (redraw)
mMap.updateMap(true);
}
mCurRotation = rotation;
}
// from http://stackoverflow.com/questions/16317599/android-compass-that-
// can-compensate-for-tilt-and-pitch/16386066#16386066
// private int mGravityAccuracy;
// private int mMagneticFieldAccuracy;
// private float[] mGravityV = new float[3];
// private float[] mMagFieldV = new float[3];
// private float[] mEastV = new float[3];
// private float[] mNorthV = new float[3];
//
// private float mNormGravity;
// private float mNormMagField;
//
// private boolean mOrientationOK;
// private float mAzimuthRadians;
// private float mPitchRadians;
// private float mPitchAxisRadians;
//
// private void handle(SensorEvent event) {
// int SensorType = event.sensor.getType();
// switch (SensorType) {
// case Sensor.TYPE_GRAVITY:
// mLastAccelerometerSet = true;
// System.arraycopy(event.values, 0, mGravityV, 0, mGravityV.length);
// mNormGravity = (float) Math.sqrt(mGravityV[0] * mGravityV[0]
// + mGravityV[1] * mGravityV[1] + mGravityV[2]
// * mGravityV[2]);
// for (int i = 0; i < mGravityV.length; i++)
// mGravityV[i] /= mNormGravity;
// break;
// case Sensor.TYPE_MAGNETIC_FIELD:
// mLastMagnetometerSet = true;
// System.arraycopy(event.values, 0, mMagFieldV, 0, mMagFieldV.length);
// mNormMagField = (float) Math.sqrt(mMagFieldV[0] * mMagFieldV[0]
// + mMagFieldV[1] * mMagFieldV[1] + mMagFieldV[2]
// * mMagFieldV[2]);
// for (int i = 0; i < mMagFieldV.length; i++)
// mMagFieldV[i] /= mNormMagField;
// break;
// }
// if (!mLastAccelerometerSet || !mLastMagnetometerSet)
// return;
//
// // first calculate the horizontal vector that points due east
// float ex = mMagFieldV[1] * mGravityV[2] - mMagFieldV[2] * mGravityV[1];
// float ey = mMagFieldV[2] * mGravityV[0] - mMagFieldV[0] * mGravityV[2];
// float ez = mMagFieldV[0] * mGravityV[1] - mMagFieldV[1] * mGravityV[0];
// float normEast = (float) Math.sqrt(ex * ex + ey * ey + ez * ez);
//
// if (mNormGravity * mNormMagField * normEast < 0.1f) { // Typical values
// are > 100.
// // device is close to free fall (or in space?), or close to magnetic
// north pole.
// mOrientationOK = false;
// return;
// }
//
// mEastV[0] = ex / normEast;
// mEastV[1] = ey / normEast;
// mEastV[2] = ez / normEast;
//
// // next calculate the horizontal vector that points due north
// float mdotG = (mGravityV[0] * mMagFieldV[0]
// + mGravityV[1] * mMagFieldV[1]
// + mGravityV[2] * mMagFieldV[2]);
//
// float nx = mMagFieldV[0] - mGravityV[0] * mdotG;
// float ny = mMagFieldV[1] - mGravityV[1] * mdotG;
// float nz = mMagFieldV[2] - mGravityV[2] * mdotG;
// float normNorth = (float) Math.sqrt(nx * nx + ny * ny + nz * nz);
//
// mNorthV[0] = nx / normNorth;
// mNorthV[1] = ny / normNorth;
// mNorthV[2] = nz / normNorth;
//
// // take account of screen rotation away from its natural rotation
// //int rotation =
// App.activity.getWindowManager().getDefaultDisplay().getRotation();
// float screenDirection = 0;
// //switch(rotation) {
// // case Surface.ROTATION_0: screenDirection = 0; break;
// // case Surface.ROTATION_90: screenDirection = (float)Math.PI/2; break;
// // case Surface.ROTATION_180: screenDirection = (float)Math.PI; break;
// // case Surface.ROTATION_270: screenDirection = 3*(float)Math.PI/2;
// break;
// //}
// // NB: the rotation matrix has now effectively been calculated. It
// consists of
// // the three vectors mEastV[], mNorthV[] and mGravityV[]
//
// // calculate all the required angles from the rotation matrix
// // NB: see
// http://math.stackexchange.com/questions/381649/whats-the-best-3d-angular-
// // co-ordinate-system-for-working-with-smartfone-apps
// float sin = mEastV[1] - mNorthV[0], cos = mEastV[0] + mNorthV[1];
// mAzimuthRadians = (float) (sin != 0 && cos != 0 ? Math.atan2(sin, cos) :
// 0);
// mPitchRadians = (float) Math.acos(mGravityV[2]);
//
// sin = -mEastV[1] - mNorthV[0];
// cos = mEastV[0] - mNorthV[1];
//
// float aximuthPlusTwoPitchAxisRadians =
// (float) (sin != 0 && cos != 0 ? Math.atan2(sin, cos) : 0);
//
// mPitchAxisRadians = (float) (aximuthPlusTwoPitchAxisRadians -
// mAzimuthRadians) / 2;
// mAzimuthRadians += screenDirection;
// mPitchAxisRadians += screenDirection;
//
// mOrientationOK = true;
// }
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// int type = sensor.getType();
// switch (type) {
// case Sensor.TYPE_GRAVITY:
// mGravityAccuracy = accuracy;
// break;
// case Sensor.TYPE_MAGNETIC_FIELD:
// mMagneticFieldAccuracy = accuracy;
// break;
// }
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2012 Hannes Janetzek
* Copyright 2016 devemux86
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.location;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.TextView;
import org.oscim.app.App;
import org.oscim.app.R;
import org.oscim.app.TileMap;
import org.oscim.core.MapPosition;
import org.oscim.map.Map;
public class LocationDialog {
public void prepareDialog(Map map, final Dialog dialog) {
EditText editText = (EditText) dialog.findViewById(R.id.latitude);
MapPosition mapCenter = map.getMapPosition();
editText.setText(Double.toString(mapCenter.getLatitude()));
editText = (EditText) dialog.findViewById(R.id.longitude);
editText.setText(Double.toString(mapCenter.getLongitude()));
SeekBar zoomlevel = (SeekBar) dialog.findViewById(R.id.zoomLevel);
zoomlevel.setMax(20);
zoomlevel.setProgress(10);
final TextView textView = (TextView) dialog.findViewById(R.id.zoomlevelValue);
textView.setText(String.valueOf(zoomlevel.getProgress()));
zoomlevel.setOnSeekBarChangeListener(new SeekBarChangeListener(textView));
}
public Dialog createDialog(final TileMap map) {
AlertDialog.Builder builder = new AlertDialog.Builder(map);
builder.setIcon(android.R.drawable.ic_menu_mylocation);
builder.setTitle(R.string.menu_position_enter_coordinates);
LayoutInflater factory = LayoutInflater.from(map);
final View view = factory.inflate(R.layout.dialog_enter_coordinates, null);
builder.setView(view);
builder.setPositiveButton(R.string.go_to_position,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// disable GPS follow mode if it is enabled
//map.mLocation.disableSnapToLocation();
if (map.getLocationHandler().getMode() == LocationHandler.Mode.SNAP)
map.getLocationHandler()
.setMode(LocationHandler.Mode.SHOW);
// set the map center and zoom level
EditText latitudeView = (EditText) view
.findViewById(R.id.latitude);
EditText longitudeView = (EditText) view
.findViewById(R.id.longitude);
double latitude = Double.parseDouble(latitudeView.getText()
.toString());
double longitude = Double.parseDouble(longitudeView.getText()
.toString());
SeekBar zoomLevelView = (SeekBar) view
.findViewById(R.id.zoomLevel);
int zoom = zoomLevelView.getProgress();
MapPosition mapPosition = new MapPosition();
mapPosition.setPosition(latitude, longitude);
mapPosition.setZoomLevel(zoom);
App.map.setMapPosition(mapPosition);
}
});
builder.setNegativeButton(R.string.cancel, null);
return builder.create();
}
class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
private final TextView textView;
SeekBarChangeListener(TextView textView) {
this.textView = textView;
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
this.textView.setText(String.valueOf(progress));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// do nothing
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// do nothing
}
}
}

View File

@@ -0,0 +1,236 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2013 Hannes Janetzek
* Copyright 2013 Ahmad Al-saleem
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.location;
import android.content.Context;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import org.oscim.app.App;
import org.oscim.app.R;
import org.oscim.app.TileMap;
import org.oscim.core.MapPosition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LocationHandler implements LocationListener {
final static Logger log = LoggerFactory.getLogger(LocationHandler.class);
public enum Mode {
OFF,
SHOW,
SNAP,
}
private final static int DIALOG_LOCATION_PROVIDER_DISABLED = 2;
private final static int SHOW_LOCATION_ZOOM = 14;
private final LocationManager mLocationManager;
private final LocationOverlay mLocationOverlay;
private Mode mMode = Mode.OFF;
private boolean mSetCenter;
private MapPosition mMapPosition;
public LocationHandler(TileMap tileMap, Compass compass) {
mLocationManager = (LocationManager) tileMap
.getSystemService(Context.LOCATION_SERVICE);
mLocationOverlay = new LocationOverlay(App.map, compass);
mMapPosition = new MapPosition();
}
public boolean setMode(Mode mode) {
if (mode == mMode)
return true;
if (mode == Mode.OFF) {
disableShowMyLocation();
if (mMode == Mode.SNAP)
App.map.getEventLayer().enableMove(true);
}
if (mMode == Mode.OFF) {
if (!enableShowMyLocation())
return false;
}
if (mode == Mode.SNAP) {
App.map.getEventLayer().enableMove(false);
gotoLastKnownPosition();
} else {
App.map.getEventLayer().enableMove(true);
}
// FIXME?
mSetCenter = false;
mMode = mode;
return true;
}
public Mode getMode() {
return mMode;
}
public boolean isFirstCenter() {
return mSetCenter;
}
@SuppressWarnings("deprecation")
private boolean enableShowMyLocation() {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
String bestProvider = mLocationManager.getBestProvider(criteria, true);
if (bestProvider == null) {
App.activity.showDialog(DIALOG_LOCATION_PROVIDER_DISABLED);
return false;
}
mLocationManager.requestLocationUpdates(bestProvider, 10000, 10, this);
Location location = gotoLastKnownPosition();
if (location == null)
return false;
mLocationOverlay.setEnabled(true);
mLocationOverlay.setPosition(location.getLatitude(),
location.getLongitude(),
location.getAccuracy());
// FIXME -> implement LayerGroup
App.map.layers().add(4, mLocationOverlay);
App.map.updateMap(true);
return true;
}
/**
* Disable "show my location" mode.
*/
private boolean disableShowMyLocation() {
mLocationManager.removeUpdates(this);
mLocationOverlay.setEnabled(false);
App.map.layers().remove(mLocationOverlay);
App.map.updateMap(true);
return true;
}
public Location gotoLastKnownPosition() {
Location location = null;
float bestAccuracy = Float.MAX_VALUE;
for (String provider : mLocationManager.getProviders(true)) {
Location l = mLocationManager.getLastKnownLocation(provider);
if (l == null)
continue;
float accuracy = l.getAccuracy();
if (accuracy <= 0)
accuracy = Float.MAX_VALUE;
if (location == null || accuracy <= bestAccuracy) {
location = l;
bestAccuracy = accuracy;
}
}
if (location == null) {
App.activity.showToastOnUiThread(App.activity
.getString(R.string.error_last_location_unknown));
return null;
}
App.map.getMapPosition(mMapPosition);
if (mMapPosition.zoomLevel < SHOW_LOCATION_ZOOM)
mMapPosition.setZoomLevel(SHOW_LOCATION_ZOOM);
mMapPosition.setPosition(location.getLatitude(), location.getLongitude());
App.map.setMapPosition(mMapPosition);
return location;
}
/***
* LocationListener
***/
@Override
public void onLocationChanged(Location location) {
if (mMode == Mode.OFF)
return;
double lat = location.getLatitude();
double lon = location.getLongitude();
log.debug("update location " + lat + ":" + lon);
if (mSetCenter || mMode == Mode.SNAP) {
mSetCenter = false;
App.map.getMapPosition(mMapPosition);
mMapPosition.setPosition(lat, lon);
App.map.setMapPosition(mMapPosition);
}
mLocationOverlay.setPosition(lat, lon, location.getAccuracy());
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
public void setCenterOnFirstFix() {
mSetCenter = true;
}
public void pause() {
if (mMode != Mode.OFF) {
log.debug("pause location listener");
}
}
public void resume() {
if (mMode != Mode.OFF) {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
String bestProvider = mLocationManager.getBestProvider(criteria, true);
mLocationManager.requestLocationUpdates(bestProvider, 10000, 10, this);
}
}
}

View File

@@ -0,0 +1,331 @@
/*
* Copyright 2013 Ahmad Saleem
* Copyright 2013 Hannes Janetzek
* Copyright 2016 devemux86
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.location;
import android.os.SystemClock;
import org.oscim.backend.GL;
import org.oscim.core.Box;
import org.oscim.core.MercatorProjection;
import org.oscim.core.Point;
import org.oscim.core.Tile;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
import org.oscim.renderer.GLShader;
import org.oscim.renderer.GLState;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.LayerRenderer;
import org.oscim.renderer.MapRenderer;
import org.oscim.utils.FastMath;
import org.oscim.utils.math.Interpolation;
import static org.oscim.backend.GLAdapter.gl;
public class LocationOverlay extends Layer {
private final int SHOW_ACCURACY_ZOOM = 16;
private final Point mLocation = new Point();
private double mRadius;
private final Compass mCompass;
public LocationOverlay(Map map, Compass compass) {
super(map);
mRenderer = new LocationIndicator(map);
mCompass = compass;
}
public void setPosition(double latitude, double longitude, double accuracy) {
mLocation.x = MercatorProjection.longitudeToX(longitude);
mLocation.y = MercatorProjection.latitudeToY(latitude);
mRadius = accuracy / MercatorProjection.groundResolution(latitude, 1);
((LocationIndicator) mRenderer).animate(true);
}
@Override
public void setEnabled(boolean enabled) {
if (enabled == isEnabled())
return;
super.setEnabled(enabled);
if (!enabled)
((LocationIndicator) mRenderer).animate(false);
mCompass.setEnabled(enabled);
}
public class LocationIndicator extends LayerRenderer {
private int mShaderProgram;
private int hVertexPosition;
private int hMatrixPosition;
private int hScale;
private int hPhase;
private int hDirection;
private final float CIRCLE_SIZE = 60;
private final static long ANIM_RATE = 50;
private final static long INTERVAL = 2000;
private final Point mIndicatorPosition = new Point();
private final Point mScreenPoint = new Point();
private final Box mBBox = new Box();
private boolean mInitialized;
private boolean mLocationIsVisible;
private boolean mRunAnim;
private long mAnimStart;
public LocationIndicator(final Map map) {
super();
}
private void animate(boolean enable) {
if (mRunAnim == enable)
return;
mRunAnim = enable;
if (!enable)
return;
final Runnable action = new Runnable() {
private long lastRun;
@Override
public void run() {
if (!mRunAnim)
return;
long diff = SystemClock.elapsedRealtime() - lastRun;
mMap.postDelayed(this, Math.min(ANIM_RATE, diff));
mMap.render();
}
};
mAnimStart = SystemClock.elapsedRealtime();
mMap.postDelayed(action, ANIM_RATE);
}
private float animPhase() {
return (float) ((MapRenderer.frametime - mAnimStart) % INTERVAL) / INTERVAL;
}
@Override
public void update(GLViewport v) {
if (!mInitialized) {
init();
mInitialized = true;
}
if (!isEnabled()) {
setReady(false);
return;
}
if (!v.changed() && isReady())
return;
setReady(true);
int width = mMap.getWidth();
int height = mMap.getHeight();
// clamp location to a position that can be
// savely translated to screen coordinates
v.getBBox(mBBox, 0);
double x = mLocation.x;
double y = mLocation.y;
if (!mBBox.contains(mLocation)) {
x = FastMath.clamp(x, mBBox.xmin, mBBox.xmax);
y = FastMath.clamp(y, mBBox.ymin, mBBox.ymax);
}
// get position of Location in pixel relative to
// screen center
v.toScreenPoint(x, y, mScreenPoint);
x = mScreenPoint.x + width / 2;
y = mScreenPoint.y + height / 2;
// clip position to screen boundaries
int visible = 0;
if (x > width - 5)
x = width;
else if (x < 5)
x = 0;
else
visible++;
if (y > height - 5)
y = height;
else if (y < 5)
y = 0;
else
visible++;
mLocationIsVisible = (visible == 2);
// set location indicator position
v.fromScreenPoint(x, y, mIndicatorPosition);
}
@Override
public void render(GLViewport v) {
GLState.useProgram(mShaderProgram);
GLState.blend(true);
GLState.test(false, false);
GLState.enableVertexArrays(hVertexPosition, -1);
MapRenderer.bindQuadVertexVBO(hVertexPosition/*, true*/);
float radius = CIRCLE_SIZE;
animate(true);
boolean viewShed = false;
if (!mLocationIsVisible /* || pos.zoomLevel < SHOW_ACCURACY_ZOOM */) {
//animate(true);
} else {
if (v.pos.zoomLevel >= SHOW_ACCURACY_ZOOM)
radius = (float) (mRadius * v.pos.scale);
viewShed = true;
//animate(false);
}
gl.uniform1f(hScale, radius);
double x = mIndicatorPosition.x - v.pos.x;
double y = mIndicatorPosition.y - v.pos.y;
double tileScale = Tile.SIZE * v.pos.scale;
v.mvp.setTransScale((float) (x * tileScale), (float) (y * tileScale), 1);
v.mvp.multiplyMM(v.viewproj, v.mvp);
v.mvp.setAsUniform(hMatrixPosition);
if (!viewShed) {
float phase = Math.abs(animPhase() - 0.5f) * 2;
//phase = Interpolation.fade.apply(phase);
phase = Interpolation.swing.apply(phase);
gl.uniform1f(hPhase, 0.8f + phase * 0.2f);
} else {
gl.uniform1f(hPhase, 1);
}
if (viewShed && mLocationIsVisible) {
float rotation = mCompass.getRotation() - 90;
gl.uniform2f(hDirection,
(float) Math.cos(Math.toRadians(rotation)),
(float) Math.sin(Math.toRadians(rotation)));
} else {
gl.uniform2f(hDirection, 0, 0);
}
gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
}
private boolean init() {
int shader = GLShader.createProgram(vShaderStr, fShaderStr);
if (shader == 0)
return false;
mShaderProgram = shader;
hVertexPosition = gl.getAttribLocation(shader, "a_pos");
hMatrixPosition = gl.getUniformLocation(shader, "u_mvp");
hPhase = gl.getUniformLocation(shader, "u_phase");
hScale = gl.getUniformLocation(shader, "u_scale");
hDirection = gl.getUniformLocation(shader, "u_dir");
return true;
}
private final static String vShaderStr = ""
+ "precision mediump float;"
+ "uniform mat4 u_mvp;"
+ "uniform float u_phase;"
+ "uniform float u_scale;"
+ "attribute vec2 a_pos;"
+ "varying vec2 v_tex;"
+ "void main() {"
+ " gl_Position = u_mvp * vec4(a_pos * u_scale * u_phase, 0.0, 1.0);"
+ " v_tex = a_pos;"
+ "}";
private final static String fShaderStr = ""
+ "precision mediump float;"
+ "varying vec2 v_tex;"
+ "uniform float u_scale;"
+ "uniform float u_phase;"
+ "uniform vec2 u_dir;"
+ "void main() {"
+ " float len = 1.0 - length(v_tex);"
+ " if (u_dir.x == 0.0 && u_dir.y == 0.0){"
+ " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * len;"
+ " } else {"
/// outer ring
+ " float a = smoothstep(0.0, 2.0 / u_scale, len);"
/// inner ring
+ " float b = 0.5 * smoothstep(4.0 / u_scale, 5.0 / u_scale, len);"
/// center point
+ " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
+ " vec2 dir = normalize(v_tex);"
+ " float d = 1.0 - dot(dir, u_dir); "
/// 0.5 width of viewshed
+ " d = clamp(step(0.5, d), 0.4, 0.7);"
/// - subtract inner from outer to create the outline
/// - multiply by viewshed
/// - add center point
+ " a = d * (a - (b + c)) + c;"
+ " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
+ "}}";
//private final static String fShaderStr = ""
// + "precision mediump float;"
// + "varying vec2 v_tex;"
// + "uniform float u_scale;"
// + "uniform float u_phase;"
// + "uniform vec2 u_dir;"
// + "void main() {"
// + " float len = 1.0 - length(v_tex);"
// /// outer ring
// + " float a = smoothstep(0.0, 2.0 / u_scale, len);"
// /// inner ring
// + " float b = 0.8 * smoothstep(3.0 / u_scale, 4.0 / u_scale, len);"
// /// center point
// + " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
// + " vec2 dir = normalize(v_tex);"
// + " float d = dot(dir, u_dir); "
// /// 0.5 width of viewshed
// + " d = clamp(smoothstep(0.7, 0.7 + 2.0/u_scale, d) * len, 0.0, 1.0);"
// /// - subtract inner from outer to create the outline
// /// - multiply by viewshed
// /// - add center point
// + " a = max(d, (a - (b + c)) + c);"
// + " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
// + "}";
}
}

View File

@@ -0,0 +1,219 @@
package org.oscim.app.preferences;
import android.content.Context;
import android.content.res.TypedArray;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import org.oscim.app.R;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CacheSizePreference extends Preference implements OnSeekBarChangeListener {
final static Logger log = LoggerFactory.getLogger(CacheSizePreference.class);
private static final String NS_OCIM_APP = "http://app.oscim.org";
private static final int DEFAULT_VALUE = 50;
private int mMaxValue = 50;
private int mMinValue = 0;
private int mInterval = 1;
private int mCurrentValue;
private String mUnitsLeft = "";
private String mUnitsRight = "";
private SeekBar mSeekBar;
private TextView mStatusText;
public CacheSizePreference(Context context, AttributeSet attrs) {
super(context, attrs);
initPreference(context, attrs);
}
public CacheSizePreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initPreference(context, attrs);
}
private void initPreference(Context context, AttributeSet attrs) {
setValuesFromXml(attrs);
mSeekBar = new SeekBar(context, attrs);
mSeekBar.setMax(mMaxValue - mMinValue);
mSeekBar.setOnSeekBarChangeListener(this);
}
private void setValuesFromXml(AttributeSet attrs) {
//StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
//double sdAvailSize = (double) stat.getAvailableBlocks()
// * (double) stat.getBlockSize();
//One binary megabyte equals 1,048,576 bytes.
// otherwise we need an logarithmic scale to set a sane value with the slider...
int megaAvailable = 50; //(int)sdAvailSize / 1048576;
mMaxValue = megaAvailable;//attrs.getAttributeIntValue(ANDROIDNS, "max", 100);
mMinValue = attrs.getAttributeIntValue(NS_OCIM_APP, "min", 0);
mUnitsLeft = getAttributeStringValue(attrs, NS_OCIM_APP, "unitsLeft", "");
//String units = getAttributeStringValue(attrs, NS_OCIM_APP, "units", "");
mUnitsRight = "/" + String.valueOf(megaAvailable) + "MB";
try {
String newInterval = attrs.getAttributeValue(NS_OCIM_APP, "interval");
if (newInterval != null)
mInterval = Integer.parseInt(newInterval);
} catch (Exception e) {
log.error("", e);
}
}
private String getAttributeStringValue(AttributeSet attrs, String namespace, String name,
String defaultValue) {
String value = attrs.getAttributeValue(namespace, name);
if (value == null)
value = defaultValue;
return value;
}
@Override
protected View onCreateView(ViewGroup parent) {
RelativeLayout layout = null;
try {
LayoutInflater mInflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
layout = (RelativeLayout) mInflater
.inflate(R.layout.seek_bar_preference, parent, false);
} catch (Exception e) {
log.error("", e);
}
return layout;
}
@SuppressWarnings("deprecation")
@Override
public void onBindView(View view) {
super.onBindView(view);
try {
// move our seekbar to the new view we've been given
ViewParent oldContainer = mSeekBar.getParent();
ViewGroup newContainer = (ViewGroup) view.findViewById(R.id.seekBarPrefBarContainer);
if (oldContainer != newContainer) {
// remove the seekbar from the old view
if (oldContainer != null) {
((ViewGroup) oldContainer).removeView(mSeekBar);
}
// remove the existing seekbar (there may not be one) and add ours
newContainer.removeAllViews();
newContainer.addView(mSeekBar, ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
} catch (Exception ex) {
log.error("Error binding view: " + ex.toString());
}
updateView(view);
}
/**
* Update a SeekBarPreference view with our current state
*
* @param view
*/
protected void updateView(View view) {
try {
RelativeLayout layout = (RelativeLayout) view;
mStatusText = (TextView) layout.findViewById(R.id.seekBarPrefValue);
mStatusText.setText(String.valueOf(mCurrentValue));
mStatusText.setMinimumWidth(30);
mSeekBar.setProgress(mCurrentValue - mMinValue);
TextView unitsRight = (TextView) layout.findViewById(R.id.seekBarPrefUnitsRight);
unitsRight.setText(mUnitsRight);
TextView unitsLeft = (TextView) layout.findViewById(R.id.seekBarPrefUnitsLeft);
unitsLeft.setText(mUnitsLeft);
} catch (Exception e) {
log.error("", e);
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int newValue = progress + mMinValue;
if (newValue > mMaxValue)
newValue = mMaxValue;
else if (newValue < mMinValue)
newValue = mMinValue;
else if (mInterval != 1 && newValue % mInterval != 0)
newValue = Math.round(((float) newValue) / mInterval) * mInterval;
// change rejected, revert to the previous value
if (!callChangeListener(newValue)) {
seekBar.setProgress(mCurrentValue - mMinValue);
return;
}
// change accepted, store it
mCurrentValue = newValue;
mStatusText.setText(String.valueOf(newValue));
persistInt(newValue);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
notifyChanged();
}
@Override
protected Object onGetDefaultValue(TypedArray ta, int index) {
int defaultValue = ta.getInt(index, DEFAULT_VALUE);
return defaultValue;
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
if (restoreValue) {
mCurrentValue = getPersistedInt(mCurrentValue);
} else {
int temp = 0;
try {
temp = (Integer) defaultValue;
} catch (Exception ex) {
log.error("Invalid default value: " + defaultValue.toString());
}
persistInt(temp);
mCurrentValue = temp;
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.preferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import org.oscim.app.App;
import org.oscim.app.R;
/**
* Activity to edit the application preferences.
*/
public class EditPreferences extends PreferenceActivity {
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
Preference button = (Preference) findPreference("clear_cache");
button.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
App.activity.getMapLayers().deleteCache();
return true;
}
});
}
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.slide_left, R.anim.slide_right2);
}
// @TargetApi(11)
@Override
protected void onResume() {
super.onResume();
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
// getActionBar().hide();
// check if the full screen mode should be activated
// if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fullscreen",
// false)) {
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
// } else {
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
// }
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.app.preferences;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.preference.DialogPreference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
/**
* This abstract class provides all code for a seek bar preference. Deriving
* classes only need to set the current and
* maximum value of the seek bar. An optional text message above the seek bar is
* also supported as well as an optional
* current value message below the seek bar.
*/
abstract class SeekBarPreference extends DialogPreference implements OnSeekBarChangeListener {
private TextView mCurrentValueTextView;
private Editor mEditor;
private SeekBar mPreferenceSeekBar;
/**
* How much the value should increase when the seek bar is moved.
*/
int increment = 1;
/**
* The maximum value of the seek bar.
*/
int max;
/**
* Optional text message to display on top of the seek bar.
*/
String messageText;
/**
* The SharedPreferences instance that is used.
*/
final SharedPreferences preferencesDefault;
/**
* The current value of the seek bar.
*/
int seekBarCurrentValue;
/**
* Create a new seek bar preference.
*
* @param context the context of the seek bar preferences activity.
* @param attrs A set of attributes (currently ignored).
*/
SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
preferencesDefault = PreferenceManager.getDefaultSharedPreferences(context);
}
@Override
public void onClick(DialogInterface dialog, int which) {
// check if the "OK" button was pressed and the seek bar value has changed
if (which == DialogInterface.BUTTON_POSITIVE
&& seekBarCurrentValue != mPreferenceSeekBar.getProgress()) {
// get the value of the seek bar and save it in the preferences
seekBarCurrentValue = mPreferenceSeekBar.getProgress();
mEditor = preferencesDefault.edit();
mEditor.putInt(getKey(), seekBarCurrentValue);
mEditor.commit();
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (mCurrentValueTextView != null) {
mCurrentValueTextView.setText(getCurrentValueText(progress));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// do nothing
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// do nothing
}
@Override
protected View onCreateDialogView() {
// create a layout for the optional text messageText and the seek bar
LinearLayout linearLayout = new LinearLayout(getContext());
linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.setPadding(20, 10, 20, 10);
// check if a text message should appear above the seek bar
if (messageText != null) {
// create a text view for the text messageText
TextView messageTextView = new TextView(getContext());
messageTextView.setText(messageText);
messageTextView.setPadding(0, 0, 0, 20);
// add the text message view to the layout
linearLayout.addView(messageTextView);
}
// create the seek bar and set the maximum and current value
mPreferenceSeekBar = new SeekBar(getContext());
mPreferenceSeekBar.setOnSeekBarChangeListener(this);
mPreferenceSeekBar.setMax(max);
mPreferenceSeekBar.setProgress(Math.min(seekBarCurrentValue, max));
mPreferenceSeekBar.setKeyProgressIncrement(increment);
mPreferenceSeekBar.setPadding(0, 0, 0, 10);
// add the seek bar to the layout
linearLayout.addView(mPreferenceSeekBar);
// create the text view for the current value below the seek bar
mCurrentValueTextView = new TextView(getContext());
mCurrentValueTextView.setText(getCurrentValueText(mPreferenceSeekBar.getProgress()));
mCurrentValueTextView.setGravity(Gravity.CENTER_HORIZONTAL);
// add the current value text view to the layout
linearLayout.addView(mCurrentValueTextView);
return linearLayout;
}
/**
* Get the current value text.
*
* @param progress the current progress level of the seek bar.
* @return the new current value text
*/
abstract String getCurrentValueText(int progress);
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2013 Ahmad Saleem
* Copyright 2013 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.overlay;
import org.oscim.core.GeoPoint;
import org.oscim.event.Event;
import org.oscim.event.Gesture;
import org.oscim.event.GestureListener;
import org.oscim.event.MotionEvent;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
import org.osmdroid.overlays.MapEventsReceiver;
import java.util.Timer;
import java.util.TimerTask;
public class DistanceTouchOverlay extends Layer implements Map.InputListener,
GestureListener {
private static final int LONGPRESS_THRESHOLD = 800;
private Timer mLongpressTimer;
private float mPrevX1, mPrevX2, mPrevY1, mPrevY2;
private float mCurX1, mCurX2, mCurY1, mCurY2;
// private final static int POINTER_UP = -1;
// private int mPointer1 = POINTER_UP;
// private int mPointer2 = POINTER_UP;
private final MapEventsReceiver mReceiver;
/**
* @param map the Map
* @param receiver the object that will receive/handle the events. It must
* implement MapEventsReceiver interface.
*/
public DistanceTouchOverlay(Map map, MapEventsReceiver receiver) {
super(map);
mReceiver = receiver;
}
@Override
public void onDetach() {
super.onDetach();
}
private void cancel() {
if (mLongpressTimer != null) {
mLongpressTimer.cancel();
mLongpressTimer = null;
}
}
@Override
public void onInputEvent(Event event, MotionEvent e) {
int action = e.getAction() & MotionEvent.ACTION_MASK;
if ((action == MotionEvent.ACTION_CANCEL)) {
cancel();
return;
}
if (mLongpressTimer != null) {
// any pointer up while long press detection
// cancels timer
if (action == MotionEvent.ACTION_POINTER_UP
|| action == MotionEvent.ACTION_UP) {
cancel();
return;
}
// two fingers must still be down, tested
// one above.
if (action == MotionEvent.ACTION_MOVE) {
// update pointer positions
// int idx1 = e.findPointerIndex(mPointer1);
// int idx2 = e.findPointerIndex(mPointer2);
mCurX1 = e.getX(0);
mCurY1 = e.getY(0);
mCurX2 = e.getX(1);
mCurY2 = e.getY(1);
// cancel if moved one finger more than 50 pixel
float maxSq = 10 * 10;
float d = (mCurX1 - mPrevX1) * (mCurX1 - mPrevX1)
+ (mCurY1 - mPrevY1) * (mCurY1 - mPrevY1);
if (d > maxSq) {
cancel();
return;
}
d = (mCurX2 - mPrevX2) * (mCurX2 - mPrevX2)
+ (mCurY2 - mPrevY2) * (mCurY2 - mPrevY2);
if (d > maxSq) {
cancel();
return;
}
}
}
if ((action == MotionEvent.ACTION_POINTER_DOWN)
&& (e.getPointerCount() == 2)) {
// App.log.debug("down");
// keep track of pointer ids, only
// use these for gesture, ignoring
// more than two pointer
// mPointer1 = e.getPointerId(0);
// mPointer2 = e.getPointerId(1);
if (mLongpressTimer == null) {
// start timer, keep initial down position
mCurX1 = mPrevX1 = e.getX(0);
mCurY1 = mPrevY1 = e.getY(0);
mCurX2 = mPrevX2 = e.getX(1);
mCurY2 = mPrevY2 = e.getY(1);
runLongpressTimer();
}
}
}
// @Override
// public boolean onLongPress(MotionEvent e) {
// // dont forward long press when two fingers are down.
// // maybe should be only done if our timer is still running.
// // ... not sure if this is even needed
// GeoPoint p = mMap.getViewport().fromScreenPoint(e.getX(), e.getY());
// return mReceiver.longPressHelper(p);
// }
public void runLongpressTimer() {
// mMap.postDelayed(action, delay);
mLongpressTimer = new Timer();
mLongpressTimer.schedule(new TimerTask() {
@Override
public void run() {
final GeoPoint p1 = mMap.viewport().fromScreenPoint(mCurX1,
mCurY1);
final GeoPoint p2 = mMap.viewport().fromScreenPoint(mCurX2,
mCurY2);
mMap.post(new Runnable() {
@Override
public void run() {
mReceiver.longPressHelper(p1, p2);
}
});
}
}, LONGPRESS_THRESHOLD);
}
@Override
public boolean onGesture(Gesture g, MotionEvent e) {
if (g instanceof Gesture.LongPress) {
GeoPoint p = mMap.viewport().fromScreenPoint(e.getX(), e.getY());
return mReceiver.longPressHelper(p);
}
return false;
}
}