Merge pull request #2498 from trashkalmar/downloader-search

[new downloader][android] add: Downloader search.
This commit is contained in:
Dmitry Yunitsky 2016-03-24 17:08:39 +03:00
commit c0b6a34066
6 changed files with 137 additions and 45 deletions

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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);
}

View file

@ -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();

View file

@ -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();