diff --git a/OsmAnd-java/src/main/java/net/osmand/search/SearchUICore.java b/OsmAnd-java/src/main/java/net/osmand/search/SearchUICore.java index c8449cc3c1..0e9d1b1fa7 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/SearchUICore.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/SearchUICore.java @@ -401,6 +401,10 @@ public class SearchUICore { } public void search(final String text, final boolean delayedExecution, final ResultMatcher matcher) { + search(text, delayedExecution, matcher, searchSettings); + } + + public void search(final String text, final boolean delayedExecution, final ResultMatcher matcher, final SearchSettings searchSettings) { final int request = requestNumber.incrementAndGet(); final SearchPhrase phrase = this.phrase.generateNewPhrase(text, searchSettings); this.phrase = phrase; diff --git a/OsmAnd/src/net/osmand/aidl/IOsmAndAidlCallback.aidl b/OsmAnd/src/net/osmand/aidl/IOsmAndAidlCallback.aidl new file mode 100644 index 0000000000..be9e753336 --- /dev/null +++ b/OsmAnd/src/net/osmand/aidl/IOsmAndAidlCallback.aidl @@ -0,0 +1,7 @@ +package net.osmand.aidl; + +import net.osmand.aidl.search.SearchResult; + +interface IOsmAndAidlCallback { + void onSearchComplete(in List resultSet); +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/aidl/IOsmAndAidlInterface.aidl b/OsmAnd/src/net/osmand/aidl/IOsmAndAidlInterface.aidl index d752aa9199..22efaa72dd 100644 --- a/OsmAnd/src/net/osmand/aidl/IOsmAndAidlInterface.aidl +++ b/OsmAnd/src/net/osmand/aidl/IOsmAndAidlInterface.aidl @@ -61,6 +61,11 @@ import net.osmand.aidl.navigation.StopNavigationParams; import net.osmand.aidl.navigation.MuteNavigationParams; import net.osmand.aidl.navigation.UnmuteNavigationParams; +import net.osmand.aidl.IOsmAndAidlCallback; + +import net.osmand.aidl.search.SearchResult; +import net.osmand.aidl.search.SearchParams; + // NOTE: Add new methods at the end of file!!! interface IOsmAndAidlInterface { @@ -121,4 +126,6 @@ interface IOsmAndAidlInterface { boolean stopNavigation(in StopNavigationParams params); boolean muteNavigation(in MuteNavigationParams params); boolean unmuteNavigation(in UnmuteNavigationParams params); + + boolean search(in SearchParams params, IOsmAndAidlCallback callback); } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java index 0e50d9e73f..c2b6d4cb13 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java @@ -1,5 +1,6 @@ package net.osmand.aidl; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; @@ -28,9 +29,11 @@ import net.osmand.aidl.maplayer.AMapLayer; import net.osmand.aidl.maplayer.point.AMapPoint; import net.osmand.aidl.mapmarker.AMapMarker; import net.osmand.aidl.mapwidget.AMapWidget; +import net.osmand.aidl.search.SearchResult; import net.osmand.data.FavouritePoint; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; +import net.osmand.plus.AppInitializer; import net.osmand.plus.ApplicationMode; import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; @@ -58,6 +61,9 @@ import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.mapwidgets.MapWidgetRegistry.MapWidgetRegInfo; import net.osmand.plus.views.mapwidgets.TextInfoWidget; +import net.osmand.search.SearchUICore; +import net.osmand.search.SearchUICore.SearchResultCollection; +import net.osmand.search.core.SearchSettings; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -81,6 +87,15 @@ import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; +import static net.osmand.search.core.ObjectType.CITY; +import static net.osmand.search.core.ObjectType.HOUSE; +import static net.osmand.search.core.ObjectType.POI; +import static net.osmand.search.core.ObjectType.POSTCODE; +import static net.osmand.search.core.ObjectType.STREET; +import static net.osmand.search.core.ObjectType.STREET_INTERSECTION; +import static net.osmand.search.core.ObjectType.VILLAGE; +import static net.osmand.search.core.SearchCoreFactory.MAX_DEFAULT_SEARCH_RADIUS; + public class OsmandAidlApi { private static final Log LOG = PlatformUtil.getLog(OsmandAidlApi.class); @@ -140,7 +155,7 @@ public class OsmandAidlApi { private Map widgetControls = new ConcurrentHashMap<>(); private Map layers = new ConcurrentHashMap<>(); private Map mapLayers = new ConcurrentHashMap<>(); - private Map receivers = new TreeMap(); + private Map receivers = new TreeMap<>(); public OsmandAidlApi(OsmandApplication app) { @@ -178,7 +193,7 @@ public class OsmandAidlApi { LOG.error(e.getMessage(), e); } } - receivers = new TreeMap(); + receivers = new TreeMap<>(); } private void registerRefreshMapReceiver(final MapActivity mapActivity) { @@ -972,6 +987,7 @@ public class OsmandAidlApi { return false; } + @SuppressLint("StaticFieldLeak") private void finishGpxImport(boolean destinationExists, File destination, String color, boolean show) { int col = ConfigureMapMenu.GpxAppearanceAdapter.parseTrackColor( app.getRendererRegistry().getCurrentSelectedRenderer(), color); @@ -1113,6 +1129,7 @@ public class OsmandAidlApi { return false; } + @SuppressLint("StaticFieldLeak") boolean showGpx(String fileName) { if (!Algorithms.isEmpty(fileName)) { File f = app.getAppPath(IndexConstants.GPX_INDEX_DIR + fileName); @@ -1310,6 +1327,63 @@ public class OsmandAidlApi { return true; } + boolean search(final String searchQuery, final double latitude, final double longitude, + final int radiusLevel, final int totalLimit, final SearchCompleteCallback callback) { + if (Algorithms.isEmpty(searchQuery) || latitude == 0 || longitude == 0 || callback == null) { + return false; + } + if (app.isApplicationInitializing()) { + app.getAppInitializer().addListener(new AppInitializer.AppInitializeListener() { + @Override + public void onProgress(AppInitializer init, AppInitializer.InitEvents event) { + } + + @Override + public void onFinish(AppInitializer init) { + runSearch(searchQuery, latitude, longitude, radiusLevel, totalLimit, callback); + } + }); + } else { + runSearch(searchQuery, latitude, longitude, radiusLevel, totalLimit, callback); + } + return true; + } + + private void runSearch(String searchQuery, double latitude, double longitude, int radiusLevel, int totalLimit, final SearchCompleteCallback callback) { + final SearchUICore core = app.getSearchUICore().getCore(); + core.setOnResultsComplete(new Runnable() { + @Override + public void run() { + List resultSet = new ArrayList<>(); + SearchResultCollection resultCollection = core.getCurrentSearchResult(); + for (net.osmand.search.core.SearchResult r : resultCollection.getCurrentSearchResults()) { + SearchResult result = new SearchResult(r.location.getLatitude(), r.location.getLongitude(), r.localeName, r.alternateName, new ArrayList<>(r.otherNames)); + resultSet.add(result); + } + callback.onSearchComplete(resultSet); + } + }); + + if (radiusLevel < 1) { + radiusLevel = 1; + } else if (radiusLevel > MAX_DEFAULT_SEARCH_RADIUS) { + radiusLevel = MAX_DEFAULT_SEARCH_RADIUS; + } + if (totalLimit <= 0) { + totalLimit = -1; + } + + SearchSettings searchSettings = new SearchSettings(core.getSearchSettings()) + .setSearchTypes(CITY, VILLAGE, POSTCODE, STREET, HOUSE, STREET_INTERSECTION, POI) + .setRadiusLevel(radiusLevel) + .setEmptyQueryAllowed(false) + .setSortByName(false) + .setOriginalLocation(new LatLon(latitude, longitude)) + .setTotalLimit(totalLimit); + + core.search(searchQuery, false, null, searchSettings); + } + boolean setNavDrawerItems(String appPackage, List items) { if (!TextUtils.isEmpty(appPackage) && items != null) { if (items.isEmpty()) { @@ -1448,4 +1522,8 @@ public class OsmandAidlApi { this.flags = flags; } } + + public interface SearchCompleteCallback { + void onSearchComplete(List resultSet); + } } diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java index 10eb39dcee..4df536f43d 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java @@ -2,10 +2,17 @@ package net.osmand.aidl; import android.app.Service; import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Parcelable; import android.os.RemoteException; import net.osmand.PlatformUtil; +import net.osmand.aidl.OsmandAidlApi.SearchCompleteCallback; import net.osmand.aidl.calculateroute.CalculateRouteParams; import net.osmand.aidl.favorite.AddFavoriteParams; import net.osmand.aidl.favorite.RemoveFavoriteParams; @@ -46,17 +53,28 @@ import net.osmand.aidl.note.StartAudioRecordingParams; import net.osmand.aidl.note.StartVideoRecordingParams; import net.osmand.aidl.note.StopRecordingParams; import net.osmand.aidl.note.TakePhotoNoteParams; +import net.osmand.aidl.search.SearchParams; +import net.osmand.aidl.search.SearchResult; import net.osmand.plus.OsmandApplication; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class OsmandAidlService extends Service { private static final Log LOG = PlatformUtil.getLog(OsmandAidlService.class); + private static final int MSG_RUN_SEARCH = 53; + private static final String DATA_KEY_RESULT_SET = "resultSet"; + + private ArrayList mRemoteCallbacks; + private ServiceHandler mHandler = null; + HandlerThread mHandlerThread = new HandlerThread("OsmAndAidlServiceThread"); + OsmandApplication getApp() { return (OsmandApplication) getApplication(); } @@ -68,15 +86,25 @@ public class OsmandAidlService extends Service { @Override public IBinder onBind(Intent intent) { + // Handler Thread handling all call back methods + mHandlerThread.start(); + mHandler = new ServiceHandler(mHandlerThread.getLooper()); + // Return the interface return mBinder; } + @Override + public void onCreate() { + super.onCreate(); + + mRemoteCallbacks = new ArrayList<>(); + } + private final IOsmAndAidlInterface.Stub mBinder = new IOsmAndAidlInterface.Stub() { private void handleException(Exception e) { LOG.error("AIDL e.getMessage()", e); - } @Override @@ -523,5 +551,77 @@ public class OsmandAidlService extends Service { return false; } } + + @Override + public boolean search(SearchParams params, final IOsmAndAidlCallback callback) throws RemoteException { + try { + return params != null && getApi("search").search(params.getSearchQuery(), + params.getLatutude(), params.getLongitude(), params.getRadiusLevel(), params.getTotalLimit(), new SearchCompleteCallback() { + @Override + public void onSearchComplete(List resultSet) { + Bundle data = new Bundle(); + if (resultSet.size() > 0) { + data.putParcelableArrayList(DATA_KEY_RESULT_SET, new ArrayList<>(resultSet)); + } + sendMsgToHandler(callback, MSG_RUN_SEARCH, data); + } + }); + } catch (Exception e) { + handleException(e); + return false; + } + } }; + + /** + * Create handler message to be sent + */ + void sendMsgToHandler(IOsmAndAidlCallback callback, int flag, Bundle data) { + + mRemoteCallbacks.add(callback); + + Message message = mHandler.obtainMessage(); + message.arg1 = mRemoteCallbacks.size() - 1; + message.setData(data); + + message.what = flag; + mHandler.sendMessage(message); + } + + /** + * Handler class sending result in callback to respective + * application + */ + private class ServiceHandler extends Handler { + int callbackIndex = 0; + + ServiceHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + callbackIndex = msg.arg1; + + switch (msg.what) { + + case MSG_RUN_SEARCH: + + Bundle data = msg.getData(); + List resultSet; + if (data.containsKey(DATA_KEY_RESULT_SET)) { + resultSet = data.getParcelableArrayList(DATA_KEY_RESULT_SET); + } else { + resultSet = Collections.emptyList(); + } + + try { + mRemoteCallbacks.get(callbackIndex).onSearchComplete(resultSet); + } catch (RemoteException e) { + LOG.error("AIDL e.getMessage()", e); + } + break; + } + } + } } diff --git a/OsmAnd/src/net/osmand/aidl/search/SearchParams.aidl b/OsmAnd/src/net/osmand/aidl/search/SearchParams.aidl new file mode 100644 index 0000000000..dfda88c493 --- /dev/null +++ b/OsmAnd/src/net/osmand/aidl/search/SearchParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.search; + +parcelable SearchParams; \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/aidl/search/SearchParams.java b/OsmAnd/src/net/osmand/aidl/search/SearchParams.java new file mode 100644 index 0000000000..760291f980 --- /dev/null +++ b/OsmAnd/src/net/osmand/aidl/search/SearchParams.java @@ -0,0 +1,79 @@ +package net.osmand.aidl.search; + +import android.os.Parcel; +import android.os.Parcelable; + +public class SearchParams implements Parcelable { + + private String searchQuery; + private double latutude; + private double longitude; + private int radiusLevel = 1; + private int totalLimit = -1; + + public SearchParams(String searchQuery, double latutude, double longitude, int radiusLevel, int totalLimit) { + this.searchQuery = searchQuery; + this.latutude = latutude; + this.longitude = longitude; + this.radiusLevel = radiusLevel; + this.totalLimit = totalLimit; + } + + public SearchParams(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SearchParams createFromParcel(Parcel in) { + return new SearchParams(in); + } + + @Override + public SearchParams[] newArray(int size) { + return new SearchParams[size]; + } + }; + + public String getSearchQuery() { + return searchQuery; + } + + public double getLatutude() { + return latutude; + } + + public double getLongitude() { + return longitude; + } + + public int getRadiusLevel() { + return radiusLevel; + } + + public int getTotalLimit() { + return totalLimit; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(searchQuery); + out.writeDouble(latutude); + out.writeDouble(longitude); + out.writeInt(radiusLevel); + out.writeInt(totalLimit); + } + + private void readFromParcel(Parcel in) { + searchQuery = in.readString(); + latutude = in.readDouble(); + longitude = in.readDouble(); + radiusLevel = in.readInt(); + totalLimit = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/OsmAnd/src/net/osmand/aidl/search/SearchResult.aidl b/OsmAnd/src/net/osmand/aidl/search/SearchResult.aidl new file mode 100644 index 0000000000..caecc7721b --- /dev/null +++ b/OsmAnd/src/net/osmand/aidl/search/SearchResult.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.search; + +parcelable SearchResult; \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/aidl/search/SearchResult.java b/OsmAnd/src/net/osmand/aidl/search/SearchResult.java new file mode 100644 index 0000000000..3781240e12 --- /dev/null +++ b/OsmAnd/src/net/osmand/aidl/search/SearchResult.java @@ -0,0 +1,85 @@ +package net.osmand.aidl.search; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +public class SearchResult implements Parcelable { + + private double latitude; + private double longitude; + + private String localeName; + private String alternateName; + private List otherNames = new ArrayList<>(); + + public SearchResult(double latitude, double longitude, String localeName, String alternateName, List otherNames) { + this.latitude = latitude; + this.longitude = longitude; + this.localeName = localeName; + this.alternateName = alternateName; + if (otherNames != null) { + this.otherNames = otherNames; + } + } + + public SearchResult(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SearchResult createFromParcel(Parcel in) { + return new SearchResult(in); + } + + @Override + public SearchResult[] newArray(int size) { + return new SearchResult[size]; + } + }; + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + public String getLocaleName() { + return localeName; + } + + public String getAlternateName() { + return alternateName; + } + + public List getOtherNames() { + return otherNames; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeDouble(latitude); + out.writeDouble(longitude); + out.writeString(localeName); + out.writeString(alternateName); + out.writeStringList(otherNames); + } + + private void readFromParcel(Parcel in) { + latitude = in.readDouble(); + longitude = in.readDouble(); + localeName = in.readString(); + alternateName = in.readString(); + in.readStringList(otherNames); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/OsmAnd/src/net/osmand/plus/helpers/ExternalApiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/ExternalApiHelper.java index 59c370f6e5..2468520da4 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/ExternalApiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/ExternalApiHelper.java @@ -91,6 +91,7 @@ public class ExternalApiHelper { public static final String PARAM_START_LON = "start_lon"; public static final String PARAM_DEST_LAT = "dest_lat"; public static final String PARAM_DEST_LON = "dest_lon"; + public static final String PARAM_DEST_SEARCH_QUERY = "dest_search_query"; public static final String PARAM_PROFILE = "profile"; public static final String PARAM_VERSION = "version"; @@ -251,9 +252,20 @@ public class ExternalApiHelper { startDesc = null; } - double destLat = Double.parseDouble(uri.getQueryParameter(PARAM_DEST_LAT)); - double destLon = Double.parseDouble(uri.getQueryParameter(PARAM_DEST_LON)); - final LatLon dest = new LatLon(destLat, destLon); + String destSearchQuery = uri.getQueryParameter(PARAM_DEST_SEARCH_QUERY); + String destLatStr = uri.getQueryParameter(PARAM_DEST_LAT); + String destLonStr = uri.getQueryParameter(PARAM_DEST_LON); + final LatLon dest; + if (!Algorithms.isEmpty(destLatStr) && !Algorithms.isEmpty(destLonStr)) { + double destLat = Double.parseDouble(destLatStr); + double destLon = Double.parseDouble(destLonStr); + dest = new LatLon(destLat, destLon); + } else { + dest = null; + } + if (!Algorithms.isEmpty(destSearchQuery)) { + + } final PointDescription destDesc = new PointDescription(PointDescription.POINT_TYPE_LOCATION, destName); boolean force = uri.getBooleanQueryParameter(PARAM_FORCE, false);