diff --git a/OsmAndCore-sample/.gitignore b/OsmAndCore-sample/.gitignore new file mode 100644 index 0000000000..9d3ed1dd63 --- /dev/null +++ b/OsmAndCore-sample/.gitignore @@ -0,0 +1,13 @@ +# Android Studio +/.idea +*.iml + +# Gradle +.gradle +/local.properties + +# MacOSX +.DS_Store + +# Output +/build diff --git a/OsmAndCore-sample/AndroidManifest.xml b/OsmAndCore-sample/AndroidManifest.xml new file mode 100644 index 0000000000..da3ce99223 --- /dev/null +++ b/OsmAndCore-sample/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + diff --git a/OsmAndCore-sample/build.gradle b/OsmAndCore-sample/build.gradle new file mode 100644 index 0000000000..8e537e3355 --- /dev/null +++ b/OsmAndCore-sample/build.gradle @@ -0,0 +1,92 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.3" + + defaultConfig { + minSdkVersion 14 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + + lintOptions { + abortOnError false + } + + // This is from OsmAndCore_android.aar - for some reason it's not inherited + aaptOptions { + // Don't compress any embedded resources + noCompress "qz" + } + + sourceSets { + main { + manifest.srcFile "AndroidManifest.xml" + jni.srcDirs = [] + jniLibs.srcDirs = ["libs"] + java.srcDirs = ["src"] + resources.srcDirs = ["src"] + renderscript.srcDirs = ["src"] + res.srcDirs = ["res"] + assets.srcDirs = ["assets"] + } + } + + productFlavors { + x86 { + ndk { + abiFilter "x86" + } + } + mips { + ndk { + abiFilter "mips" + } + } + armv7 { + ndk { + abiFilter "armeabi-v7a" + } + } + armv5 { + ndk { + abiFilter "armeabi" + } + } + fat + } + + buildTypes { + debug { + signingConfig android.signingConfigs.debug + } + nativeDebug { + signingConfig android.signingConfigs.debug + } + release { + signingConfig android.signingConfigs.debug + } + } +} + +repositories { + ivy { + name = "OsmAndBinariesIvy" + url = "http://builder.osmand.net" + layout "pattern" , { + artifact "ivy/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" + } + } +} + +dependencies { + compile project(path: ':OsmAnd-java', configuration: 'android') + compile 'com.android.support:appcompat-v7:23.3.0' + compile fileTree(dir: "libs", include: ["*.jar"]) + compile "net.osmand:OsmAndCore_android:0.1-SNAPSHOT@aar" + debugCompile "net.osmand:OsmAndCore_androidNativeRelease:0.1-SNAPSHOT@aar" + nativeDebugCompile "net.osmand:OsmAndCore_androidNativeDebug:0.1-SNAPSHOT@aar" + releaseCompile "net.osmand:OsmAndCore_androidNativeRelease:0.1-SNAPSHOT@aar" +} diff --git a/OsmAndCore-sample/res/drawable-hdpi/bg_contextmenu_shadow.9.png b/OsmAndCore-sample/res/drawable-hdpi/bg_contextmenu_shadow.9.png new file mode 100644 index 0000000000..0356c4a541 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-hdpi/bg_contextmenu_shadow.9.png differ diff --git a/OsmAndCore-sample/res/drawable-hdpi/ic_action_remove_dark.png b/OsmAndCore-sample/res/drawable-hdpi/ic_action_remove_dark.png new file mode 100644 index 0000000000..718053c411 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-hdpi/ic_action_remove_dark.png differ diff --git a/OsmAndCore-sample/res/drawable-hdpi/map_bt_round_1_shadow.png b/OsmAndCore-sample/res/drawable-hdpi/map_bt_round_1_shadow.png new file mode 100644 index 0000000000..159d4c08b5 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-hdpi/map_bt_round_1_shadow.png differ diff --git a/OsmAndCore-sample/res/drawable-hdpi/map_compass_niu.png b/OsmAndCore-sample/res/drawable-hdpi/map_compass_niu.png new file mode 100644 index 0000000000..3ae3157990 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-hdpi/map_compass_niu.png differ diff --git a/OsmAndCore-sample/res/drawable-hdpi/map_zoom_in.png b/OsmAndCore-sample/res/drawable-hdpi/map_zoom_in.png new file mode 100644 index 0000000000..d37a733a9b Binary files /dev/null and b/OsmAndCore-sample/res/drawable-hdpi/map_zoom_in.png differ diff --git a/OsmAndCore-sample/res/drawable-hdpi/map_zoom_out.png b/OsmAndCore-sample/res/drawable-hdpi/map_zoom_out.png new file mode 100644 index 0000000000..36a88d3536 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-hdpi/map_zoom_out.png differ diff --git a/OsmAndCore-sample/res/drawable-mdpi/bg_contextmenu_shadow.9.png b/OsmAndCore-sample/res/drawable-mdpi/bg_contextmenu_shadow.9.png new file mode 100644 index 0000000000..f5984c27ce Binary files /dev/null and b/OsmAndCore-sample/res/drawable-mdpi/bg_contextmenu_shadow.9.png differ diff --git a/OsmAndCore-sample/res/drawable-mdpi/ic_action_remove_dark.png b/OsmAndCore-sample/res/drawable-mdpi/ic_action_remove_dark.png new file mode 100644 index 0000000000..939f58b193 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-mdpi/ic_action_remove_dark.png differ diff --git a/OsmAndCore-sample/res/drawable-mdpi/map_bt_round_1_shadow.png b/OsmAndCore-sample/res/drawable-mdpi/map_bt_round_1_shadow.png new file mode 100644 index 0000000000..586ba999a4 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-mdpi/map_bt_round_1_shadow.png differ diff --git a/OsmAndCore-sample/res/drawable-mdpi/map_compass_niu.png b/OsmAndCore-sample/res/drawable-mdpi/map_compass_niu.png new file mode 100644 index 0000000000..5207161498 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-mdpi/map_compass_niu.png differ diff --git a/OsmAndCore-sample/res/drawable-mdpi/map_zoom_in.png b/OsmAndCore-sample/res/drawable-mdpi/map_zoom_in.png new file mode 100644 index 0000000000..938a4d4805 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-mdpi/map_zoom_in.png differ diff --git a/OsmAndCore-sample/res/drawable-mdpi/map_zoom_out.png b/OsmAndCore-sample/res/drawable-mdpi/map_zoom_out.png new file mode 100644 index 0000000000..2051dbd1a6 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-mdpi/map_zoom_out.png differ diff --git a/OsmAndCore-sample/res/drawable-xhdpi/bg_contextmenu_shadow.9.png b/OsmAndCore-sample/res/drawable-xhdpi/bg_contextmenu_shadow.9.png new file mode 100644 index 0000000000..b40ce9b5b7 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xhdpi/bg_contextmenu_shadow.9.png differ diff --git a/OsmAndCore-sample/res/drawable-xhdpi/ic_action_remove_dark.png b/OsmAndCore-sample/res/drawable-xhdpi/ic_action_remove_dark.png new file mode 100644 index 0000000000..7f2de6194e Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xhdpi/ic_action_remove_dark.png differ diff --git a/OsmAndCore-sample/res/drawable-xhdpi/map_bt_round_1_shadow.png b/OsmAndCore-sample/res/drawable-xhdpi/map_bt_round_1_shadow.png new file mode 100644 index 0000000000..2459bd66ee Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xhdpi/map_bt_round_1_shadow.png differ diff --git a/OsmAndCore-sample/res/drawable-xhdpi/map_compass_niu.png b/OsmAndCore-sample/res/drawable-xhdpi/map_compass_niu.png new file mode 100644 index 0000000000..05f8351357 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xhdpi/map_compass_niu.png differ diff --git a/OsmAndCore-sample/res/drawable-xhdpi/map_zoom_in.png b/OsmAndCore-sample/res/drawable-xhdpi/map_zoom_in.png new file mode 100644 index 0000000000..eeb0b1a9cc Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xhdpi/map_zoom_in.png differ diff --git a/OsmAndCore-sample/res/drawable-xhdpi/map_zoom_out.png b/OsmAndCore-sample/res/drawable-xhdpi/map_zoom_out.png new file mode 100644 index 0000000000..8e62ba2a84 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xhdpi/map_zoom_out.png differ diff --git a/OsmAndCore-sample/res/drawable-xxhdpi/bg_contextmenu_shadow.9.png b/OsmAndCore-sample/res/drawable-xxhdpi/bg_contextmenu_shadow.9.png new file mode 100644 index 0000000000..89b8ffc35e Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xxhdpi/bg_contextmenu_shadow.9.png differ diff --git a/OsmAndCore-sample/res/drawable-xxhdpi/ic_action_remove_dark.png b/OsmAndCore-sample/res/drawable-xxhdpi/ic_action_remove_dark.png new file mode 100644 index 0000000000..ea01fe05e2 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xxhdpi/ic_action_remove_dark.png differ diff --git a/OsmAndCore-sample/res/drawable-xxhdpi/map_bt_round_1_shadow.png b/OsmAndCore-sample/res/drawable-xxhdpi/map_bt_round_1_shadow.png new file mode 100644 index 0000000000..04833b06d2 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xxhdpi/map_bt_round_1_shadow.png differ diff --git a/OsmAndCore-sample/res/drawable-xxhdpi/map_compass_niu.png b/OsmAndCore-sample/res/drawable-xxhdpi/map_compass_niu.png new file mode 100644 index 0000000000..1e039c7f47 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xxhdpi/map_compass_niu.png differ diff --git a/OsmAndCore-sample/res/drawable-xxhdpi/map_zoom_in.png b/OsmAndCore-sample/res/drawable-xxhdpi/map_zoom_in.png new file mode 100644 index 0000000000..363037676d Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xxhdpi/map_zoom_in.png differ diff --git a/OsmAndCore-sample/res/drawable-xxhdpi/map_zoom_out.png b/OsmAndCore-sample/res/drawable-xxhdpi/map_zoom_out.png new file mode 100644 index 0000000000..10f88a6623 Binary files /dev/null and b/OsmAndCore-sample/res/drawable-xxhdpi/map_zoom_out.png differ diff --git a/OsmAndCore-sample/res/drawable/btn_circle.xml b/OsmAndCore-sample/res/drawable/btn_circle.xml new file mode 100644 index 0000000000..96603af448 --- /dev/null +++ b/OsmAndCore-sample/res/drawable/btn_circle.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/OsmAndCore-sample/res/drawable/btn_circle_n.xml b/OsmAndCore-sample/res/drawable/btn_circle_n.xml new file mode 100644 index 0000000000..dfbe7a2326 --- /dev/null +++ b/OsmAndCore-sample/res/drawable/btn_circle_n.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAndCore-sample/res/drawable/btn_circle_p.xml b/OsmAndCore-sample/res/drawable/btn_circle_p.xml new file mode 100644 index 0000000000..9574b2cbd5 --- /dev/null +++ b/OsmAndCore-sample/res/drawable/btn_circle_p.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAndCore-sample/res/layout/activity_main.xml b/OsmAndCore-sample/res/layout/activity_main.xml new file mode 100755 index 0000000000..5fb4343512 --- /dev/null +++ b/OsmAndCore-sample/res/layout/activity_main.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OsmAndCore-sample/res/layout/search_list_item.xml b/OsmAndCore-sample/res/layout/search_list_item.xml new file mode 100644 index 0000000000..81d63628f9 --- /dev/null +++ b/OsmAndCore-sample/res/layout/search_list_item.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAndCore-sample/res/mipmap-hdpi/sample_app.png b/OsmAndCore-sample/res/mipmap-hdpi/sample_app.png new file mode 100755 index 0000000000..80663eef15 Binary files /dev/null and b/OsmAndCore-sample/res/mipmap-hdpi/sample_app.png differ diff --git a/OsmAndCore-sample/res/mipmap-mdpi/sample_app.png b/OsmAndCore-sample/res/mipmap-mdpi/sample_app.png new file mode 100755 index 0000000000..0d1df64202 Binary files /dev/null and b/OsmAndCore-sample/res/mipmap-mdpi/sample_app.png differ diff --git a/OsmAndCore-sample/res/mipmap-xhdpi/sample_app.png b/OsmAndCore-sample/res/mipmap-xhdpi/sample_app.png new file mode 100755 index 0000000000..614165ae18 Binary files /dev/null and b/OsmAndCore-sample/res/mipmap-xhdpi/sample_app.png differ diff --git a/OsmAndCore-sample/res/mipmap-xxhdpi/sample_app.png b/OsmAndCore-sample/res/mipmap-xxhdpi/sample_app.png new file mode 100755 index 0000000000..21e771e448 Binary files /dev/null and b/OsmAndCore-sample/res/mipmap-xxhdpi/sample_app.png differ diff --git a/OsmAndCore-sample/res/mipmap-xxxhdpi/sample_app.png b/OsmAndCore-sample/res/mipmap-xxxhdpi/sample_app.png new file mode 100755 index 0000000000..20e83f11d8 Binary files /dev/null and b/OsmAndCore-sample/res/mipmap-xxxhdpi/sample_app.png differ diff --git a/OsmAndCore-sample/res/values-w820dp/dimens.xml b/OsmAndCore-sample/res/values-w820dp/dimens.xml new file mode 100644 index 0000000000..63fc816444 --- /dev/null +++ b/OsmAndCore-sample/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/OsmAndCore-sample/res/values/colors.xml b/OsmAndCore-sample/res/values/colors.xml new file mode 100644 index 0000000000..695c2bbee3 --- /dev/null +++ b/OsmAndCore-sample/res/values/colors.xml @@ -0,0 +1,17 @@ + + + #ff8f00 + #e68200 + #FF4081 + + #F0F0F0 + #EAEAEA + #FFF + #727272 + #2f7af5 + + #EAEAEA + #FFF + #000 + + diff --git a/OsmAndCore-sample/res/values/dimens.xml b/OsmAndCore-sample/res/values/dimens.xml new file mode 100644 index 0000000000..91dec05887 --- /dev/null +++ b/OsmAndCore-sample/res/values/dimens.xml @@ -0,0 +1,6 @@ + + + 16dp + 16dp + 16dp + diff --git a/OsmAndCore-sample/res/values/strings.xml b/OsmAndCore-sample/res/values/strings.xml new file mode 100644 index 0000000000..0586d71b0e --- /dev/null +++ b/OsmAndCore-sample/res/values/strings.xml @@ -0,0 +1,10 @@ + + + OsmAnd Core API sample 1 for Android OS + OsmAnd Core Sample 1 + Zoom in + Zoom out + Azimuth to North + Type and Search + Close + diff --git a/OsmAndCore-sample/res/values/styles.xml b/OsmAndCore-sample/res/values/styles.xml new file mode 100644 index 0000000000..26dd38c319 --- /dev/null +++ b/OsmAndCore-sample/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java new file mode 100644 index 0000000000..a845426429 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java @@ -0,0 +1,546 @@ +package net.osmand.core.samples.android.sample1; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.graphics.PointF; +import android.os.Bundle; +import android.os.Environment; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorListener; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.MotionEvent; +import android.view.View; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ListView; +import android.widget.TextView; + +import net.osmand.core.android.AtlasMapRendererView; +import net.osmand.core.android.CoreResourcesFromAndroidAssets; +import net.osmand.core.android.NativeCore; +import net.osmand.core.jni.AreaI; +import net.osmand.core.jni.IMapLayerProvider; +import net.osmand.core.jni.IMapStylesCollection; +import net.osmand.core.jni.LatLon; +import net.osmand.core.jni.LogSeverityLevel; +import net.osmand.core.jni.Logger; +import net.osmand.core.jni.MapObjectsSymbolsProvider; +import net.osmand.core.jni.MapPresentationEnvironment; +import net.osmand.core.jni.MapPrimitivesProvider; +import net.osmand.core.jni.MapPrimitiviser; +import net.osmand.core.jni.MapRasterLayerProvider_Software; +import net.osmand.core.jni.MapStylesCollection; +import net.osmand.core.jni.ObfMapObjectsProvider; +import net.osmand.core.jni.ObfsCollection; +import net.osmand.core.jni.PointI; +import net.osmand.core.jni.QIODeviceLogSink; +import net.osmand.core.jni.ResolvedMapStyle; +import net.osmand.core.jni.Utilities; +import net.osmand.core.samples.android.sample1.MultiTouchSupport.MultiTouchZoomListener; +import net.osmand.core.samples.android.sample1.SearchAPI.SearchAPICallback; +import net.osmand.core.samples.android.sample1.SearchAPI.SearchItem; +import net.osmand.core.samples.android.sample1.SearchUIHelper.SearchListAdapter; +import net.osmand.core.samples.android.sample1.SearchUIHelper.SearchRow; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class MainActivity extends Activity { + private static final String TAG = "OsmAndCoreSample"; + + private float displayDensityFactor; + private int referenceTileSize; + private int rasterTileSize; + private IMapStylesCollection mapStylesCollection; + private ResolvedMapStyle mapStyle; + private ObfsCollection obfsCollection; + private MapPresentationEnvironment mapPresentationEnvironment; + private MapPrimitiviser mapPrimitiviser; + private ObfMapObjectsProvider obfMapObjectsProvider; + private MapPrimitivesProvider mapPrimitivesProvider; + private MapObjectsSymbolsProvider mapObjectsSymbolsProvider; + private IMapLayerProvider mapLayerProvider0; + private IMapLayerProvider mapLayerProvider1; + private QIODeviceLogSink fileLogSink; + + private AtlasMapRendererView mapView; + private TextView textZoom; + private ImageButton azimuthNorthButton; + + private GestureDetector gestureDetector; + private PointI target31; + private float zoom; + private float azimuth; + private float elevationAngle; + private MultiTouchSupport multiTouchSupport; + + private SearchAPI searchAPI; + private ListView searchListView; + private SearchListAdapter adapter; + private final static int MAX_SEARCH_RESULTS = 50; + + // Germany + private final static float INIT_LAT = 49.353953f; + private final static float INIT_LON = 11.214384f; + // Kyiv + //private final static float INIT_LAT = 50.450117f; + //private final static float INIT_LON = 30.524142f; + private final static float INIT_ZOOM = 6.0f; + private final static float INIT_AZIMUTH = 0.0f; + private final static float INIT_ELEVATION_ANGLE = 90.0f; + private final static int MIN_ZOOM_LEVEL = 2; + private final static int MAX_ZOOM_LEVEL = 22; + + private static final String PREF_MAP_CENTER_LAT = "MAP_CENTER_LAT"; + private static final String PREF_MAP_CENTER_LON = "MAP_CENTER_LON"; + private static final String PREF_MAP_AZIMUTH = "MAP_AZIMUTH"; + private static final String PREF_MAP_ZOOM = "MAP_ZOOM"; + private static final String PREF_MAP_ELEVATION_ANGLE = "MAP_ELEVATION_ANGLE"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + gestureDetector = new GestureDetector(this, new MapViewOnGestureListener()); + multiTouchSupport = new MultiTouchSupport(this, new MapViewMultiTouchZoomListener()); + + long startTime = System.currentTimeMillis(); + + // Initialize native core prior (if needed) + if (NativeCore.isAvailable() && !NativeCore.isLoaded()) + NativeCore.load(CoreResourcesFromAndroidAssets.loadFromCurrentApplication(this)); + + Logger.get().setSeverityLevelThreshold(LogSeverityLevel.Debug); + + // Inflate views + setContentView(R.layout.activity_main); + + // Get map view + mapView = (AtlasMapRendererView) findViewById(R.id.mapRendererView); + + textZoom = (TextView) findViewById(R.id.text_zoom); + azimuthNorthButton = (ImageButton) findViewById(R.id.map_azimuth_north_button); + + azimuthNorthButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setAzimuth(0f); + } + }); + + findViewById(R.id.map_zoom_in_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setZoom(zoom + 1f); + } + }); + + findViewById(R.id.map_zoom_out_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setZoom(zoom - 1f); + } + }); + + // Additional log sink + fileLogSink = QIODeviceLogSink.createFileLogSink( + Environment.getExternalStorageDirectory() + "/osmand/osmandcore.log"); + Logger.get().addLogSink(fileLogSink); + + // Get device display density factor + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + displayDensityFactor = displayMetrics.densityDpi / 160.0f; + referenceTileSize = (int)(256 * displayDensityFactor); + rasterTileSize = Integer.highestOneBit(referenceTileSize - 1) * 2; + Log.i(TAG, "displayDensityFactor = " + displayDensityFactor); + Log.i(TAG, "referenceTileSize = " + referenceTileSize); + Log.i(TAG, "rasterTileSize = " + rasterTileSize); + + Log.i(TAG, "Going to resolve default embedded style..."); + mapStylesCollection = new MapStylesCollection(); + mapStyle = mapStylesCollection.getResolvedStyleByName("default"); + if (mapStyle == null) + { + Log.e(TAG, "Failed to resolve style 'default'"); + System.exit(0); + } + + Log.i(TAG, "Going to prepare OBFs collection"); + obfsCollection = new ObfsCollection(); + Log.i(TAG, "Will load OBFs from " + Environment.getExternalStorageDirectory() + "/osmand"); + obfsCollection.addDirectory(Environment.getExternalStorageDirectory() + "/osmand", false); + + Log.i(TAG, "Going to prepare all resources for renderer"); + mapPresentationEnvironment = new MapPresentationEnvironment( + mapStyle, + displayDensityFactor, + 1.0f, + 1.0f, + MapUtils.LANGUAGE); + //mapPresentationEnvironment->setSettings(configuration.styleSettings); + mapPrimitiviser = new MapPrimitiviser( + mapPresentationEnvironment); + obfMapObjectsProvider = new ObfMapObjectsProvider( + obfsCollection); + mapPrimitivesProvider = new MapPrimitivesProvider( + obfMapObjectsProvider, + mapPrimitiviser, + rasterTileSize); + mapObjectsSymbolsProvider = new MapObjectsSymbolsProvider( + mapPrimitivesProvider, + rasterTileSize); + + mapView.setReferenceTileSizeOnScreenInPixels(referenceTileSize); + mapView.addSymbolsProvider(mapObjectsSymbolsProvider); + + restoreMapState(); + + mapLayerProvider0 = new MapRasterLayerProvider_Software(mapPrimitivesProvider); + mapView.setMapLayerProvider(0, mapLayerProvider0); + + System.out.println("NATIVE_INITIALIZED = " + (System.currentTimeMillis() - startTime) / 1000f); + + //Setup search + + searchAPI = new SearchAPI(obfsCollection); + + final EditText searchEditText = (EditText) findViewById(R.id.searchEditText); + searchEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + if (s.length() > 2) { + runSearch(getScreenBounds31(), s.toString()); + } + } + }); + searchEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus && adapter.getCount() > 0 && isSearchListHidden()) { + showSearchList(); + LatLon latLon = Utilities.convert31ToLatLon(target31); + adapter.updateDistance(latLon.getLatitude(), latLon.getLongitude()); + adapter.notifyDataSetChanged(); + } + } + }); + + ImageButton clearButton = (ImageButton) findViewById(R.id.clearButton); + clearButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + searchEditText.setText(""); + adapter.clear(); + adapter.notifyDataSetChanged(); + hideSearchList(); + } + }); + + searchListView = (ListView) findViewById(android.R.id.list); + adapter = new SearchListAdapter(this); + searchListView.setAdapter(adapter); + searchListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + hideSearchList(); + mapView.requestFocus(); + SearchRow item = adapter.getItem(position); + PointI target = Utilities.convertLatLonTo31(new LatLon(item.getLatitude(), item.getLongitude())); + setTarget(target); + setZoom(17f); + } + }); + + } + + @Override + protected void onResume() { + super.onResume(); + + mapView.handleOnResume(); + } + + @Override + protected void onPause() { + saveMapState(); + mapView.handleOnPause(); + + super.onPause(); + } + + @Override + protected void onDestroy() { + mapView.handleOnDestroy(); + + super.onDestroy(); + } + + private AreaI getScreenBounds31() { + PointI topLeftPoint = new PointI(); + PointI bottomRightPoint = new PointI(); + mapView.getLocationFromScreenPoint(new PointI(0, 0), topLeftPoint); + mapView.getLocationFromScreenPoint(new PointI(mapView.getWidth(), mapView.getHeight()), bottomRightPoint); + return new AreaI(topLeftPoint, bottomRightPoint); + } + + private boolean isSearchListHidden() { + return searchListView.getVisibility() != View.VISIBLE; + } + + private void showSearchList() { + if (isSearchListHidden()) { + ViewCompat.setAlpha(searchListView, 0f); + searchListView.setVisibility(View.VISIBLE); + ViewCompat.animate(searchListView).alpha(1f).setListener(null); + } + } + + private void hideSearchList() { + ViewCompat.animate(searchListView).alpha(0f).setListener(new ViewPropertyAnimatorListener() { + @Override + public void onAnimationStart(View view) { + + } + + @Override + public void onAnimationEnd(View view) { + searchListView.setVisibility(View.GONE); + } + + @Override + public void onAnimationCancel(View view) { + searchListView.setVisibility(View.GONE); + } + }); + } + + public void saveMapState() { + SharedPreferences prefs = getPreferences(MODE_PRIVATE); + Editor edit = prefs.edit(); + LatLon latLon = Utilities.convert31ToLatLon(target31); + edit.putFloat(PREF_MAP_CENTER_LAT, (float)latLon.getLatitude()); + edit.putFloat(PREF_MAP_CENTER_LON, (float)latLon.getLongitude()); + edit.putFloat(PREF_MAP_AZIMUTH, azimuth); + edit.putFloat(PREF_MAP_ZOOM, zoom); + edit.putFloat(PREF_MAP_ELEVATION_ANGLE, elevationAngle); + edit.commit(); + } + + public void restoreMapState() { + SharedPreferences prefs = getPreferences(MODE_PRIVATE); + float prefLat = prefs.getFloat(PREF_MAP_CENTER_LAT, INIT_LAT); + float prefLon = prefs.getFloat(PREF_MAP_CENTER_LON, INIT_LON); + float prefAzimuth = prefs.getFloat(PREF_MAP_AZIMUTH, INIT_AZIMUTH); + float prefZoom = prefs.getFloat(PREF_MAP_ZOOM, INIT_ZOOM); + float prefElevationAngle = prefs.getFloat(PREF_MAP_ELEVATION_ANGLE, INIT_ELEVATION_ANGLE); + + setAzimuth(prefAzimuth); + setElevationAngle(prefElevationAngle); + setTarget(Utilities.convertLatLonTo31(new LatLon(prefLat, prefLon))); + setZoom(prefZoom); + } + + public boolean setTarget(PointI pointI) { + target31 = pointI; + return mapView.setTarget(pointI); + } + + @SuppressLint("DefaultLocale") + public boolean setZoom(float zoom) { + if (zoom < MIN_ZOOM_LEVEL) { + zoom = MIN_ZOOM_LEVEL; + } else if (zoom > MAX_ZOOM_LEVEL) { + zoom = MAX_ZOOM_LEVEL; + } + this.zoom = zoom; + textZoom.setText(String.format("%.0f", zoom)); + return mapView.setZoom(zoom); + } + + public void setAzimuth(float angle) { + angle = MapUtils.unifyRotationTo360(angle); + this.azimuth = angle; + mapView.setAzimuth(angle); + + if (angle == 0f && azimuthNorthButton.getVisibility() == View.VISIBLE) { + azimuthNorthButton.setVisibility(View.INVISIBLE); + } else if (angle != 0f && azimuthNorthButton.getVisibility() == View.INVISIBLE) { + azimuthNorthButton.setVisibility(View.VISIBLE); + } + } + + public void setElevationAngle(float angle) { + if (angle < 35f) { + angle = 35f; + } else if (angle > 90f) { + angle = 90f; + } + this.elevationAngle = angle; + mapView.setElevationAngle(angle); + } + + public boolean onTouchEvent(MotionEvent event) { + return multiTouchSupport.onTouchEvent(event) + || gestureDetector.onTouchEvent(event); + } + + private void runSearch(AreaI bounds31, String keyword) { + + searchAPI.setObfAreaFilter(bounds31); + searchAPI.startSearch(keyword, MAX_SEARCH_RESULTS, new SearchAPICallback() { + @Override + public void onSearchFinished(List searchItems, boolean cancelled) { + if (searchItems != null && !cancelled) { + LatLon latLon = Utilities.convert31ToLatLon(target31); + List rows = new ArrayList<>(); + for (SearchItem item : searchItems) { + SearchRow row = new SearchRow(item); + rows.add(row); + } + + adapter.clear(); + adapter.addAll(rows); + adapter.updateDistance(latLon.getLatitude(), latLon.getLongitude()); + adapter.sort(new Comparator() { + @Override + public int compare(SearchRow lhs, SearchRow rhs) { + int res = Double.compare(lhs.getDistance(), rhs.getDistance()); + if (res == 0) { + return lhs.getSearchItem().getLocalizedName().compareToIgnoreCase(rhs.getSearchItem().getLocalizedName()); + } else { + return res; + } + } + }); + adapter.notifyDataSetChanged(); + if (adapter.getCount() > 0) { + searchListView.setSelection(0); + } + + showSearchList(); + } + } + }); + } + + private class MapViewOnGestureListener extends SimpleOnGestureListener { + + @Override + public boolean onSingleTapUp(MotionEvent e) { + mapView.requestFocus(); + return true; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + float fromX = e2.getX() + distanceX; + float fromY = e2.getY() + distanceY; + float toX = e2.getX(); + float toY = e2.getY(); + + float dx = (fromX - toX); + float dy = (fromY - toY); + + PointI newTarget = new PointI(); + mapView.getLocationFromScreenPoint(new PointI(mapView.getWidth() / 2 + (int)dx, mapView.getHeight() / 2 + (int)dy), newTarget); + + setTarget(newTarget); + + mapView.requestFocus(); + return true; + } + } + + private class MapViewMultiTouchZoomListener implements MultiTouchZoomListener { + + private float initialZoom; + private float initialAzimuth; + private float initialElevation; + private PointF centerPoint; + + @Override + public void onGestureFinished(float scale, float rotation) { + } + + @Override + public void onGestureInit(float x1, float y1, float x2, float y2) { + } + + @Override + public void onZoomStarted(PointF centerPoint) { + initialZoom = zoom; + initialAzimuth = azimuth; + this.centerPoint = centerPoint; + } + + @Override + public void onZoomingOrRotating(float scale, float rotation) { + + PointI centerLocationBefore = new PointI(); + mapView.getLocationFromScreenPoint( + new PointI((int)centerPoint.x, (int)centerPoint.y), centerLocationBefore); + + // Change zoom + setZoom(initialZoom + (float)(Math.log(scale) / Math.log(2))); + + // Adjust current target position to keep touch center the same + PointI centerLocationAfter = new PointI(); + mapView.getLocationFromScreenPoint( + new PointI((int)centerPoint.x, (int)centerPoint.y), centerLocationAfter); + PointI centerLocationDelta = new PointI( + centerLocationAfter.getX() - centerLocationBefore.getX(), + centerLocationAfter.getY() - centerLocationBefore.getY()); + + setTarget(new PointI(target31.getX() - centerLocationDelta.getX(), target31.getY() - centerLocationDelta.getY())); + + /* + // Convert point from screen to location + PointI centerLocation = new PointI(); + mapView.getLocationFromScreenPoint( + new PointI((int)centerPoint.x, (int)centerPoint.y), centerLocation); + + // Rotate current target around center location + PointI target = new PointI(xI - centerLocation.getX(), yI - centerLocation.getY()); + double cosAngle = Math.cos(-Math.toRadians(rotation)); + double sinAngle = Math.sin(-Math.toRadians(rotation)); + + PointI newTarget = new PointI( + (int)(target.getX() * cosAngle - target.getY() * sinAngle + centerLocation.getX()), + (int)(target.getX() * sinAngle + target.getY() * cosAngle + centerLocation.getY())); + + setTarget(newTarget); + */ + + // Set rotation + setAzimuth(initialAzimuth - rotation); + } + + @Override + public void onChangeViewAngleStarted() { + initialElevation = elevationAngle; + } + + @Override + public void onChangingViewAngle(float angle) { + setElevationAngle(initialElevation - angle); + } + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MapUtils.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MapUtils.java new file mode 100644 index 0000000000..eac4454d46 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MapUtils.java @@ -0,0 +1,30 @@ +package net.osmand.core.samples.android.sample1; + +import java.util.Locale; + +public class MapUtils { + + public static final String LANGUAGE; + + static { + LANGUAGE = getLanguage(); + } + + public static float unifyRotationTo360(float rotate) { + while (rotate < -180) { + rotate += 360; + } + while (rotate > +180) { + rotate -= 360; + } + return rotate; + } + + private static String getLanguage() { + String langCode = Locale.getDefault().getLanguage(); + if (langCode.isEmpty()) { + langCode = "en"; + } + return langCode; + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MultiTouchSupport.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MultiTouchSupport.java new file mode 100644 index 0000000000..9f0dbd350b --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MultiTouchSupport.java @@ -0,0 +1,202 @@ +package net.osmand.core.samples.android.sample1; + +import android.content.Context; +import android.graphics.PointF; +import android.util.Log; +import android.view.MotionEvent; + +import java.lang.reflect.Method; + +public class MultiTouchSupport { + + private static final String TAG = "MultiTouchSupport"; + + public static final int ACTION_MASK = 255; + protected final Context ctx; + private final MultiTouchZoomListener listener; + protected Method getPointerCount; + protected Method getX; + protected Method getY; + protected Method getPointerId; + + private float initialAngle; + private float rotation; + private static final float ROTATION_THRESHOLD_DEG = 15.0f; + private boolean isRotating; + + private boolean multiTouchAPISupported = false; + + private boolean inTiltMode = false; + private PointF firstFingerStart = new PointF(); + private PointF secondFingerStart = new PointF(); + private static final int TILT_X_THRESHOLD_PX = 40; + private static final int TILT_Y_THRESHOLD_PX = 40; + private static final int TILT_DY_THRESHOLD_PX = 40; + + private boolean inZoomMode = false; + private float initialDistance = 100; + private float scale = 1; + private PointF centerPoint = new PointF(); + + private boolean multiTouch; + + public MultiTouchSupport(Context ctx, MultiTouchZoomListener listener) { + this.ctx = ctx; + this.listener = listener; + initMethods(); + } + + public boolean isMultiTouchSupported(){ + return multiTouchAPISupported; + } + + public boolean isInZoomMode(){ + return inZoomMode; + } + + public boolean isInTiltMode() { + return inTiltMode; + } + + private void initMethods() { + try { + getPointerCount = MotionEvent.class.getMethod("getPointerCount"); //$NON-NLS-1$ + getPointerId = MotionEvent.class.getMethod("getPointerId", Integer.TYPE); //$NON-NLS-1$ + getX = MotionEvent.class.getMethod("getX", Integer.TYPE); //$NON-NLS-1$ + getY = MotionEvent.class.getMethod("getY", Integer.TYPE); //$NON-NLS-1$ + multiTouchAPISupported = true; + } catch (Exception e) { + multiTouchAPISupported = false; + } + } + + public boolean onTouchEvent(MotionEvent event){ + if(!isMultiTouchSupported()){ + return false; + } + int actionCode = event.getAction() & ACTION_MASK; + try { + if (actionCode == MotionEvent.ACTION_UP || actionCode == MotionEvent.ACTION_CANCEL) { + multiTouch = false; + } + Integer pointCount = (Integer) getPointerCount.invoke(event); + if (pointCount < 2) { + if (inZoomMode || inTiltMode) { + listener.onGestureFinished(scale, rotation); + inZoomMode = false; + inTiltMode = false; + } + return multiTouch; + } else { + multiTouch = true; + } + + Float x1 = (Float) getX.invoke(event, 0); + Float x2 = (Float) getX.invoke(event, 1); + Float y1 = (Float) getY.invoke(event, 0); + Float y2 = (Float) getY.invoke(event, 1); + float distance = (float) Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + float angle = 0; + boolean angleDefined = false; + if (x1.floatValue() != x2.floatValue() || y1.floatValue() != y2.floatValue()) { + angleDefined = true; + angle = (float) (Math.atan2(y2 - y1, x2 -x1) * 180 / Math.PI); + } + + switch (actionCode) { + + case MotionEvent.ACTION_POINTER_DOWN: { + + centerPoint = new PointF((x1 + x2) / 2, (y1 + y2) / 2); + firstFingerStart = new PointF(x1, y1); + secondFingerStart = new PointF(x2, y2); + listener.onGestureInit(x1, y1, x2, y2); + return true; + } + case MotionEvent.ACTION_POINTER_UP: { + + if (inZoomMode || inTiltMode) { + listener.onGestureFinished(scale, rotation); + inZoomMode = false; + inTiltMode = false; + } + return true; + } + case MotionEvent.ACTION_MOVE: { + + if (inZoomMode) { + if (angleDefined) { + float a = MapUtils.unifyRotationTo360(angle - initialAngle); + if (!isRotating && Math.abs(a) > ROTATION_THRESHOLD_DEG) { + isRotating = true; + initialAngle = angle; + } else if (isRotating) { + rotation = a; + } + } + scale = distance / initialDistance; + + listener.onZoomingOrRotating(scale, rotation); + return true; + + } else if (inTiltMode) { + float dy2 = secondFingerStart.y - y2; + float viewAngle = dy2 / 8f; + listener.onChangingViewAngle(viewAngle); + + } else { + float dx1 = Math.abs(firstFingerStart.x - x1); + float dx2 = Math.abs(secondFingerStart.x - x2); + float dy1 = Math.abs(firstFingerStart.y - y1); + float dy2 = Math.abs(secondFingerStart.y - y2); + float startDy = Math.abs(secondFingerStart.y - firstFingerStart.y); + if (dx1 < TILT_X_THRESHOLD_PX && dx2 < TILT_X_THRESHOLD_PX + && dy1 > TILT_Y_THRESHOLD_PX && dy2 > TILT_Y_THRESHOLD_PX + && startDy < TILT_Y_THRESHOLD_PX * 6 + && Math.abs(dy2 - dy1) < TILT_DY_THRESHOLD_PX) { + listener.onChangeViewAngleStarted(); + inTiltMode = true; + + } else if (dx1 > TILT_X_THRESHOLD_PX || dx2 > TILT_X_THRESHOLD_PX + || Math.abs(dy2 - dy1) > TILT_DY_THRESHOLD_PX + || Math.abs(dy1 - dy2) > TILT_DY_THRESHOLD_PX) { + listener.onZoomStarted(centerPoint); + initialDistance = distance; + initialAngle = angle; + rotation = 0; + scale = 0; + isRotating = false; + inZoomMode = true; + } + } + } + default: + break; + } + + } catch (Exception e) { + Log.e(TAG, "Multi touch exception" , e); //$NON-NLS-1$ + } + return true; + } + + public PointF getCenterPoint() { + return centerPoint; + } + + public interface MultiTouchZoomListener { + + void onZoomStarted(PointF centerPoint); + + void onZoomingOrRotating(float scale, float rotation); + + void onChangeViewAngleStarted(); + + void onChangingViewAngle(float angle); + + void onGestureFinished(float scale, float rotation); + + void onGestureInit(float x1, float y1, float x2, float y2); + + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SearchAPI.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SearchAPI.java new file mode 100644 index 0000000000..b00b677a90 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SearchAPI.java @@ -0,0 +1,306 @@ +package net.osmand.core.samples.android.sample1; + +import android.os.AsyncTask; + +import net.osmand.core.jni.AmenitiesByNameSearch; +import net.osmand.core.jni.Amenity; +import net.osmand.core.jni.Amenity.DecodedCategory; +import net.osmand.core.jni.AreaI; +import net.osmand.core.jni.DecodedCategoryList; +import net.osmand.core.jni.IObfsCollection; +import net.osmand.core.jni.IQueryController; +import net.osmand.core.jni.ISearch; +import net.osmand.core.jni.LatLon; +import net.osmand.core.jni.NullableAreaI; +import net.osmand.core.jni.ObfInfo; +import net.osmand.core.jni.ObfsCollection; +import net.osmand.core.jni.PointI; +import net.osmand.core.jni.QStringStringHash; +import net.osmand.core.jni.Utilities; + +import java.util.ArrayList; +import java.util.List; + +public class SearchAPI { + + private ObfsCollection obfsCollection; + private AreaI searchableArea; + private AreaI obfAreaFilter; + private SearchRequestExecutor executor; + + interface SearchAPICallback { + void onSearchFinished(List searchItems, boolean cancelled); + } + + public SearchAPI(ObfsCollection obfsCollection) { + this.obfsCollection = obfsCollection; + executor = new SearchRequestExecutor(); + } + + public AreaI getSearchableArea() { + return searchableArea; + } + + public void setSearchableArea(AreaI searchableArea) { + this.searchableArea = searchableArea; + } + + public AreaI getObfAreaFilter() { + return obfAreaFilter; + } + + public void setObfAreaFilter(AreaI obfAreaFilter) { + this.obfAreaFilter = obfAreaFilter; + } + + public void startSearch(String keyword, int maxSearchResults, SearchAPICallback apiCallback) { + executor.run(new SearchRequest(keyword, maxSearchResults, apiCallback), true); + } + + public void cancelSearch() { + executor.cancel(); + } + + + public class SearchRequestExecutor { + + private SearchRequest ongoingSearchRequest; + private SearchRequest nextSearchRequest; + + public void run(SearchRequest searchRequest, boolean cancelCurrentRequest) { + if (ongoingSearchRequest != null) { + nextSearchRequest = searchRequest; + if (cancelCurrentRequest) { + ongoingSearchRequest.cancel(); + } + } else { + ongoingSearchRequest = searchRequest; + nextSearchRequest = null; + searchRequest.setOnFinishedCallback(new Runnable() { + @Override + public void run() { + operationFinished(); + } + }); + searchRequest.run(); + } + } + + public void cancel() { + if (nextSearchRequest != null) { + nextSearchRequest = null; + } + if (ongoingSearchRequest != null) { + ongoingSearchRequest.cancel(); + } + } + + private void operationFinished() { + ongoingSearchRequest = null; + if (nextSearchRequest != null) { + run(nextSearchRequest, false); + } + } + } + + public class SearchRequest { + private String keyword; + private int maxSearchResults; + private Runnable onFinished; + private SearchAPICallback apiCallback; + + private boolean cancelled; + private int resCount; + + public SearchRequest(String keyword, int maxSearchResults, SearchAPICallback apiCallback) { + this.keyword = keyword; + this.maxSearchResults = maxSearchResults; + this.apiCallback = apiCallback; + } + + public void run() { + + new AsyncTask>() { + @Override + protected List doInBackground(String... params) { + return doSearch(params[0]); + } + + @Override + protected void onPostExecute(List searchItems) { + + if (onFinished != null) { + onFinished.run(); + } + + if (apiCallback != null) { + apiCallback.onSearchFinished(searchItems, cancelled); + } + } + }.execute(keyword); + } + + private List doSearch(String keyword) { + System.out.println("=== Start search"); + resCount = 0; + + final List searchItems = new ArrayList<>(); + + AmenitiesByNameSearch byNameSearch = new AmenitiesByNameSearch(obfsCollection); + AmenitiesByNameSearch.Criteria criteria = new AmenitiesByNameSearch.Criteria(); + criteria.setName(keyword); + if (obfAreaFilter != null) { + criteria.setObfInfoAreaFilter(new NullableAreaI(new AreaI(obfAreaFilter))); + } + + ISearch.INewResultEntryCallback newResultEntryCallback = new ISearch.INewResultEntryCallback() { + @Override + public void method(ISearch.Criteria criteria, ISearch.IResultEntry resultEntry) { + Amenity amenity = new ResultEntry(resultEntry).getAmenity(); + searchItems.add(new AmenitySearchItem(amenity)); + System.out.println("Poi found === " + amenity.getNativeName()); + resCount++; + /* + QStringStringHash locNames = amenity.getLocalizedNames(); + if (locNames.size() > 0) { + QStringList keys = locNames.keys(); + StringBuilder sb = new StringBuilder("=== Localized names: "); + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + sb.append(key).append("=").append(locNames.get(key)).append(" | "); + } + System.out.println(sb.toString()); + } + */ + } + }; + + byNameSearch.performSearch(criteria, newResultEntryCallback.getBinding(), new IQueryController() { + @Override + public boolean isAborted() { + return resCount >= maxSearchResults || cancelled; + } + }); + + System.out.println("=== Finish search"); + + return searchItems; + } + + public void cancel() { + cancelled = true; + } + + public void setOnFinishedCallback(Runnable onFinished) { + this.onFinished = onFinished; + } + } + + private static class ResultEntry extends AmenitiesByNameSearch.ResultEntry { + protected ResultEntry(ISearch.IResultEntry resultEntry) { + super(ISearch.IResultEntry.getCPtr(resultEntry), false); + } + } + + public static abstract class SearchItem { + + protected double latitude; + protected double longitude; + + public SearchItem(double latitude, double longitude) { + this.latitude = latitude; + this.longitude = longitude; + } + + public SearchItem(PointI location31) { + LatLon latLon = Utilities.convert31ToLatLon(location31); + latitude = latLon.getLatitude(); + longitude = latLon.getLongitude(); + } + + public abstract String getNativeName(); + + public abstract String getLocalizedName(); + + public abstract String getTypeName(); + + public abstract String getSubTypeName(); + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + @Override + public String toString() { + return getNativeName() + " {lat:" + getLatitude() + " lon: " + getLongitude() + "}"; + } + } + + public static class AmenitySearchItem extends SearchItem { + + private String nativeName; + private String localizedName; + private String category; + private String subcategory; + + public AmenitySearchItem(Amenity amenity) { + super(amenity.getPosition31()); + + nativeName = amenity.getNativeName(); + QStringStringHash locNames = amenity.getLocalizedNames(); + if (locNames.has_key(MapUtils.LANGUAGE)) { + localizedName = locNames.get(MapUtils.LANGUAGE); + } + /* + if (locNames.size() > 0) { + QStringList keys = locNames.keys(); + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + localizedNamesMap.put(key, locNames.get(key)); + } + } + */ + + DecodedCategoryList catList = amenity.getDecodedCategories(); + if (catList.size() > 0) { + DecodedCategory decodedCategory = catList.get(0); + category = decodedCategory.getCategory(); + subcategory = decodedCategory.getSubcategory(); + } + } + + @Override + public String getNativeName() { + return nativeName; + } + + @Override + public String getLocalizedName() { + return localizedName != null ? localizedName : nativeName; + } + + @Override + public String getTypeName() { + return category; + } + + @Override + public String getSubTypeName() { + return subcategory; + } + + @Override + public double getLatitude() { + return latitude; + } + + @Override + public double getLongitude() { + return longitude; + } + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SearchUIHelper.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SearchUIHelper.java new file mode 100644 index 0000000000..ff82491271 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/SearchUIHelper.java @@ -0,0 +1,139 @@ +package net.osmand.core.samples.android.sample1; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.osmand.core.jni.Utilities; +import net.osmand.core.samples.android.sample1.SearchAPI.SearchItem; + +import java.text.MessageFormat; + +public class SearchUIHelper { + + public static Drawable getIcon(Context ctx, SearchItem item) { + return null; + } + + public static class SearchRow { + + private SearchItem searchItem; + private double distance; + + public SearchRow(SearchItem searchItem) { + this.searchItem = searchItem; + } + + public SearchItem getSearchItem() { + return searchItem; + } + + public double getLatitude() { + return searchItem.getLatitude(); + } + + public double getLongitude() { + return searchItem.getLongitude(); + } + + public double getDistance() { + return distance; + } + + public void setDistance(double distance) { + this.distance = distance; + } + } + + public static class SearchListAdapter extends ArrayAdapter { + + private Context ctx; + + public SearchListAdapter(Context ctx) { + super(ctx, R.layout.search_list_item); + this.ctx = ctx; + } + + public void updateDistance(double latitude, double longitude) { + for (int i = 0; i < getCount(); i++) { + SearchRow item = getItem(i); + item.setDistance(Utilities.distance( + longitude, latitude, + item.getLongitude(), item.getLatitude())); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + SearchRow item = getItem(position); + + LinearLayout view; + if (convertView == null) { + LayoutInflater inflater = (LayoutInflater) ctx + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = (LinearLayout) inflater.inflate( + R.layout.search_list_item, null); + } else { + view = (LinearLayout) convertView; + } + + TextView title = (TextView) view.findViewById(R.id.title); + TextView subtitle = (TextView) view.findViewById(R.id.subtitle); + TextView distance = (TextView) view.findViewById(R.id.distance); + title.setText(item.searchItem.getLocalizedName()); + StringBuilder sb = new StringBuilder(); + if (!item.searchItem.getSubTypeName().isEmpty()) { + sb.append(getNiceString(item.searchItem.getSubTypeName())); + } + if (!item.searchItem.getTypeName().isEmpty()) { + if (sb.length() > 0) { + sb.append(" — "); + } + sb.append(getNiceString(item.searchItem.getTypeName())); + } + subtitle.setText(sb.toString()); + if (item.getDistance() == 0) { + distance.setText(""); + } else { + distance.setText(getFormattedDistance(item.getDistance())); + } + + //text1.setTextColor(ctx.getResources().getColor(R.color.listTextColor)); + //view.setCompoundDrawablesWithIntrinsicBounds(getIcon(ctx, item), null, null, null); + //view.setCompoundDrawablePadding(ctx.getResources().getDimensionPixelSize(R.dimen.list_content_padding)); + return view; + } + } + + public static String capitalizeFirstLetterAndLowercase(String s) { + if (s != null && s.length() > 1) { + // not very efficient algorithm + return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase(); + } else { + return s; + } + } + + public static String getNiceString(String s) { + return capitalizeFirstLetterAndLowercase(s.replaceAll("_", " ")); + } + + public static String getFormattedDistance(double meters) { + double mainUnitInMeters = 1000; + String mainUnitStr = "km"; + if (meters >= 100 * mainUnitInMeters) { + return (int) (meters / mainUnitInMeters + 0.5) + " " + mainUnitStr; + } else if (meters > 9.99f * mainUnitInMeters) { + return MessageFormat.format("{0,number,#.#} " + mainUnitStr, ((float) meters) / mainUnitInMeters).replace('\n', ' '); + } else if (meters > 0.999f * mainUnitInMeters) { + return MessageFormat.format("{0,number,#.##} " + mainUnitStr, ((float) meters) / mainUnitInMeters).replace('\n', ' '); + } else { + return ((int) (meters + 0.5)) + " m"; + } + } +} diff --git a/settings.gradle b/settings.gradle index 4c604d0ff3..234ab0eb96 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -include ':OsmAnd-java' +include ':OsmAnd-java', ':OsmAndCore-sample' include ':OsmAnd' include ':plugins:OsmAnd-AddressPlugin' include ':plugins:Osmand-ParkingPlugin'