forked from organicmaps/organicmaps
Merge pull request #2498 from trashkalmar/downloader-search
[new downloader][android] add: Downloader search.
This commit is contained in:
commit
c0b6a34066
6 changed files with 137 additions and 45 deletions
|
@ -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<jlong>(timestamp));
|
||||
}
|
||||
|
||||
jobjectArray BuildJavaMapResults(vector<storage::DownloaderSearchResult> const & results)
|
||||
{
|
||||
JNIEnv * env = jni::GetEnv();
|
||||
lock_guard<mutex> 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<jlong>(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
|
||||
|
|
|
@ -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<DownloaderAdapter.ViewHolde
|
|||
private final StickyRecyclerHeadersDecoration mHeadersDecoration;
|
||||
|
||||
private boolean mSearchResultsMode;
|
||||
private String mSearchQuery;
|
||||
|
||||
private final List<CountryItem> mItems = new ArrayList<>();
|
||||
private final Map<String, CountryItem> mCountryIndex = new HashMap<>(); // Country.id -> Country
|
||||
|
||||
|
@ -490,8 +496,17 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
|
|||
|
||||
if (mSearchResultsMode)
|
||||
{
|
||||
String found = mItem.searchResultName.toLowerCase();
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder(mItem.searchResultName);
|
||||
int start = found.indexOf(mSearchQuery);
|
||||
int end = start + mSearchQuery.length();
|
||||
|
||||
if (start > -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<DownloaderAdapter.ViewHolde
|
|||
private void collectHeaders()
|
||||
{
|
||||
mHeaders.clear();
|
||||
if (mSearchResultsMode)
|
||||
return;
|
||||
|
||||
int headerId = 0;
|
||||
int prev = -1;
|
||||
|
@ -574,6 +591,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
|
|||
private void refreshData()
|
||||
{
|
||||
mSearchResultsMode = false;
|
||||
mSearchQuery = null;
|
||||
|
||||
mItems.clear();
|
||||
|
||||
|
@ -603,9 +621,10 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
|
|||
refreshData();
|
||||
}
|
||||
|
||||
void setSearchResultsMode(Collection<CountryItem> results)
|
||||
void setSearchResultsMode(Collection<CountryItem> results, String query)
|
||||
{
|
||||
mSearchResultsMode = true;
|
||||
mSearchQuery = query.toLowerCase();
|
||||
|
||||
mItems.clear();
|
||||
mItems.addAll(results);
|
||||
|
|
|
@ -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<String, CountryItem> 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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -29,9 +29,9 @@ class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.BaseViewHolder>
|
|||
|
||||
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<SearchAdapter.BaseViewHolder>
|
|||
}
|
||||
}
|
||||
|
||||
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<SearchAdapter.BaseViewHolder>
|
|||
refreshData(null);
|
||||
}
|
||||
|
||||
public void refreshData(SearchResult[] results)
|
||||
void refreshData(SearchResult[] results)
|
||||
{
|
||||
mResults = results;
|
||||
notifyDataSetChanged();
|
||||
|
|
|
@ -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<NativeSearchListener> mListeners = new Listeners<>();
|
||||
private final Listeners<NativeMapSearchListener> 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();
|
||||
|
|
Loading…
Add table
Reference in a new issue