diff --git a/android/jni/com/mapswithme/maps/SearchEngine.cpp b/android/jni/com/mapswithme/maps/SearchEngine.cpp index 877b302dc9..697e06402c 100644 --- a/android/jni/com/mapswithme/maps/SearchEngine.cpp +++ b/android/jni/com/mapswithme/maps/SearchEngine.cpp @@ -33,6 +33,11 @@ jmethodID g_suggestConstructor; jclass g_descriptionClass; jmethodID g_descriptionConstructor; +// Implements 'NativeMapSearchListener' java interface. +jmethodID g_mapResultsMethod; +jclass g_mapResultClass; +jmethodID g_mapResultCtor; + jobject ToJavaResult(Result & result, bool hasPosition, double lat, double lon) { JNIEnv * env = jni::GetEnv(); @@ -93,7 +98,7 @@ jobjectArray BuildJavaResults(Results const & results, bool hasPosition, double g_results = results; int const count = g_results.GetCount(); - jobjectArray const jResults = env->NewObjectArray(count, g_resultClass, 0); + jobjectArray const jResults = env->NewObjectArray(count, g_resultClass, nullptr); for (int i = 0; i < count; i++) { jni::TScopedLocalRef jRes(env, ToJavaResult(g_results.GetResult(i), hasPosition, lat, lon)); @@ -122,6 +127,36 @@ void OnResults(Results const & results, long long timestamp, bool isMapAndTable, jni::TScopedLocalObjectArrayRef jResults(env, BuildJavaResults(results, hasPosition, lat, lon)); env->CallVoidMethod(g_javaListener, g_updateResultsId, jResults.get(), static_cast(timestamp)); } + +jobjectArray BuildJavaMapResults(vector const & results) +{ + JNIEnv * env = jni::GetEnv(); + lock_guard guard(g_resultsMutex); + + int const count = results.size(); + jobjectArray const res = env->NewObjectArray(count, g_mapResultClass, nullptr); + for (int i = 0; i < count; i++) + { + jni::TScopedLocalRef country(env, jni::ToJavaString(env, results[i].m_countryId)); + jni::TScopedLocalRef matched(env, jni::ToJavaString(env, results[i].m_matchedName)); + jni::TScopedLocalRef item(env, env->NewObject(g_mapResultClass, g_mapResultCtor, country.get(), matched.get())); + env->SetObjectArrayElement(res, i, item.get()); + } + + return res; +} + +void OnMapSearchResults(storage::DownloaderSearchResults const & results, long long timestamp) +{ + // Ignore results from obsolete searches. + if (g_queryTimestamp > timestamp) + return; + + JNIEnv * env = jni::GetEnv(); + jni::TScopedLocalObjectArrayRef jResults(env, BuildJavaMapResults(results.m_results)); + env->CallVoidMethod(g_javaListener, g_mapResultsMethod, jResults.get(), static_cast(timestamp), results.m_endMarker); +} + } // namespace extern "C" @@ -129,8 +164,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_mapswithme_maps_search_SearchEngine_nativeInit(JNIEnv * env, jobject thiz) { - if ( g_javaListener ) - env->DeleteGlobalRef(g_javaListener); g_javaListener = env->NewGlobalRef(thiz); g_updateResultsId = jni::GetMethodID(env, g_javaListener, "onResultsUpdate", "([Lcom/mapswithme/maps/search/SearchResult;J)V"); g_endResultsId = jni::GetMethodID(env, g_javaListener, "onResultsEnd", "(J)V"); @@ -139,6 +172,10 @@ extern "C" g_suggestConstructor = jni::GetConstructorID(env, g_resultClass, "(Ljava/lang/String;Ljava/lang/String;DD[I)V"); g_descriptionClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/search/SearchResult$Description"); g_descriptionConstructor = jni::GetConstructorID(env, g_descriptionClass, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V"); + + g_mapResultsMethod = jni::GetMethodID(env, g_javaListener, "onMapSearchResults", "([Lcom/mapswithme/maps/search/NativeMapSearchListener$Result;JZ)V"); + g_mapResultClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/search/NativeMapSearchListener$Result"); + g_mapResultCtor = jni::GetConstructorID(env, g_mapResultClass, "(Ljava/lang/String;Ljava/lang/String;)V"); } JNIEXPORT jboolean JNICALL @@ -180,16 +217,13 @@ extern "C" JNIEXPORT void JNICALL Java_com_mapswithme_maps_search_SearchEngine_nativeRunSearchMaps(JNIEnv * env, jclass clazz, jbyteArray bytes, jstring lang, jlong timestamp) { - search::SearchParams params; + storage::DownloaderSearchParams params; params.m_query = jni::ToNativeString(env, bytes); - params.SetInputLocale(ReplaceDeprecatedLanguageCode(jni::ToNativeString(env, lang))); - params.SetForceSearch(true); - params.SetMode(search::Mode::World); - params.SetSuggestsEnabled(false); - params.m_onResults = bind(&OnResults, _1, timestamp, false /* isMapAndTable */, false /* hasPosition */, 0.0, 0.0); + params.m_inputLocale = ReplaceDeprecatedLanguageCode(jni::ToNativeString(env, lang)); + params.m_onResults = bind(&OnMapSearchResults, _1, timestamp); - g_framework->NativeFramework()->Search(params); - g_queryTimestamp = timestamp; + if (g_framework->NativeFramework()->SearchInDownloader(params)) + g_queryTimestamp = timestamp; } JNIEXPORT void JNICALL diff --git a/android/src/com/mapswithme/maps/downloader/DownloaderAdapter.java b/android/src/com/mapswithme/maps/downloader/DownloaderAdapter.java index 82d5fab0de..2a7d0c6f27 100644 --- a/android/src/com/mapswithme/maps/downloader/DownloaderAdapter.java +++ b/android/src/com/mapswithme/maps/downloader/DownloaderAdapter.java @@ -3,6 +3,7 @@ package com.mapswithme.maps.downloader; import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; +import android.graphics.Typeface; import android.location.Location; import android.support.annotation.AttrRes; import android.support.annotation.DrawableRes; @@ -11,7 +12,10 @@ import android.support.annotation.StringRes; import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.StyleSpan; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.LayoutInflater; @@ -53,6 +57,8 @@ class DownloaderAdapter extends RecyclerView.Adapter mItems = new ArrayList<>(); private final Map mCountryIndex = new HashMap<>(); // Country.id -> Country @@ -490,8 +496,17 @@ class DownloaderAdapter extends RecyclerView.Adapter -1) + builder.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + mSearchFoundName.setText(builder); + mSearchName.setText(mItem.name); - mSearchFoundName.setText(mItem.searchResultName); UiUtils.setTextAndHideIfEmpty(mSearchParent, mItem.parentName); } else @@ -532,6 +547,8 @@ class DownloaderAdapter extends RecyclerView.Adapter results) + void setSearchResultsMode(Collection results, String query) { mSearchResultsMode = true; + mSearchQuery = query.toLowerCase(); mItems.clear(); mItems.addAll(results); diff --git a/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java b/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java index 9bf828ba5f..b8a7662b8c 100644 --- a/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java +++ b/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java @@ -17,9 +17,8 @@ import java.util.Map; import com.mapswithme.maps.R; import com.mapswithme.maps.base.BaseMwmRecyclerFragment; import com.mapswithme.maps.base.OnBackPressListener; -import com.mapswithme.maps.search.NativeSearchListener; +import com.mapswithme.maps.search.NativeMapSearchListener; import com.mapswithme.maps.search.SearchEngine; -import com.mapswithme.maps.search.SearchResult; import com.mapswithme.util.StringUtils; import com.mapswithme.util.UiUtils; @@ -47,39 +46,33 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment } }; - private final NativeSearchListener mSearchListener = new NativeSearchListener() + private final NativeMapSearchListener mSearchListener = new NativeMapSearchListener() { private final Map mResults = new HashMap<>(); @Override - public void onResultsUpdate(SearchResult[] results, long timestamp) + public void onMapSearchResults(Result[] results, long timestamp, boolean isLast) { if (!mSearchRunning || timestamp != mCurrentSearch) return; - for (SearchResult result : results) + for (Result result : results) { - String id = MapManager.nativeFindCountry(result.lat, result.lon); - if (!TextUtils.isEmpty(id) && !mResults.containsKey(id)) - { - CountryItem item = CountryItem.fill(id); - item.searchResultName = result.name; - item.category = CountryItem.CATEGORY_AVAILABLE; - mResults.put(id, item); - } + if (TextUtils.isEmpty(result.countryId) || mResults.containsKey(result.countryId)) + continue; + + CountryItem item = CountryItem.fill(result.countryId); + item.searchResultName = result.matchedString; + mResults.put(result.countryId, item); } - } - @Override - public void onResultsEnd(long timestamp) - { - if (!mSearchRunning || timestamp != mCurrentSearch) - return; + if (isLast) + { + mAdapter.setSearchResultsMode(mResults.values(), mToolbarController.getQuery()); + mResults.clear(); - mAdapter.setSearchResultsMode(mResults.values()); - mResults.clear(); - - onSearchEnd(); + onSearchEnd(); + } } }; @@ -93,14 +86,15 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment void cancelSearch() { - onSearchEnd(); mAdapter.cancelSearch(); + onSearchEnd(); } private void onSearchEnd() { mSearchRunning = false; mToolbarController.showProgress(false); + update(); } void update() @@ -112,7 +106,7 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment mToolbarController.update(onTop ? null : mAdapter.getCurrentParent(), onTop ? "" : rootName); // Bottom panel - boolean showBottom = onTop; + boolean showBottom = (onTop && !mAdapter.isSearchResultsMode()); if (showBottom) { UpdateInfo info = MapManager.nativeGetUpdateInfo(null); @@ -142,7 +136,7 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment public void onProgress(String countryId, long localSize, long remoteSize) {} }); - SearchEngine.INSTANCE.addListener(mSearchListener); + SearchEngine.INSTANCE.addMapListener(mSearchListener); } @Override @@ -155,7 +149,7 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment mSubscriberSlot = 0; } - SearchEngine.INSTANCE.removeListener(mSearchListener); + SearchEngine.INSTANCE.removeMapListener(mSearchListener); } @Override diff --git a/android/src/com/mapswithme/maps/search/NativeMapSearchListener.java b/android/src/com/mapswithme/maps/search/NativeMapSearchListener.java new file mode 100644 index 0000000000..d89fb432e4 --- /dev/null +++ b/android/src/com/mapswithme/maps/search/NativeMapSearchListener.java @@ -0,0 +1,18 @@ +package com.mapswithme.maps.search; + +public interface NativeMapSearchListener +{ + class Result + { + public final String countryId; + public final String matchedString; + + public Result(String countryId, String matchedString) + { + this.countryId = countryId; + this.matchedString = matchedString; + } + } + + void onMapSearchResults(Result[] results, long timestamp, boolean isLast); +} diff --git a/android/src/com/mapswithme/maps/search/SearchAdapter.java b/android/src/com/mapswithme/maps/search/SearchAdapter.java index 1aef2e1a18..fd1b500be1 100644 --- a/android/src/com/mapswithme/maps/search/SearchAdapter.java +++ b/android/src/com/mapswithme/maps/search/SearchAdapter.java @@ -29,9 +29,9 @@ class SearchAdapter extends RecyclerView.Adapter private final SearchFragment mSearchFragment; private SearchResult[] mResults; - private Drawable mClosedMarkerBackground; + private final Drawable mClosedMarkerBackground; - protected static abstract class BaseViewHolder extends RecyclerView.ViewHolder + static abstract class BaseViewHolder extends RecyclerView.ViewHolder { SearchResult mResult; // Position within search results @@ -221,7 +221,7 @@ class SearchAdapter extends RecyclerView.Adapter } } - public SearchAdapter(SearchFragment fragment) + SearchAdapter(SearchFragment fragment) { mSearchFragment = fragment; mClosedMarkerBackground = fragment.getResources().getDrawable(ThemeUtils.isNightTheme() ? R.drawable.search_closed_marker_night @@ -320,7 +320,7 @@ class SearchAdapter extends RecyclerView.Adapter refreshData(null); } - public void refreshData(SearchResult[] results) + void refreshData(SearchResult[] results) { mResults = results; notifyDataSetChanged(); diff --git a/android/src/com/mapswithme/maps/search/SearchEngine.java b/android/src/com/mapswithme/maps/search/SearchEngine.java index ddb1c26f81..5c73e48d1d 100644 --- a/android/src/com/mapswithme/maps/search/SearchEngine.java +++ b/android/src/com/mapswithme/maps/search/SearchEngine.java @@ -8,7 +8,8 @@ import com.mapswithme.util.Language; import com.mapswithme.util.Listeners; import com.mapswithme.util.concurrency.UiThread; -public enum SearchEngine implements NativeSearchListener +public enum SearchEngine implements NativeSearchListener, + NativeMapSearchListener { INSTANCE; @@ -45,7 +46,23 @@ public enum SearchEngine implements NativeSearchListener }); } + @Override + public void onMapSearchResults(final NativeMapSearchListener.Result[] results, final long timestamp, final boolean isLast) + { + UiThread.run(new Runnable() + { + @Override + public void run() + { + for (NativeMapSearchListener listener : mMapListeners) + listener.onMapSearchResults(results, timestamp, isLast); + mMapListeners.finishIterate(); + } + }); + } + private final Listeners mListeners = new Listeners<>(); + private final Listeners mMapListeners = new Listeners<>(); public void addListener(NativeSearchListener listener) { @@ -57,6 +74,16 @@ public enum SearchEngine implements NativeSearchListener mListeners.unregister(listener); } + public void addMapListener(NativeMapSearchListener listener) + { + mMapListeners.register(listener); + } + + public void removeMapListener(NativeMapSearchListener listener) + { + mMapListeners.unregister(listener); + } + SearchEngine() { nativeInit();