diff --git a/android/jni/com/mapswithme/country/ActiveCountryTree.cpp b/android/jni/com/mapswithme/country/ActiveCountryTree.cpp index fb250c8ade..420179efa0 100644 --- a/android/jni/com/mapswithme/country/ActiveCountryTree.cpp +++ b/android/jni/com/mapswithme/country/ActiveCountryTree.cpp @@ -48,7 +48,7 @@ extern "C" ActiveMapsLayout & layout = GetMapLayout(); ActiveMapsLayout::TGroup coreGroup = ToGroup(group); int pos = static_cast(position); - bool const local = (isLocal == JNI_TRUE) ? true : false; + bool const local = isLocal == JNI_TRUE; TMapOptions opt = ToOptions(options); if (options == -1 || local) diff --git a/android/res/layout/item_search.xml b/android/res/layout/item_search.xml index e88e177200..bca24e4e9c 100644 --- a/android/res/layout/item_search.xml +++ b/android/res/layout/item_search.xml @@ -1,6 +1,5 @@ 0) + if (result.mHighlightRanges.length > 0) { - StringBuilder builder = new StringBuilder(); - int pos = 0, j = 0, n = r.mHighlightRanges.length / 2; + int j = 0, n = result.mHighlightRanges.length / 2; for (int i = 0; i < n; ++i) { - int start = r.mHighlightRanges[j++]; - int len = r.mHighlightRanges[j++]; + int start = result.mHighlightRanges[j++]; + int len = result.mHighlightRanges[j++]; - builder.append(r.mName.substring(pos, start)); - builder.append(""); - builder.append(r.mName.substring(start, start + len)); - builder.append(""); - - pos = start + len; + builder.setSpan(new StyleSpan(Typeface.BOLD), start, start + len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } - builder.append(r.mName.substring(pos)); - s = Html.fromHtml(builder.toString()); } else - s = Html.fromHtml(r.mName); + builder.clearSpans(); - country = r.mCountry; - dist = r.mDistance; - UiUtils.hide(holder.mImageLeft); - holder.mView.setBackgroundResource(0); + country = result.mCountry; + dist = result.mDistance; } else - s = Html.fromHtml("" + r.mName + ""); + builder.setSpan(new ForegroundColorSpan(mResources.getColor(R.color.text_green)), 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - UiUtils.setTextAndShow(holder.mName, s); + UiUtils.hide(holder.mImageLeft); + UiUtils.setTextAndShow(holder.mName, builder); UiUtils.setTextAndHideIfEmpty(holder.mCountry, country); - UiUtils.setTextAndHideIfEmpty(holder.mItemType, r.mAmenity); + UiUtils.setTextAndHideIfEmpty(holder.mItemType, result.mAmenity); UiUtils.setTextAndHideIfEmpty(holder.mDistance, dist); } } @@ -244,7 +239,7 @@ public class SearchAdapter extends BaseAdapter * @param count * @param resultId */ - public void updateData(int count, int resultId) + public void showData(int count, int resultId) { mCount = count; mResultId = resultId; @@ -252,9 +247,9 @@ public class SearchAdapter extends BaseAdapter notifyDataSetChanged(); } - public void updateCategories() + public void showCategories() { - mCount = -1; + mCount = COUNT_NO_RESULTS; notifyDataSetChanged(); } @@ -346,6 +341,7 @@ public class SearchAdapter extends BaseAdapter public static final int TYPE_FEATURE = 1; public int mType; + // consecutive pairs of numbers (each pair contains : start index, length), specifying highlighted substrings of original query public int[] mHighlightRanges; diff --git a/android/src/com/mapswithme/maps/search/SearchFragment.java b/android/src/com/mapswithme/maps/search/SearchFragment.java index 426adc436e..dd3d9110c5 100644 --- a/android/src/com/mapswithme/maps/search/SearchFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchFragment.java @@ -4,8 +4,6 @@ import android.app.Activity; import android.content.Intent; import android.location.Location; import android.os.Bundle; -import android.text.Editable; -import android.text.TextWatcher; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -20,7 +18,6 @@ import android.widget.TextView; import com.mapswithme.maps.Framework; import com.mapswithme.maps.MWMActivity; -import com.mapswithme.maps.MWMApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.base.BaseMwmListFragment; import com.mapswithme.maps.base.OnBackPressListener; @@ -28,6 +25,7 @@ import com.mapswithme.maps.data.RouterTypes; import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.util.InputUtils; import com.mapswithme.util.Language; +import com.mapswithme.util.StringUtils; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; import com.mapswithme.util.statistics.Statistics; @@ -56,10 +54,10 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL // Current position. private double mLat; private double mLon; - private double mNorth = -1.0; + private static final double DUMMY_NORTH = -1; // - private int mFlags = 0; - private int mQueryId = 0; + private int mFlags; + private int mQueryId; private SearchAdapter mAdapter; @Override @@ -90,44 +88,85 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL } } - private void initAdapter() + @Override + public void onResume() { - mAdapter = new SearchAdapter(this); + super.onResume(); + + LocationHelper.INSTANCE.addLocationListener(this); + setSearchQuery(getLastQuery()); + mEtSearchQuery.requestFocus(); + } + + @Override + public void onPause() + { + LocationHelper.INSTANCE.removeLocationListener(this); + + super.onPause(); } @Override public void onDestroy() { nativeDisconnect(); + super.onDestroy(); } + @Override + public void onListItemClick(ListView l, View v, int position, long id) + { + super.onListItemClick(l, v, position, id); + + if (!isAdded()) + return; + + position -= l.getHeaderViewsCount(); + final String suggestion = mAdapter.onItemClick(position); + if (suggestion == null) + showSearchResultOnMap(position); + else + // set suggestion string and run search (this call invokes runSearch) + setSearchQuery(suggestion); + } + /** * If search string is empty - show search categories. */ protected boolean doShowCategories() { - return getSearchString().length() == 0; + return getSearchQuery().isEmpty(); } - private String getSearchString() + private String getSearchQuery() { return mEtSearchQuery.getText().toString(); } + private void setSearchQuery(String query) + { + Utils.setTextAndCursorToEnd(mEtSearchQuery, query); + } + + private void initAdapter() + { + mAdapter = new SearchAdapter(this); + } + private void setUpView(ViewGroup root) { mBtnVoice = root.findViewById(R.id.search_voice_input); + mBtnVoice.setOnClickListener(this); mPbSearch = (ProgressBar) root.findViewById(R.id.search_progress); mBtnClearQuery = root.findViewById(R.id.search_image_clear); mBtnClearQuery.setOnClickListener(this); - // Initialize search edit box processor. mEtSearchQuery = (EditText) root.findViewById(R.id.search_text_query); - mEtSearchQuery.addTextChangedListener(new TextWatcher() + mEtSearchQuery.addTextChangedListener(new StringUtils.SimpleTextWatcher() { @Override - public void afterTextChanged(Editable s) + public void onTextChanged(CharSequence s, int start, int before, int count) { if (!isAdded()) return; @@ -154,16 +193,6 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL UiUtils.hide(mBtnVoice); } } - - @Override - public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) - { - } - - @Override - public void onTextChanged(CharSequence s, int arg1, int arg2, int arg3) - { - } }); mEtSearchQuery.setOnEditorActionListener(new TextView.OnEditorActionListener() @@ -186,22 +215,14 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL } }); - mBtnVoice.setOnClickListener(this); - final ListView listView = getListView(); listView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { - // Hide keyboard when user starts scroll InputUtils.hideKeyboard(mEtSearchQuery); - - // Hacky way to remove focus from only edittext at activity - mEtSearchQuery.setFocusableInTouchMode(false); - mEtSearchQuery.setFocusable(false); - mEtSearchQuery.setFocusableInTouchMode(true); - mEtSearchQuery.setFocusable(true); + InputUtils.removeFocusEditTextHack(mEtSearchQuery); } @Override @@ -253,46 +274,6 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL } // FIXME: This code only for demonstration purposes and will be removed soon - @Override - public void onResume() - { - super.onResume(); - - // Reset current mode flag - start first search. - mFlags = 0; - mNorth = -1.0; - - LocationHelper.INSTANCE.addLocationListener(this); - - setSearchQuery(getLastQuery()); - mEtSearchQuery.requestFocus(); - } - - @Override - public void onPause() - { - LocationHelper.INSTANCE.removeLocationListener(this); - - super.onPause(); - } - - @Override - public void onListItemClick(ListView l, View v, int position, long id) - { - super.onListItemClick(l, v, position, id); - - if (!isAdded()) - return; - - position -= l.getHeaderViewsCount(); - final String suggestion = mAdapter.onItemClick(position); - if (suggestion == null) - showSearchResultOnMap(position); - else - // set suggestion string and run search (this call invokes runSearch) - setSearchQuery(suggestion); - } - private void showSearchResultOnMap(int position) { // If user searched for something, clear API layer @@ -300,12 +281,12 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL // Put query string for "View on map" or feature name for search result. final boolean allResults = (position == 0); - final String query = getSearchString(); + final String query = getSearchQuery(); SearchController.getInstance().setQuery(allResults ? query : ""); if (allResults) { nativeShowAllSearchResults(); - runInteractiveSearch(query, Language.getKeyboardInput(getActivity())); + runInteractiveSearch(query, Language.getKeyboardLocale()); } InputUtils.hideKeyboard(mEtSearchQuery); @@ -322,10 +303,9 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL private void showCategories() { - // TODO clear edittexty? clearLastQuery(); displaySearchProgress(false); - mAdapter.updateCategories(); + mAdapter.showCategories(); } @Override @@ -346,11 +326,6 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL @Override public void onLocationError(int errorCode) {} - private boolean isCurrentResult(int id) - { - return (id >= mQueryId && id < mQueryId + QUERY_STEP); - } - // Called from native code @SuppressWarnings("unused") public void updateData(final int count, final int resultId) @@ -365,7 +340,7 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL if (!doShowCategories()) { - mAdapter.updateData(count, resultId); + mAdapter.showData(count, resultId); // scroll list view to the top setSelection(0); } @@ -390,14 +365,9 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL }); } - private void setSearchQuery(String query) - { - Utils.setTextAndCursorToEnd(mEtSearchQuery, query); - } - private int runSearch() { - final String query = getSearchString(); + final String query = getSearchQuery(); if (query.isEmpty()) { // do force search next time from empty list @@ -406,7 +376,7 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL } final int id = mQueryId + QUERY_STEP; - if (nativeRunSearch(query, Language.getKeyboardInput(MWMApplication.get()), mLat, mLon, mFlags, id)) + if (nativeRunSearch(query, Language.getKeyboardLocale(), mLat, mLon, mFlags, id)) { mQueryId = id; // mark that it's not the first query already - don't do force search @@ -446,7 +416,7 @@ public class SearchFragment extends BaseMwmListFragment implements View.OnClickL public SearchAdapter.SearchResult getResult(int position, int queryID) { - return nativeGetResult(position, queryID, mLat, mLon, (mFlags & HAS_POSITION) != 0, mNorth); + return nativeGetResult(position, queryID, mLat, mLon, (mFlags & HAS_POSITION) != 0, DUMMY_NORTH); } @Override diff --git a/android/src/com/mapswithme/util/InputUtils.java b/android/src/com/mapswithme/util/InputUtils.java index d315500e2a..46d5a1358a 100644 --- a/android/src/com/mapswithme/util/InputUtils.java +++ b/android/src/com/mapswithme/util/InputUtils.java @@ -5,6 +5,7 @@ import android.content.Intent; import android.speech.RecognizerIntent; import android.view.View; import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; import java.util.ArrayList; @@ -12,12 +13,13 @@ public class InputUtils { private static Boolean mVoiceInputSupported = null; + private InputUtils() { /* static class */ } + public static boolean isVoiceInputSupported(Context context) { if (mVoiceInputSupported == null) - { mVoiceInputSupported = Utils.isIntentSupported(context, new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)); - } + return mVoiceInputSupported; } @@ -25,7 +27,7 @@ public class InputUtils { final Intent vrIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); vrIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH) - .putExtra(RecognizerIntent.EXTRA_PROMPT, promptText); + .putExtra(RecognizerIntent.EXTRA_PROMPT, promptText); return vrIntent; } @@ -36,21 +38,30 @@ public class InputUtils */ public static String getMostConfidentResult(Intent vrIntentResult) { - final ArrayList recongnizedStrings - = vrIntentResult.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); + final ArrayList recognizedStrings + = vrIntentResult.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); - if (recongnizedStrings == null) + if (recognizedStrings == null) return null; - return recongnizedStrings.isEmpty() ? null : recongnizedStrings.get(0); + return recognizedStrings.isEmpty() ? null : recognizedStrings.get(0); } public static void hideKeyboard(View view) { final Context c = view.getContext(); - final InputMethodManager imm = (InputMethodManager)c.getSystemService(Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = (InputMethodManager) c.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } - private InputUtils() { /* static class */ } + /* + Hacky method to remove focus from the only EditText at activity + */ + public static void removeFocusEditTextHack(EditText editText) + { + editText.setFocusableInTouchMode(false); + editText.setFocusable(false); + editText.setFocusableInTouchMode(true); + editText.setFocusable(true); + } } diff --git a/android/src/com/mapswithme/util/Language.java b/android/src/com/mapswithme/util/Language.java index 889e763535..c991f6d558 100644 --- a/android/src/com/mapswithme/util/Language.java +++ b/android/src/com/mapswithme/util/Language.java @@ -1,29 +1,30 @@ package com.mapswithme.util; -import android.annotation.SuppressLint; import android.content.Context; import android.os.Build; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; +import com.mapswithme.maps.MWMApplication; + import java.util.Locale; public class Language { // Locale.getLanguage() returns even 3-letter codes, not that we need in the C++ core, // so we use locale itself, like zh_CN - static public String getDefault() + static public String getDefaultLocale() { return Locale.getDefault().toString(); } // After some testing on Galaxy S4, looks like this method doesn't work on all devices: - // sometime it always returns the same value as getDefault() - static public String getKeyboardInput(Context context) + // sometime it always returns the same value as getDefaultLocale() + static public String getKeyboardLocale() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = (InputMethodManager) MWMApplication.get().getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { final InputMethodSubtype ims = imm.getCurrentInputMethodSubtype(); @@ -32,6 +33,6 @@ public class Language } } - return getDefault(); + return getDefaultLocale(); } } diff --git a/android/src/com/mapswithme/util/StringUtils.java b/android/src/com/mapswithme/util/StringUtils.java index 6e4ec3ffe4..1e0826438b 100644 --- a/android/src/com/mapswithme/util/StringUtils.java +++ b/android/src/com/mapswithme/util/StringUtils.java @@ -1,7 +1,9 @@ package com.mapswithme.util; +import android.text.Editable; import android.text.Spannable; import android.text.SpannableStringBuilder; +import android.text.TextWatcher; import android.text.style.CharacterStyle; import com.mapswithme.maps.MWMApplication; @@ -62,6 +64,7 @@ public class StringUtils * Removes html tags, generated from edittext content after it's transformed to html. * In version 4.3.1 we converted descriptions, entered by users, to html automatically. Later html conversion was cancelled, but those converted descriptions should be converted back to * plain text, that's why that ugly util is introduced. + * * @param text source text * @return result text */ @@ -84,5 +87,17 @@ public class StringUtils return (size + Constants.KB - 1) / Constants.KB + " " + MWMApplication.get().getString(R.string.kb); } + public static class SimpleTextWatcher implements 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) { } + } + private StringUtils() {} }