diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index a0d089b317..1b9d613317 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -188,10 +188,9 @@ + android:windowSoftInputMode="stateVisible"> DeregisterAllMaps(); } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_Framework_nativeGetRecentSearchQueries(JNIEnv * env, jclass thiz, jobject result) + { + using TSearchRequest = search::QuerySaver::TSearchRequest; + const list &items = frm()->GetLastSearchQueries(); + if (items.empty()) + return; + + jclass listClass = env->GetObjectClass(result); + static const jmethodID listAddMethod = env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z"); + + jclass pairClass = env->FindClass("android/util/Pair"); + static const jmethodID pairCtor = env->GetMethodID(pairClass, "", "(Ljava/lang/Object;Ljava/lang/Object;)V"); + + for (const TSearchRequest &item : items) + { + jstring locale = jni::ToJavaString(env, item.first.c_str()); + jstring query = jni::ToJavaString(env, item.second.c_str()); + jobject pair = env->NewObject(pairClass, pairCtor, locale, query); + ASSERT(pair, (jni::DescribeException())); + + env->CallBooleanMethod(result, listAddMethod, pair); + + env->DeleteLocalRef(locale); + env->DeleteLocalRef(query); + env->DeleteLocalRef(pair); + } + } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_Framework_nativeAddRecentSearchQuery(JNIEnv * env, jclass thiz, jstring locale, jstring query) + { + search::QuerySaver::TSearchRequest sr(jni::ToNativeString(env, locale), jni::ToNativeString(env, query)); + frm()->SaveSearchQuery(sr); + } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_Framework_nativeClearRecentSearchQueries(JNIEnv * env, jclass thiz) + { + frm()->ClearSearchHistory(); + } } // extern "C" diff --git a/android/jni/com/mapswithme/maps/SearchFragment.cpp b/android/jni/com/mapswithme/maps/SearchFragment.cpp index 489c06c18f..9f7e50301d 100644 --- a/android/jni/com/mapswithme/maps/SearchFragment.cpp +++ b/android/jni/com/mapswithme/maps/SearchFragment.cpp @@ -38,8 +38,8 @@ class SearchAdapter if (res.IsEndMarker()) { - jmethodID const methodId = jni::GetJavaMethodID(env, m_fragment, "onResultsEnd", "()V"); - env->CallVoidMethod(m_fragment, methodId); + jmethodID const methodId = jni::GetJavaMethodID(env, m_fragment, "onResultsEnd", "(I)V"); + env->CallVoidMethod(m_fragment, methodId, queryID); return; } @@ -204,17 +204,15 @@ Java_com_mapswithme_maps_search_SearchFragment_nativeDisconnectSearchListener(JN JNIEXPORT jboolean JNICALL Java_com_mapswithme_maps_search_SearchFragment_nativeRunSearch( JNIEnv * env, jobject thiz, jstring s, jstring lang, - jdouble lat, jdouble lon, jint flags, jint queryID) + jint queryID, jboolean force, jboolean hasPosition, jdouble lat, jdouble lon) { search::SearchParams params; params.m_query = jni::ToNativeString(env, s); params.SetInputLocale(ReplaceDeprecatedLanguageCode(jni::ToNativeString(env, lang))); - - /// @note These magic numbers should be equal with NOT_FIRST_QUERY and HAS_POSITION - /// from SearchFragment.java - if ((flags & 1) == 0) params.SetForceSearch(true); - if ((flags & 2) != 0) params.SetPosition(lat, lon); + params.SetForceSearch(force); + if (hasPosition) + params.SetPosition(lat, lon); return SearchAdapter::Instance().RunSearch(env, params, queryID); } @@ -234,7 +232,7 @@ Java_com_mapswithme_maps_search_SearchFragment_nativeShowAllSearchResults(JNIEnv JNIEXPORT jobject JNICALL Java_com_mapswithme_maps_search_SearchFragment_nativeGetResult( JNIEnv * env, jobject thiz, jint position, jint queryID, - jdouble lat, jdouble lon, jboolean hasPosition, jdouble north) + jboolean hasPosition, jdouble lat, jdouble lon, jdouble north) { search::Result const * res = SearchAdapter::Instance().GetResult(position, queryID); if (res == nullptr) return 0; @@ -250,24 +248,24 @@ Java_com_mapswithme_maps_search_SearchFragment_nativeGetResult( env->ReleaseIntArrayElements(ranges, narr, 0); - static shared_ptr klassGlobalRef = jni::make_global_ref(env->FindClass("com/mapswithme/maps/search/SearchResult")); - jclass klass = static_cast(*klassGlobalRef.get()); - ASSERT(klass, ()); + static shared_ptr resultClassGlobalRef = jni::make_global_ref(env->FindClass("com/mapswithme/maps/search/SearchResult")); + jclass resultClass = static_cast(*resultClassGlobalRef.get()); + ASSERT(resultClass, ()); if (res->IsSuggest()) { - static jmethodID methodID = env->GetMethodID(klass, "", "(Ljava/lang/String;Ljava/lang/String;[I)V"); - ASSERT ( methodID, () ); + static jmethodID suggestCtor = env->GetMethodID(resultClass, "", "(;Ljava/lang/String;;Ljava/lang/String;[I)V"); + ASSERT(suggestCtor, ()); - return env->NewObject(klass, methodID, + return env->NewObject(resultClass, suggestCtor, jni::ToJavaString(env, res->GetString()), jni::ToJavaString(env, res->GetSuggestionString()), static_cast(ranges)); } - static jmethodID methodID = env->GetMethodID(klass, "", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)V"); - ASSERT ( methodID, () ); + static jmethodID resultCtor = env->GetMethodID(klass, "", + "(Ljava/lang/String;Lcom/mapswithme/maps/search/SearchResult$Description;[I)V"); + ASSERT ( resultCtor, () ); string distance; if (hasPosition) @@ -276,11 +274,24 @@ Java_com_mapswithme_maps_search_SearchFragment_nativeGetResult( (void) g_framework->NativeFramework()->GetDistanceAndAzimut(res->GetFeatureCenter(), lat, lon, north, distance, dummy); } - return env->NewObject(klass, methodID, + static shared_ptr descClassGlobalRef = jni::make_global_ref(env->FindClass("com/mapswithme/maps/search/SearchResult$Description")); + jclass descClass = static_cast(*descClassGlobalRef.get()); + ASSERT(descClass, ()); + + static jmethodID descCtor = env->GetMethodID(descClass, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZ)V"); + ASSERT(descCtor, ()); + + jobject desc = env->NewObject(descClass, descCtor, + jni::ToJavaString(env, res->GetFeatureType()), + jni::ToJavaString(env, res->GetRegionString()), + jni::ToJavaString(env, distance.c_str()), + jni::ToJavaString(env, res->GetCuisine()), + res->GetStarsCount(), + res->IsClosed()); + + return env->NewObject(resultClass, resultCtor, jni::ToJavaString(env, res->GetString()), - jni::ToJavaString(env, res->GetRegionString()), - jni::ToJavaString(env, res->GetFeatureType()), - jni::ToJavaString(env, distance.c_str()), + desc, static_cast(ranges)); } diff --git a/android/res/color/text_highlight.xml b/android/res/color/text_highlight.xml new file mode 100644 index 0000000000..88243c303e --- /dev/null +++ b/android/res/color/text_highlight.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/android/res/drawable-hdpi/ic_search_menu.png b/android/res/drawable-hdpi/ic_search_menu.png deleted file mode 100644 index 239f13862e..0000000000 Binary files a/android/res/drawable-hdpi/ic_search_menu.png and /dev/null differ diff --git a/android/res/drawable-hdpi/ic_search_menu_2.png b/android/res/drawable-hdpi/ic_search_menu_2.png deleted file mode 100644 index b4ce4cf6e6..0000000000 Binary files a/android/res/drawable-hdpi/ic_search_menu_2.png and /dev/null differ diff --git a/android/res/drawable-hdpi/ic_search_menu_3.png b/android/res/drawable-hdpi/ic_search_menu_3.png deleted file mode 100644 index 42e4f0b038..0000000000 Binary files a/android/res/drawable-hdpi/ic_search_menu_3.png and /dev/null differ diff --git a/android/res/drawable-hdpi/ic_search_menu_4.png b/android/res/drawable-hdpi/ic_search_menu_4.png deleted file mode 100644 index a8bbe7e8c8..0000000000 Binary files a/android/res/drawable-hdpi/ic_search_menu_4.png and /dev/null differ diff --git a/android/res/drawable-hdpi/ic_search_tab_categories.png b/android/res/drawable-hdpi/ic_search_tab_categories.png new file mode 100644 index 0000000000..141c2ad016 Binary files /dev/null and b/android/res/drawable-hdpi/ic_search_tab_categories.png differ diff --git a/android/res/drawable-hdpi/ic_search_tab_history.png b/android/res/drawable-hdpi/ic_search_tab_history.png new file mode 100644 index 0000000000..ad5c226dc1 Binary files /dev/null and b/android/res/drawable-hdpi/ic_search_tab_history.png differ diff --git a/android/res/drawable-mdpi/ic_search_menu.png b/android/res/drawable-mdpi/ic_search_menu.png deleted file mode 100644 index 52177d8f77..0000000000 Binary files a/android/res/drawable-mdpi/ic_search_menu.png and /dev/null differ diff --git a/android/res/drawable-mdpi/ic_search_menu_2.png b/android/res/drawable-mdpi/ic_search_menu_2.png deleted file mode 100644 index ff054404c4..0000000000 Binary files a/android/res/drawable-mdpi/ic_search_menu_2.png and /dev/null differ diff --git a/android/res/drawable-mdpi/ic_search_menu_3.png b/android/res/drawable-mdpi/ic_search_menu_3.png deleted file mode 100644 index 9f29eec931..0000000000 Binary files a/android/res/drawable-mdpi/ic_search_menu_3.png and /dev/null differ diff --git a/android/res/drawable-mdpi/ic_search_menu_4.png b/android/res/drawable-mdpi/ic_search_menu_4.png deleted file mode 100644 index b125770fe2..0000000000 Binary files a/android/res/drawable-mdpi/ic_search_menu_4.png and /dev/null differ diff --git a/android/res/drawable-mdpi/ic_search_tab_categories.png b/android/res/drawable-mdpi/ic_search_tab_categories.png new file mode 100644 index 0000000000..173185f45e Binary files /dev/null and b/android/res/drawable-mdpi/ic_search_tab_categories.png differ diff --git a/android/res/drawable-mdpi/ic_search_tab_history.png b/android/res/drawable-mdpi/ic_search_tab_history.png new file mode 100644 index 0000000000..a92d8a8dd9 Binary files /dev/null and b/android/res/drawable-mdpi/ic_search_tab_history.png differ diff --git a/android/res/drawable-xhdpi/ic_search_menu.png b/android/res/drawable-xhdpi/ic_search_menu.png deleted file mode 100644 index eba5af0b6e..0000000000 Binary files a/android/res/drawable-xhdpi/ic_search_menu.png and /dev/null differ diff --git a/android/res/drawable-xhdpi/ic_search_menu_2.png b/android/res/drawable-xhdpi/ic_search_menu_2.png deleted file mode 100644 index f3e071394a..0000000000 Binary files a/android/res/drawable-xhdpi/ic_search_menu_2.png and /dev/null differ diff --git a/android/res/drawable-xhdpi/ic_search_menu_3.png b/android/res/drawable-xhdpi/ic_search_menu_3.png deleted file mode 100644 index 7b625a3bec..0000000000 Binary files a/android/res/drawable-xhdpi/ic_search_menu_3.png and /dev/null differ diff --git a/android/res/drawable-xhdpi/ic_search_menu_4.png b/android/res/drawable-xhdpi/ic_search_menu_4.png deleted file mode 100644 index 1a47676f4c..0000000000 Binary files a/android/res/drawable-xhdpi/ic_search_menu_4.png and /dev/null differ diff --git a/android/res/drawable-xhdpi/ic_search_tab_categories.png b/android/res/drawable-xhdpi/ic_search_tab_categories.png new file mode 100644 index 0000000000..78db4ff4c1 Binary files /dev/null and b/android/res/drawable-xhdpi/ic_search_tab_categories.png differ diff --git a/android/res/drawable-xhdpi/ic_search_tab_history.png b/android/res/drawable-xhdpi/ic_search_tab_history.png new file mode 100644 index 0000000000..02949c4efc Binary files /dev/null and b/android/res/drawable-xhdpi/ic_search_tab_history.png differ diff --git a/android/res/drawable-xxhdpi/ic_search_menu.png b/android/res/drawable-xxhdpi/ic_search_menu.png deleted file mode 100644 index 7ccc0a3160..0000000000 Binary files a/android/res/drawable-xxhdpi/ic_search_menu.png and /dev/null differ diff --git a/android/res/drawable-xxhdpi/ic_search_menu_2.png b/android/res/drawable-xxhdpi/ic_search_menu_2.png deleted file mode 100644 index 1e6e0abd25..0000000000 Binary files a/android/res/drawable-xxhdpi/ic_search_menu_2.png and /dev/null differ diff --git a/android/res/drawable-xxhdpi/ic_search_menu_3.png b/android/res/drawable-xxhdpi/ic_search_menu_3.png deleted file mode 100644 index d5470a7b14..0000000000 Binary files a/android/res/drawable-xxhdpi/ic_search_menu_3.png and /dev/null differ diff --git a/android/res/drawable-xxhdpi/ic_search_menu_4.png b/android/res/drawable-xxhdpi/ic_search_menu_4.png deleted file mode 100644 index fa4f7f7301..0000000000 Binary files a/android/res/drawable-xxhdpi/ic_search_menu_4.png and /dev/null differ diff --git a/android/res/drawable-xxhdpi/ic_search_tab_categories.png b/android/res/drawable-xxhdpi/ic_search_tab_categories.png new file mode 100644 index 0000000000..2ad023b584 Binary files /dev/null and b/android/res/drawable-xxhdpi/ic_search_tab_categories.png differ diff --git a/android/res/drawable-xxhdpi/ic_search_tab_history.png b/android/res/drawable-xxhdpi/ic_search_tab_history.png new file mode 100644 index 0000000000..e4cd6dd53d Binary files /dev/null and b/android/res/drawable-xxhdpi/ic_search_tab_history.png differ diff --git a/android/res/drawable-xxxhdpi/ic_search_tab_categories.png b/android/res/drawable-xxxhdpi/ic_search_tab_categories.png new file mode 100644 index 0000000000..a75414911e Binary files /dev/null and b/android/res/drawable-xxxhdpi/ic_search_tab_categories.png differ diff --git a/android/res/drawable-xxxhdpi/ic_search_tab_history.png b/android/res/drawable-xxxhdpi/ic_search_tab_history.png new file mode 100644 index 0000000000..76dfc11901 Binary files /dev/null and b/android/res/drawable-xxxhdpi/ic_search_tab_history.png differ diff --git a/android/res/drawable/search_closed_marker.xml b/android/res/drawable/search_closed_marker.xml new file mode 100644 index 0000000000..5e3af4b7d5 --- /dev/null +++ b/android/res/drawable/search_closed_marker.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/android/res/layout-land/activity_map.xml b/android/res/layout-land/activity_map.xml index 2b213cd90d..b5df7aa612 100644 --- a/android/res/layout-land/activity_map.xml +++ b/android/res/layout-land/activity_map.xml @@ -11,7 +11,7 @@ android:layout_height="match_parent"/> + android:layout_below="@id/toolbar"/> - + android:layout_below="@id/toolbar"/> - - - - - - - - - - - \ No newline at end of file + + + \ No newline at end of file diff --git a/android/res/layout/fragment_search.xml b/android/res/layout/fragment_search.xml index 94c2480c8d..b38a4fa70b 100644 --- a/android/res/layout/fragment_search.xml +++ b/android/res/layout/fragment_search.xml @@ -1,5 +1,7 @@ - - + + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + + + + + + android:layout_height="match_parent" + android:background="@android:color/white"> + + + + + + + + + + + + - \ No newline at end of file diff --git a/android/res/layout/fragment_search_recent.xml b/android/res/layout/fragment_search_recent.xml new file mode 100644 index 0000000000..f62a754010 --- /dev/null +++ b/android/res/layout/fragment_search_recent.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/res/layout/item_search.xml b/android/res/layout/item_search.xml deleted file mode 100644 index 1b1f280fb3..0000000000 --- a/android/res/layout/item_search.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/res/layout/item_search_category.xml b/android/res/layout/item_search_category.xml index 170e5fce27..b26f80af7b 100644 --- a/android/res/layout/item_search_category.xml +++ b/android/res/layout/item_search_category.xml @@ -1,13 +1,13 @@ - - + - \ No newline at end of file diff --git a/android/res/layout/item_search_common.xml b/android/res/layout/item_search_common.xml new file mode 100644 index 0000000000..ce38f5aee8 --- /dev/null +++ b/android/res/layout/item_search_common.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/android/res/layout/item_search_message.xml b/android/res/layout/item_search_message.xml deleted file mode 100644 index 48529d36b8..0000000000 --- a/android/res/layout/item_search_message.xml +++ /dev/null @@ -1,13 +0,0 @@ - - \ No newline at end of file diff --git a/android/res/layout/item_search_populate.xml b/android/res/layout/item_search_populate.xml new file mode 100644 index 0000000000..7dff78b273 --- /dev/null +++ b/android/res/layout/item_search_populate.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/android/res/layout/item_search_recent.xml b/android/res/layout/item_search_recent.xml new file mode 100644 index 0000000000..4fcb2f3b4d --- /dev/null +++ b/android/res/layout/item_search_recent.xml @@ -0,0 +1,6 @@ + + diff --git a/android/res/layout/item_search_result.xml b/android/res/layout/item_search_result.xml new file mode 100644 index 0000000000..6350679831 --- /dev/null +++ b/android/res/layout/item_search_result.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/android/res/layout/item_search_suggest.xml b/android/res/layout/item_search_suggest.xml new file mode 100644 index 0000000000..3a2121bada --- /dev/null +++ b/android/res/layout/item_search_suggest.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/android/res/layout/recycler_default.xml b/android/res/layout/recycler_default.xml index 0f956ecbde..81c73788ef 100644 --- a/android/res/layout/recycler_default.xml +++ b/android/res/layout/recycler_default.xml @@ -1,6 +1,6 @@ \ No newline at end of file diff --git a/android/res/layout/search_box.xml b/android/res/layout/search_box.xml index 2ac2756c0e..28191152a1 100644 --- a/android/res/layout/search_box.xml +++ b/android/res/layout/search_box.xml @@ -7,10 +7,11 @@ android:layout_height="match_parent" android:baselineAligned="false" android:gravity="center_vertical" - android:orientation="horizontal"> + android:orientation="horizontal" + tools:background="#FF004F00"> + android:visibility="gone" + tools:visibility="visible"/> \ No newline at end of file diff --git a/android/res/layout/tab.xml b/android/res/layout/tab.xml new file mode 100644 index 0000000000..de65ec01b3 --- /dev/null +++ b/android/res/layout/tab.xml @@ -0,0 +1,13 @@ + + diff --git a/android/res/layout/toolbar_with_search.xml b/android/res/layout/toolbar_with_search.xml index ca4987900f..d362079b87 100644 --- a/android/res/layout/toolbar_with_search.xml +++ b/android/res/layout/toolbar_with_search.xml @@ -1,13 +1,10 @@ - - + android:layout_height="?attr/actionBarSize"> + diff --git a/android/res/values-land/dimens.xml b/android/res/values-land/dimens.xml index 9bac179633..316ea1e3c4 100644 --- a/android/res/values-land/dimens.xml +++ b/android/res/values-land/dimens.xml @@ -1,7 +1,6 @@ - - -10dp - 12dp - + -10dp + 12dp + 48dp \ No newline at end of file diff --git a/android/res/values/colors.xml b/android/res/values/colors.xml index bafdf0fa4a..f60d032676 100644 --- a/android/res/values/colors.xml +++ b/android/res/values/colors.xml @@ -68,6 +68,6 @@ - @color/text_green + #1F000000 diff --git a/android/res/values/dimens.xml b/android/res/values/dimens.xml index 3c476a0088..540b0f0585 100644 --- a/android/res/values/dimens.xml +++ b/android/res/values/dimens.xml @@ -80,4 +80,6 @@ 4dp 4dp + + 64dp diff --git a/android/res/values/donottranslate.xml b/android/res/values/donottranslate.xml index 78b1ba0273..313b154f5b 100644 --- a/android/res/values/donottranslate.xml +++ b/android/res/values/donottranslate.xml @@ -38,5 +38,6 @@ %2$s :%1$s - collapse + collapse + height limited \ No newline at end of file diff --git a/android/res/values/font_sizes.xml b/android/res/values/font_sizes.xml index 5427a2d157..2456e63d29 100644 --- a/android/res/values/font_sizes.xml +++ b/android/res/values/font_sizes.xml @@ -14,6 +14,7 @@ 16sp 16sp 14sp + 12sp 14sp 10sp 16sp diff --git a/android/res/values/styles.xml b/android/res/values/styles.xml index 2cf2578f87..b11d611d55 100644 --- a/android/res/values/styles.xml +++ b/android/res/values/styles.xml @@ -157,6 +157,18 @@ @android:color/transparent + + + + + + diff --git a/android/script/replace_links.bat b/android/script/replace_links.bat index e3d5b34273..430c44ea95 100644 --- a/android/script/replace_links.bat +++ b/android/script/replace_links.bat @@ -8,6 +8,7 @@ cp ../data/copyright.html assets/ cp ../data/countries.txt assets/ cp ../data/drules_proto.bin assets/ cp ../data/drules_proto_dark.bin assets/ +cp ../data/drules_proto_clear.bin assets/ cp ../data/external_resources.txt assets/ cp ../data/faq.html assets/ cp ../data/fonts_blacklist.txt assets/ @@ -19,14 +20,19 @@ cp ../data/unicode_blocks.txt assets/ cp -r ../data/resources-hdpi/ assets/ cp -r ../data/resources-hdpi_dark/ assets/ +cp -r ../data/resources-hdpi_clear/ assets/ cp -r ../data/resources-ldpi/ assets/ cp -r ../data/resources-ldpi_dark/ assets/ +cp -r ../data/resources-ldpi_clear/ assets/ cp -r ../data/resources-mdpi/ assets/ cp -r ../data/resources-mdpi_dark/ assets/ +cp -r ../data/resources-mdpi_clear/ assets/ cp -r ../data/resources-xhdpi/ assets/ cp -r ../data/resources-xhdpi_dark/ assets/ +cp -r ../data/resources-xhdpi_clear/ assets/ cp -r ../data/resources-xxhdpi/ assets/ cp -r ../data/resources-xxhdpi_dark/ assets/ +cp -r ../data/resources-xxhdpi_clear/ assets/ cp -r ../data/sound-strings/ assets/ diff --git a/android/src/com/mapswithme/maps/Framework.java b/android/src/com/mapswithme/maps/Framework.java index 6ec47c89ba..c4fad59329 100644 --- a/android/src/com/mapswithme/maps/Framework.java +++ b/android/src/com/mapswithme/maps/Framework.java @@ -1,5 +1,6 @@ package com.mapswithme.maps; +import android.util.Pair; import com.mapswithme.maps.MapStorage.Index; import com.mapswithme.maps.bookmarks.data.DistanceAndAzimut; import com.mapswithme.maps.bookmarks.data.MapObject; @@ -7,6 +8,8 @@ import com.mapswithme.maps.bookmarks.data.MapObject.SearchResult; import com.mapswithme.maps.routing.RoutingInfo; import com.mapswithme.util.Constants; +import java.util.List; + /** * This class wraps android::Framework.cpp class * via static methods @@ -188,4 +191,10 @@ public class Framework public native static void nativeRegisterMaps(); public native static void nativeDeregisterMaps(); + + public native static void nativeGetRecentSearchQueries(List> result); + + public native static void nativeAddRecentSearchQuery(String locale, String query); + + public native static void nativeClearRecentSearchQueries(); } diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index eb894447de..744edcd1f2 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -20,7 +20,6 @@ import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.Toast; - import com.mapswithme.country.ActiveCountryTree; import com.mapswithme.country.DownloadActivity; import com.mapswithme.country.DownloadFragment; @@ -42,7 +41,7 @@ import com.mapswithme.maps.location.LocationPredictor; import com.mapswithme.maps.routing.RoutingResultCodesProcessor; import com.mapswithme.maps.search.SearchActivity; import com.mapswithme.maps.search.SearchFragment; -import com.mapswithme.maps.search.SearchToolbarController; +import com.mapswithme.maps.search.FloatingSearchToolbarController; import com.mapswithme.maps.settings.SettingsActivity; import com.mapswithme.maps.settings.StoragePathManager; import com.mapswithme.maps.settings.UnitLocale; @@ -53,12 +52,7 @@ import com.mapswithme.maps.widget.menu.MainMenu; import com.mapswithme.maps.widget.placepage.BasePlacePageAnimationController; import com.mapswithme.maps.widget.placepage.PlacePageView; import com.mapswithme.maps.widget.placepage.PlacePageView.State; -import com.mapswithme.util.BottomSheetHelper; -import com.mapswithme.util.InputUtils; -import com.mapswithme.util.LocationUtils; -import com.mapswithme.util.UiUtils; -import com.mapswithme.util.Utils; -import com.mapswithme.util.Yota; +import com.mapswithme.util.*; import com.mapswithme.util.sharing.ShareOption; import com.mapswithme.util.sharing.SharingHelper; import com.mapswithme.util.statistics.AlohaHelper; @@ -81,9 +75,17 @@ public class MwmActivity extends BaseMwmFragmentActivity ChooseBookmarkCategoryFragment.Listener { public static final String EXTRA_TASK = "map_task"; - private static final String EXTRA_CONSUMED = "mwm.extra.intent.processed"; + private final static String TAG = "MwmActivity"; + private final static String EXTRA_CONSUMED = "mwm.extra.intent.processed"; + private final static String EXTRA_SCREENSHOTS_TASK = "screenshots_task"; + private final static String SCREENSHOTS_TASK_LOCATE = "locate_task"; + private final static String SCREENSHOTS_TASK_PPP = "show_place_page"; + private final static String EXTRA_LAT = "lat"; + private final static String EXTRA_LON = "lon"; + private static final String EXTRA_SET_MAP_STYLE = "set_map_style"; private static final String EXTRA_UPDATE_COUNTRIES = ".extra.update.countries"; + private static final String[] DOCKED_FRAGMENTS = {SearchFragment.class.getName(), DownloadFragment.class.getName()}; // Instance state @@ -116,7 +118,7 @@ public class MwmActivity extends BaseMwmFragmentActivity private boolean mIsFragmentContainer; private LocationPredictor mLocationPredictor; - private SearchToolbarController mSearchController; + private FloatingSearchToolbarController mSearchController; private LastCompassData mLastCompassData; public interface LeftAnimationTrackListener @@ -149,13 +151,6 @@ public class MwmActivity extends BaseMwmFragmentActivity .putExtra(DownloadResourcesActivity.EXTRA_AUTODOWNLOAD_COUNTRY, doAutoDownload); } - public static void setMapStyle(Context context, int mapStyle) - { - final Intent mapIntent = new Intent(context, MwmActivity.class); - mapIntent.putExtra(EXTRA_SET_MAP_STYLE, mapStyle); - context.startActivity(mapIntent); - } - public static void startSearch(Context context, String query) { final MwmActivity activity = (MwmActivity) context; @@ -281,7 +276,7 @@ public class MwmActivity extends BaseMwmFragmentActivity return; popFragment(); - SearchToolbarController.cancelSearch(); + FloatingSearchToolbarController.cancelSearch(); mSearchController.refreshToolbar(); replaceFragment(DownloadFragment.class, true, args); } @@ -311,7 +306,9 @@ public class MwmActivity extends BaseMwmFragmentActivity processIntent(getIntent()); mLocationPredictor = new LocationPredictor(new Handler(), this); - mSearchController = new SearchToolbarController(this); + + if (mIsFragmentContainer) + mSearchController = new FloatingSearchToolbarController(this); SharingHelper.prepare(); } @@ -777,7 +774,10 @@ public class MwmActivity extends BaseMwmFragmentActivity listenLocationStateUpdates(); invalidateLocationState(); adjustZoomButtons(Framework.nativeIsRoutingActive()); - mSearchController.refreshToolbar(); + + if (mIsFragmentContainer) + mSearchController.refreshToolbar(); + mPlacePage.onResume(); LikesManager.INSTANCE.showDialogs(this); mMainMenu.onResume(); @@ -865,9 +865,9 @@ public class MwmActivity extends BaseMwmFragmentActivity return; } - if (mSearchController.hide()) + if (mIsFragmentContainer && mSearchController.hide()) { - SearchToolbarController.cancelSearch(); + FloatingSearchToolbarController.cancelSearch(); return; } @@ -1173,7 +1173,9 @@ public class MwmActivity extends BaseMwmFragmentActivity if (popFragment()) { InputUtils.hideKeyboard(mMainMenu.getFrame()); - mSearchController.refreshToolbar(); + + if (mIsFragmentContainer) + mSearchController.refreshToolbar(); } } diff --git a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java index bdbaeac5ec..22ff411321 100644 --- a/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java +++ b/android/src/com/mapswithme/maps/base/BaseMwmRecyclerFragment.java @@ -2,7 +2,7 @@ package com.mapswithme.maps.base; import android.app.Activity; import android.os.Bundle; -import android.support.annotation.Nullable; +import android.support.annotation.LayoutRes; import android.support.v4.app.Fragment; import android.support.v4.app.NavUtils; import android.support.v7.widget.LinearLayoutManager; @@ -11,7 +11,6 @@ import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import com.mapswithme.maps.R; import com.mapswithme.maps.activity.CustomNavigateUpListener; import com.mapswithme.util.UiUtils; @@ -21,11 +20,15 @@ public abstract class BaseMwmRecyclerFragment extends Fragment private Toolbar mToolbar; protected RecyclerView mRecycler; - @Nullable + protected @LayoutRes int getLayoutRes() + { + return R.layout.fragment_recycler; + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_recycler, container, false); + return inflater.inflate(getLayoutRes(), container, false); } @Override diff --git a/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java b/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java index 63e022f0b1..6eaf806453 100644 --- a/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java +++ b/android/src/com/mapswithme/maps/bookmarks/BookmarkCategoriesFragment.java @@ -2,11 +2,9 @@ package com.mapswithme.maps.bookmarks; import android.content.Intent; import android.os.Bundle; -import android.view.LayoutInflater; +import android.support.annotation.LayoutRes; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; - import com.cocosw.bottomsheet.BottomSheet; import com.mapswithme.maps.R; import com.mapswithme.maps.base.BaseMwmRecyclerFragment; @@ -28,9 +26,9 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment private BookmarkCategoriesAdapter mAdapter; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) + protected @LayoutRes int getLayoutRes() { - return inflater.inflate(R.layout.recycler_default, container, false); + return R.layout.recycler_default; } @Override diff --git a/android/src/com/mapswithme/maps/bookmarks/BookmarkListAdapter.java b/android/src/com/mapswithme/maps/bookmarks/BookmarkListAdapter.java index 684100388c..fb8ff0f2f9 100644 --- a/android/src/com/mapswithme/maps/bookmarks/BookmarkListAdapter.java +++ b/android/src/com/mapswithme/maps/bookmarks/BookmarkListAdapter.java @@ -9,14 +9,13 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; - import com.mapswithme.maps.R; import com.mapswithme.maps.bookmarks.data.Bookmark; import com.mapswithme.maps.bookmarks.data.BookmarkCategory; import com.mapswithme.maps.bookmarks.data.DistanceAndAzimut; import com.mapswithme.maps.bookmarks.data.Track; import com.mapswithme.maps.location.LocationHelper; -import com.mapswithme.util.UiUtils; +import com.mapswithme.util.Graphics; import java.util.ArrayList; import java.util.List; @@ -204,7 +203,7 @@ public class BookmarkListAdapter extends BaseAdapter void setIcon(Track trk) { - final Drawable circle = UiUtils.drawCircle(trk.getColor(), R.dimen.track_circle_size, mActivity.getResources()); + final Drawable circle = Graphics.drawCircle(trk.getColor(), R.dimen.track_circle_size, mActivity.getResources()); icon.setImageDrawable(circle); } diff --git a/android/src/com/mapswithme/maps/search/CachedResults.java b/android/src/com/mapswithme/maps/search/CachedResults.java new file mode 100644 index 0000000000..a750bfc23a --- /dev/null +++ b/android/src/com/mapswithme/maps/search/CachedResults.java @@ -0,0 +1,37 @@ +package com.mapswithme.maps.search; + +import android.util.SparseArray; + +/** + * Helper to avoid unneccessary invocations of native functions to obtain search result objects. + */ +class CachedResults +{ + private final SearchFragment mFragment; + private final SparseArray mCache = new SparseArray<>(); + private int mCurrentQueryId = -1; + + public CachedResults(SearchFragment fragment) + { + mFragment = fragment; + } + + public SearchResult get(int position, int queryId) + { + SearchResult res = null; + if (queryId == mCurrentQueryId) + res = mCache.get(position); + else + { + mCache.clear(); + mCurrentQueryId = queryId; + } + + if (res == null) + { + res = mFragment.getUncachedResult(position, queryId); + mCache.put(position, res); + } + return res; + } +} diff --git a/android/src/com/mapswithme/maps/search/CategoriesAdapter.java b/android/src/com/mapswithme/maps/search/CategoriesAdapter.java index ce7431baea..2b19f7ec8e 100644 --- a/android/src/com/mapswithme/maps/search/CategoriesAdapter.java +++ b/android/src/com/mapswithme/maps/search/CategoriesAdapter.java @@ -2,6 +2,7 @@ package com.mapswithme.maps.search; import android.content.res.Resources; import android.content.res.TypedArray; +import android.support.v4.app.Fragment; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -20,9 +21,15 @@ public class CategoriesAdapter extends RecyclerView.Adapter +class SearchAdapter extends RecyclerView.Adapter { - private static final int RESULT_TYPE = 0; - private static final int MESSAGE_TYPE = 1; - private final SearchFragment mSearchFragment; - private final LayoutInflater mInflater; - private final Resources mResources; + private static final int TYPE_POPULATE_BUTTON = 0; + private static final int TYPE_SUGGEST = 1; + private static final int TYPE_RESULT = 2; - private static final int COUNT_NO_RESULTS = -1; - private int mResultsCount = COUNT_NO_RESULTS; - private int mResultsId; + private static final int NO_RESULTS = -1; + + protected static abstract class BaseViewHolder extends RecyclerView.ViewHolder + { + SearchResult mResult; + // Position within search results + int mOrder; + + BaseViewHolder(View view) + { + super(view); + } + + void bind(@NonNull SearchResult result, int order) + { + mResult = result; + mOrder = order; + } + } + + private class PopulateResultsViewHolder extends BaseViewHolder + { + PopulateResultsViewHolder(View view) + { + super(view); + view.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + mSearchFragment.showAllResultsOnMap(); + } + }); + } + } + + private static abstract class BaseResultViewHolder extends BaseViewHolder + { + BaseResultViewHolder(View view) + { + super(view); + view.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + processClick(mResult, mOrder); + } + }); + } + + @Override + void bind(@NonNull SearchResult result, int order) + { + super.bind(result, order); + + SpannableStringBuilder builder = new SpannableStringBuilder(result.name); + if (result.highlightRanges != null) + { + final int size = result.highlightRanges.length / 2; + int index = 0; + + for (int i = 0; i < size; i++) + { + final int start = result.highlightRanges[index++]; + final int len = result.highlightRanges[index++]; + + builder.setSpan(new StyleSpan(Typeface.BOLD), start, start + len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + getTitleView().setText(builder); + } + + abstract TextView getTitleView(); + abstract void processClick(SearchResult result, int order); + } + + private class SuggestViewHolder extends BaseResultViewHolder + { + SuggestViewHolder(View view) + { + super(view); + } + + @Override + TextView getTitleView() + { + return (TextView)itemView; + } + + @Override + void processClick(SearchResult result, int order) + { + mSearchFragment.setSearchQuery(result.suggestion); + } + } + + private class ResultViewHolder extends BaseResultViewHolder + { + final TextView mName; + final View mClosedMarker; + final TextView mDescription; + final TextView mRegion; + final TextView mDistance; + + + // FIXME: Better format based on result type + private CharSequence formatDescription(SearchResult result) + { + final SpannableStringBuilder res = new SpannableStringBuilder(result.description.featureType); + final SpannableStringBuilder tail = new SpannableStringBuilder(); + + final int stars = Math.min(result.description.stars, 5); + if (stars > 0) + { + // Colorize last dimmed stars: "★ ★ ★ ★ ★" + final SpannableStringBuilder sb = new SpannableStringBuilder("\u2605 \u2605 \u2605 \u2605 \u2605"); + if (stars < 5) + { + final int start = sb.length() - ((5 - stars) * 2 - 1); + sb.setSpan(new ForegroundColorSpan(itemView.getResources().getColor(R.color.search_star_dimmed)), + start, sb.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + + tail.append(sb); + } + else if (!TextUtils.isEmpty(result.description.cuisine)) + tail.append(result.description.cuisine); + + if (!TextUtils.isEmpty(tail)) + res.append(" \u2022 ") + .append(tail); + + return res; + } + + ResultViewHolder(View view) + { + super(view); + + mName = (TextView) view.findViewById(R.id.title); + mClosedMarker = view.findViewById(R.id.closed); + mDescription = (TextView) view.findViewById(R.id.description); + mRegion = (TextView) view.findViewById(R.id.region); + mDistance = (TextView) view.findViewById(R.id.distance); + } + + @Override + TextView getTitleView() + { + return mName; + } + + @Override + void bind(@NonNull SearchResult result, int order) + { + super.bind(result, order); + + UiUtils.showIf(result.description.closedNow, mClosedMarker); + UiUtils.setTextAndHideIfEmpty(mDescription, formatDescription(result)); + UiUtils.setTextAndHideIfEmpty(mRegion, result.description.region); + UiUtils.setTextAndHideIfEmpty(mDistance, result.description.distance); + } + + @Override + void processClick(SearchResult result, int order) + { + mSearchFragment.showSingleResultOnMap(order); + notifyDataSetChanged(); + } + } + + private final SearchFragment mSearchFragment; + private int mResultsCount = NO_RESULTS; + private int mQueryId; public SearchAdapter(SearchFragment fragment) { mSearchFragment = fragment; - mInflater = mSearchFragment.getActivity().getLayoutInflater(); - mResources = mSearchFragment.getResources(); } @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) + public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - if (viewType == RESULT_TYPE) - return new ViewHolder(mInflater.inflate(R.layout.item_search, parent, false), viewType); + final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - return new ViewHolder(mInflater.inflate(R.layout.item_search_message, parent, false), viewType); + switch (viewType) + { + case TYPE_POPULATE_BUTTON: + return new PopulateResultsViewHolder(inflater.inflate(R.layout.item_search_populate, parent, false)); + + case TYPE_SUGGEST: + return new SuggestViewHolder(inflater.inflate(R.layout.item_search_suggest, parent, false)); + + case TYPE_RESULT: + return new ResultViewHolder(inflater.inflate(R.layout.item_search_result, parent, false)); + + default: + throw new IllegalArgumentException("Unhandled view type given"); + } } @Override - public void onBindViewHolder(ViewHolder holder, int position) + public void onBindViewHolder(BaseViewHolder holder, int position) { - if (holder.getItemViewType() == RESULT_TYPE) - bindResultView(holder); - else - bindMessageView(holder); + if (showPopulateButton()) + { + if (position == 0) + return; + + position--; + } + + final SearchResult result = mSearchFragment.getResult(position, mQueryId); + if (result != null) + holder.bind(result, position); } @Override public int getItemViewType(int position) { - if (position == 0 && doShowSearchOnMapButton()) - return MESSAGE_TYPE; + if (showPopulateButton()) + { + if (position == 0) + return TYPE_POPULATE_BUTTON; - return RESULT_TYPE; + position--; + } + + final SearchResult result = mSearchFragment.getResult(position, mQueryId); + switch (result.type) + { + case SearchResult.TYPE_SUGGEST: + return TYPE_SUGGEST; + + case SearchResult.TYPE_RESULT: + return TYPE_RESULT; + + default: + throw new IllegalArgumentException("Unhandled SearchResult type"); + } } - private boolean doShowSearchOnMapButton() + boolean showPopulateButton() { - if (mResultsCount == 0) - return true; - - final SearchResult result = mSearchFragment.getResult(0, mResultsId); - return result != null && result.mType != SearchResult.TYPE_SUGGESTION; - } - - public int getPositionInResults(int position) - { - if (doShowSearchOnMapButton()) - return position - 1; - - return position; + return (mResultsCount > 0 && !mSearchFragment.isSearchRunning()); } @Override @@ -88,114 +279,25 @@ public class SearchAdapter extends RecyclerView.Adapter 0) - { - int j = 0, n = result.mHighlightRanges.length / 2; - - for (int i = 0; i < n; ++i) - { - int start = result.mHighlightRanges[j++]; - int len = result.mHighlightRanges[j++]; - - builder.setSpan(new StyleSpan(Typeface.BOLD), start, start + len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - - if (result.mType == SearchResult.TYPE_SUGGESTION) - { - builder.setSpan(new ForegroundColorSpan(mResources.getColor(R.color.text_search_suggestion)), 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - UiUtils.hide(holder.mCountry, holder.mDistance); - } - else - { - UiUtils.setTextAndHideIfEmpty(holder.mCountry, result.mCountry); - UiUtils.setTextAndHideIfEmpty(holder.mDistance, result.mDistance); - } - - UiUtils.setTextAndShow(holder.mName, builder); - UiUtils.setTextAndHideIfEmpty(holder.mType, result.mAmenity); - } + refreshData(0, 0); } - private void bindMessageView(ViewHolder holder) - { - UiUtils.setTextAndShow(holder.mName, mResources.getString(R.string.search_on_map)); - } - - /** - * Update list data. - * - * @param count total count of result - * @param resultId id to query results - */ - public void refreshData(int count, int resultId) + public void refreshData(int count, int queryId) { mResultsCount = count; - mResultsId = resultId; + mQueryId = queryId; notifyDataSetChanged(); } - - public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener - { - public View mView; - public TextView mName; - public TextView mCountry; - public TextView mDistance; - public TextView mType; - - public ViewHolder(View v, int type) - { - super(v); - - mView = v; - mView.setOnClickListener(this); - if (type == MESSAGE_TYPE) - mName = (TextView) mView; - else - { - mName = (TextView) v.findViewById(R.id.tv__search_title); - mCountry = (TextView) v.findViewById(R.id.tv__search_subtitle); - mDistance = (TextView) v.findViewById(R.id.tv__search_distance); - mType = (TextView) v.findViewById(R.id.tv__search_type); - } - } - - @Override - public void onClick(View v) - { - if (getItemViewType() == MESSAGE_TYPE) - { - Statistics.INSTANCE.trackSimpleNamedEvent(Statistics.EventName.SEARCH_ON_MAP_CLICKED); - mSearchFragment.showAllResultsOnMap(); - } - else - { - final int resIndex = getPositionInResults(getAdapterPosition()); - final SearchResult result = mSearchFragment.getResult(resIndex, mResultsId); - if (result != null) - { - if (result.mType == SearchResult.TYPE_FEATURE) - mSearchFragment.showSingleResultOnMap(resIndex); - else - mSearchFragment.setSearchQuery(result.mSuggestion); - } - } - } - } } \ No newline at end of file diff --git a/android/src/com/mapswithme/maps/search/SearchCategoriesFragment.java b/android/src/com/mapswithme/maps/search/SearchCategoriesFragment.java new file mode 100644 index 0000000000..29ab883c48 --- /dev/null +++ b/android/src/com/mapswithme/maps/search/SearchCategoriesFragment.java @@ -0,0 +1,32 @@ +package com.mapswithme.maps.search; + +import android.os.Bundle; +import android.view.View; + +import com.mapswithme.maps.base.BaseMwmRecyclerFragment; + +public class SearchCategoriesFragment extends BaseMwmRecyclerFragment implements CategoriesAdapter.OnCategorySelectedListener +{ + @Override + public void onViewCreated(View view, Bundle savedInstanceState) + { + super.onViewCreated(view, savedInstanceState); + getRecyclerView().setAdapter(new CategoriesAdapter(this)); + } + + @Override + public void onCategorySelected(String category) + { + if (!passCategory(getParentFragment(), category)) + passCategory(getActivity(), category); + } + + private boolean passCategory(Object listener, String category) + { + if (!(listener instanceof CategoriesAdapter.OnCategorySelectedListener)) + return false; + + ((CategoriesAdapter.OnCategorySelectedListener)listener).onCategorySelected(category); + return true; + } +} diff --git a/android/src/com/mapswithme/maps/search/SearchFragment.java b/android/src/com/mapswithme/maps/search/SearchFragment.java index 55b0daa0af..86c60f281a 100644 --- a/android/src/com/mapswithme/maps/search/SearchFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchFragment.java @@ -1,65 +1,238 @@ package com.mapswithme.maps.search; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.Intent; import android.location.Location; import android.os.Bundle; +import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; +import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.view.KeyEvent; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.widget.EditText; -import android.widget.ProgressBar; -import android.widget.TextView; - import com.mapswithme.country.ActiveCountryTree; import com.mapswithme.country.CountrySuggestFragment; import com.mapswithme.maps.Framework; -import com.mapswithme.maps.MwmActivity; import com.mapswithme.maps.R; -import com.mapswithme.maps.base.BaseMwmRecyclerFragment; +import com.mapswithme.maps.base.BaseMwmFragment; import com.mapswithme.maps.base.OnBackPressListener; import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.maps.sound.TtsPlayer; +import com.mapswithme.maps.widget.SearchToolbarController; 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.concurrency.UiThread; +import com.mapswithme.util.statistics.AlohaHelper; import com.mapswithme.util.statistics.Statistics; +import java.util.ArrayList; +import java.util.List; -public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnClickListener, LocationHelper.LocationListener, OnBackPressListener, NativeSearchListener + +public class SearchFragment extends BaseMwmFragment + implements LocationHelper.LocationListener, + OnBackPressListener, + NativeSearchListener, + SearchToolbarController.Container, + CategoriesAdapter.OnCategorySelectedListener { - // These constants should be equal with Java_com_mapswithme_maps_SearchFragment_nativeRunSearch routine. - private static final int NOT_FIRST_QUERY = 1; - private static final int HAS_POSITION = 2; // Make 5-step increment to leave space for middle queries. // This constant should be equal with native SearchAdapter::QUERY_STEP; private static final int QUERY_STEP = 5; - private static final int STATUS_SEARCH_LAUNCHED = 0; - private static final int STATUS_QUERY_EMPTY = 1; - private static final int STATUS_SEARCH_SKIPPED = 2; + private static final int RC_VOICE_RECOGNITION = 0xCA11; private static final double DUMMY_NORTH = -1; - // Search views - private EditText mEtSearchQuery; - private ProgressBar mPbSearch; - private View mBtnClearQuery; - private View mBtnVoice; - // Current position. - private double mLat; - private double mLon; - private int mSearchFlags; - private int mQueryId; + private static class LastPosition + { + double lat; + double lon; + boolean valid; + + public void set(double lat, double lon) + { + this.lat = lat; + this.lon = lon; + valid = true; + } + } + + private class ToolbarController extends SearchToolbarController + { + public ToolbarController(View root) + { + super(root, getActivity()); + mToolbar.setNavigationOnClickListener( + new View.OnClickListener() + { + @Override + public void onClick(View v) + { + if (getQuery().isEmpty()) + { + Utils.navigateToParent(getActivity()); + return; + } + + setQuery(""); + FloatingSearchToolbarController.cancelSearch(); + updateFrames(); + } + }); + } + + @Override + protected void onTextChanged(String query) + { + if (!isAdded()) + return; + + if (TextUtils.isEmpty(query)) + { + mSearchAdapter.clear(); + nativeClearLastQuery(); + + stopSearch(); + return; + } + + // TODO: This code only for demonstration purposes and will be removed soon + if (tryChangeMapStyle(query) || trySwitchOnTurnSound(query)) + return; + + runSearch(true); + } + + @Override + protected boolean onStartSearchClick() + { + if (!mSearchAdapter.showPopulateButton()) + return false; + + showAllResultsOnMap(); + return true; + } + + @Override + protected void onClearClick() + { + super.onClearClick(); + FloatingSearchToolbarController.cancelApiCall(); + FloatingSearchToolbarController.cancelSearch(); + } + + @Override + protected void onVoiceInputClick() + { + try + { + startActivityForResult(InputUtils.createIntentForVoiceRecognition(getString(R.string.search_map)), RC_VOICE_RECOGNITION); + } catch (ActivityNotFoundException e) + { + AlohaHelper.logException(e); + } + } + + @Override + protected void onUpClick() + { + if (TextUtils.isEmpty(getSearchQuery())) + { + super.onUpClick(); + return; + } + + mToolbarController.clear(); + } + } + + private View mResultsFrame; + private RecyclerView mResults; + private View mResultsPlaceholder; + + private View mTabsFrame; + + private SearchToolbarController mToolbarController; + private ViewPager mPager; + private SearchAdapter mSearchAdapter; - private CategoriesAdapter mCategoriesAdapter; + + private final List mAttachedRecyclers = new ArrayList<>(); + private final RecyclerView.OnScrollListener mRecyclerListener = new RecyclerView.OnScrollListener() + { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) + { + if (newState == RecyclerView.SCROLL_STATE_DRAGGING) + mToolbarController.setActive(false); + } + }; + + private final CachedResults mCachedResults = new CachedResults(this); + private final LastPosition mLastPosition = new LastPosition(); + + private int mQueryId; + private volatile boolean mSearchRunning; + + + private boolean doShowDownloadSuggest() + { + return ActiveCountryTree.getTotalDownloadedCount() == 0; + } + + private void showDownloadSuggest() + { + FragmentManager fm = getChildFragmentManager(); + String fragmentName = CountrySuggestFragment.class.getName(); + Fragment fragment = fm.findFragmentByTag(fragmentName); + + if (fragment == null || fragment.isDetached() || fragment.isRemoving()) + { + fragment = Fragment.instantiate(getActivity(), fragmentName, null); + fm.beginTransaction() + .add(R.id.inner_fragment_container, fragment, fragmentName) + .commit(); + } + } + + private void hideDownloadSuggest() + { + final FragmentManager manager = getChildFragmentManager(); + final Fragment fragment = manager.findFragmentByTag(CountrySuggestFragment.class.getName()); + if (fragment != null && !fragment.isDetached() && !fragment.isRemoving()) + manager.beginTransaction() + .remove(fragment) + .commit(); + } + + protected void updateFrames() + { + boolean active = searchActive(); + UiUtils.showIf(active, mResultsFrame); + + if (active) + hideDownloadSuggest(); + else if (doShowDownloadSuggest()) + showDownloadSuggest(); + else + hideDownloadSuggest(); + } + + private void updateResultsPlaceholder() + { + boolean show = (!mSearchRunning && + mSearchAdapter.getItemCount() == 0 && + searchActive()); + + UiUtils.showIf(show, mResultsPlaceholder); + } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) @@ -72,29 +245,39 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl { super.onViewCreated(view, savedInstanceState); - final ViewGroup root = (ViewGroup) view; - initView(root); - initAdapters(); - refreshContent(); - nativeConnectSearchListener(); - readArguments(getArguments()); - if (getToolbar() != null) - getToolbar().setNavigationOnClickListener(new View.OnClickListener() + ViewGroup root = (ViewGroup) view; + + mTabsFrame = root.findViewById(R.id.tab_frame); + mPager = (ViewPager) mTabsFrame.findViewById(R.id.pages); + mToolbarController = new ToolbarController(view); + new TabAdapter(getChildFragmentManager(), mPager, (TabLayout)root.findViewById(R.id.tabs)); + + mResultsFrame = root.findViewById(R.id.results_frame); + mResults = (RecyclerView) mResultsFrame.findViewById(R.id.recycler); + setRecyclerScrollListener(mResults); + mResultsPlaceholder = mResultsFrame.findViewById(R.id.placeholder); + + if (mSearchAdapter == null) + { + mSearchAdapter = new SearchAdapter(this); + mSearchAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override - public void onClick(View v) + public void onChanged() { - if (getSearchQuery().isEmpty()) - { - navigateUpToParent(); - return; - } - - setSearchQuery(""); - SearchToolbarController.cancelSearch(); - refreshContent(); + updateResultsPlaceholder(); } }); + } + + mResults.setLayoutManager(new LinearLayoutManager(view.getContext())); + mResults.setAdapter(mSearchAdapter); + + updateFrames(); + updateResultsPlaceholder(); + + nativeConnectSearchListener(); + readArguments(); } @Override @@ -102,8 +285,7 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl { super.onResume(); LocationHelper.INSTANCE.addLocationListener(this); - setSearchQuery(nativeGetLastQuery()); - mEtSearchQuery.requestFocus(); + mToolbarController.setActive(true); } @Override @@ -116,159 +298,31 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl @Override public void onDestroy() { + for (RecyclerView v : mAttachedRecyclers) + v.removeOnScrollListener(mRecyclerListener); + nativeDisconnectSearchListener(); super.onDestroy(); } protected String getSearchQuery() { - return mEtSearchQuery.getText().toString(); + return mToolbarController.getQuery(); } - protected void setSearchQuery(String query) + protected void setSearchQuery(String text) { - Utils.setTextAndCursorToEnd(mEtSearchQuery, query); + mToolbarController.setQuery(text); } - private void initView(View root) + protected boolean searchActive() { - 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); - - mEtSearchQuery = (EditText) root.findViewById(R.id.search_text_query); - mEtSearchQuery.addTextChangedListener(new StringUtils.SimpleTextWatcher() - { - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) - { - if (!isAdded()) - return; - - // TODO: This code only for demonstration purposes and will be removed soon - if (tryChangeMapStyle(s.toString())) - return; - - if (runSearch() == STATUS_QUERY_EMPTY) - showCategories(); - - // TODO: This code only for demonstration purposes and will be removed soon - if (trySwitchOnTurnSound(s.toString())) - return; - - if (s.length() == 0) - { - UiUtils.hide(mBtnClearQuery); - UiUtils.showIf(InputUtils.isVoiceInputSupported(getActivity()), mBtnVoice); - } - else - { - UiUtils.show(mBtnClearQuery); - UiUtils.hide(mBtnVoice); - } - } - }); - - mEtSearchQuery.setOnEditorActionListener(new TextView.OnEditorActionListener() - { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) - { - final boolean isSearchDown = (event != null) && - (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_SEARCH); - final boolean isActionSearch = (actionId == EditorInfo.IME_ACTION_SEARCH); - - if ((isSearchDown || isActionSearch) && mSearchAdapter.getItemCount() > 0) - { - Statistics.INSTANCE.trackSimpleNamedEvent(Statistics.EventName.SEARCH_KEY_CLICKED); - if (!doShowCategories()) - showAllResultsOnMap(); - return true; - } - return false; - } - }); - - getRecyclerView().addOnScrollListener(new RecyclerView.OnScrollListener() - { - @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) - { - if (newState != RecyclerView.SCROLL_STATE_DRAGGING) - return; - InputUtils.hideKeyboard(mEtSearchQuery); - InputUtils.removeFocusEditTextHack(mEtSearchQuery); - } - }); + return !getSearchQuery().isEmpty(); } - /** - * If search string is empty - show search categories. - */ - protected boolean doShowCategories() - { - return getSearchQuery().isEmpty(); - } - - private void initAdapters() - { - mSearchAdapter = new SearchAdapter(this); - mCategoriesAdapter = new CategoriesAdapter(this); - } - - private void refreshContent() - { - final RecyclerView recyclerView = getRecyclerView(); - - if (!getSearchQuery().isEmpty()) - { - hideDownloadSuggest(); - recyclerView.setAdapter(mSearchAdapter); - } - else if (doShowDownloadSuggest()) - showDownloadSuggest(); - else - { - hideDownloadSuggest(); - recyclerView.setAdapter(mCategoriesAdapter); - } - } - - private boolean doShowDownloadSuggest() - { - return ActiveCountryTree.getTotalDownloadedCount() == 0; - } - - private void showDownloadSuggest() - { - getRecyclerView().setVisibility(View.GONE); - final FragmentManager manager = getChildFragmentManager(); - final String fragmentName = CountrySuggestFragment.class.getName(); - Fragment fragment = manager.findFragmentByTag(fragmentName); - if (fragment == null || fragment.isDetached() || fragment.isRemoving()) - { - final FragmentTransaction transaction = manager.beginTransaction(); - fragment = Fragment.instantiate(getActivity(), fragmentName, null); - transaction.add(R.id.inner_fragment_container, fragment, fragmentName).commit(); - } - } - - private void hideDownloadSuggest() - { - getRecyclerView().setVisibility(View.VISIBLE); - final FragmentManager manager = getChildFragmentManager(); - final Fragment fragment = manager.findFragmentByTag(CountrySuggestFragment.class.getName()); - if (fragment != null && !fragment.isRemoving() && !fragment.isDetached()) - { - final FragmentTransaction transaction = manager.beginTransaction(); - transaction.remove(fragment).commit(); - } - } - - private void readArguments(Bundle arguments) + private void readArguments() { + Bundle arguments = getArguments(); if (arguments == null) return; @@ -279,9 +333,9 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl private void hideSearch() { - mEtSearchQuery.setText(null); - InputUtils.hideKeyboard(mEtSearchQuery); - navigateUpToParent(); + mToolbarController.clear(); + mToolbarController.setActive(false); + Utils.navigateToParent(getActivity()); } // FIXME: This code only for demonstration purposes and will be removed soon @@ -300,7 +354,7 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl // change map style for the Map activity final int mapStyle = isOld ? Framework.MAP_STYLE_LIGHT : (isDark ? Framework.MAP_STYLE_DARK : Framework.MAP_STYLE_CLEAR); - MwmActivity.setMapStyle(getActivity(), mapStyle); + Framework.setMapStyle(mapStyle); return true; } @@ -322,38 +376,33 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl protected void showSingleResultOnMap(int resultIndex) { - SearchToolbarController.cancelApiCall(); - SearchToolbarController.setQuery(""); + final String query = getSearchQuery(); + SearchRecentsTemp.INSTANCE.add(query); + + FloatingSearchToolbarController.cancelApiCall(); + FloatingSearchToolbarController.saveQuery(""); nativeShowItem(resultIndex); - InputUtils.hideKeyboard(mEtSearchQuery); - navigateUpToParent(); + Utils.navigateToParent(getActivity()); } protected void showAllResultsOnMap() { + final String query = getSearchQuery(); nativeShowAllSearchResults(); - runInteractiveSearch(getSearchQuery(), Language.getKeyboardLocale()); - SearchToolbarController.setQuery(getSearchQuery()); - navigateUpToParent(); - } + runInteractiveSearch(query, Language.getKeyboardLocale()); + FloatingSearchToolbarController.saveQuery(query); + Utils.navigateToParent(getActivity()); - private void showCategories() - { - nativeClearLastQuery(); - displaySearchProgress(false); - refreshContent(); + Statistics.INSTANCE.trackSimpleNamedEvent(Statistics.EventName.SEARCH_KEY_CLICKED); } @Override public void onLocationUpdated(final Location l) { - mSearchFlags |= HAS_POSITION; + mLastPosition.set(l.getLatitude(), l.getLongitude()); - mLat = l.getLatitude(); - mLon = l.getLongitude(); - - if (runSearch() == STATUS_SEARCH_SKIPPED) - mSearchAdapter.notifyDataSetChanged(); + if (!TextUtils.isEmpty(getSearchQuery())) + runSearch(false); } @Override @@ -362,34 +411,54 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl @Override public void onLocationError(int errorCode) {} - private int runSearch() + private void stopSearch() { - final String query = getSearchQuery(); - if (query.isEmpty()) - { - // do force search next time from empty list - mSearchFlags &= (~NOT_FIRST_QUERY); - return STATUS_QUERY_EMPTY; - } + mSearchRunning = false; + mToolbarController.showProgress(false); + updateFrames(); + updateResultsPlaceholder(); + } - final int id = mQueryId + QUERY_STEP; - if (nativeRunSearch(query, Language.getKeyboardLocale(), mLat, mLon, mSearchFlags, id)) - { - mQueryId = id; - // mark that it's not the first query already - don't do force search - mSearchFlags |= NOT_FIRST_QUERY; - displaySearchProgress(true); - mSearchAdapter.notifyDataSetChanged(); - return STATUS_SEARCH_LAUNCHED; - } - else - return STATUS_SEARCH_SKIPPED; + private void runSearch(boolean force) + { + int id = mQueryId + QUERY_STEP; + if (!nativeRunSearch(getSearchQuery(), Language.getKeyboardLocale(), id, force, mLastPosition.valid, mLastPosition.lat, mLastPosition.lon)) + return; + + mSearchRunning = true; + mQueryId = id; + mToolbarController.showProgress(true); + + updateFrames(); } @Override - public void onResultsUpdate(final int count, final int resultId) + public void onResultsUpdate(final int count, final int queryId) { - getActivity().runOnUiThread(new Runnable() + if (!mSearchRunning) + return; + + UiThread.run(new Runnable() + { + @Override + public void run() + { + if (!isAdded() || !searchActive()) + return; + + updateFrames(); + mSearchAdapter.refreshData(count, queryId); + } + }); + } + + @Override + public void onResultsEnd(int queryId) + { + if (!mSearchRunning || queryId != mQueryId) + return; + + UiThread.run(new Runnable() { @Override public void run() @@ -397,55 +466,15 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl if (!isAdded()) return; - if (doShowCategories()) - return; - - refreshContent(); - mSearchAdapter.refreshData(count, resultId); - getRecyclerView().scrollToPosition(0); + stopSearch(); } }); } @Override - public void onResultsEnd() + public void onCategorySelected(String category) { - getActivity().runOnUiThread(new Runnable() - { - @Override - public void run() - { - if (!isAdded()) - return; - - displaySearchProgress(false); - } - }); - } - - private void displaySearchProgress(boolean inProgress) - { - if (inProgress) - UiUtils.show(mPbSearch); - else - UiUtils.invisible(mPbSearch); - } - - @Override - public void onClick(View v) - { - switch (v.getId()) - { - case R.id.search_image_clear: - mEtSearchQuery.setText(null); - SearchToolbarController.cancelApiCall(); - SearchToolbarController.cancelSearch(); - break; - case R.id.search_voice_input: - final Intent vrIntent = InputUtils.createIntentForVoiceRecognition(getResources().getString(R.string.search_map)); - startActivityForResult(vrIntent, RC_VOICE_RECOGNITION); - break; - } + mToolbarController.setQuery(category); } @Override @@ -453,11 +482,11 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl { super.onActivityResult(requestCode, resultCode, data); - if ((requestCode == RC_VOICE_RECOGNITION) && (resultCode == Activity.RESULT_OK)) + if (requestCode == RC_VOICE_RECOGNITION && resultCode == Activity.RESULT_OK) { - final String result = InputUtils.getMostConfidentResult(data); - if (result != null) - mEtSearchQuery.setText(result); + String result = InputUtils.getBestRecognitionResult(data); + if (!TextUtils.isEmpty(result)) + setSearchQuery(result); } } @@ -467,23 +496,44 @@ public class SearchFragment extends BaseMwmRecyclerFragment implements View.OnCl return false; } + SearchResult getUncachedResult(int position, int queryId) + { + return nativeGetResult(position, queryId, mLastPosition.valid, mLastPosition.lat, mLastPosition.lon, DUMMY_NORTH); + } + public SearchResult getResult(int position, int queryId) { - return nativeGetResult(position, queryId, mLat, mLon, (mSearchFlags & HAS_POSITION) != 0, DUMMY_NORTH); + return mCachedResults.get(position, queryId); + } + + public void setRecyclerScrollListener(RecyclerView recycler) + { + recycler.addOnScrollListener(mRecyclerListener); + mAttachedRecyclers.add(recycler); + } + + @Override + public SearchToolbarController getController() + { + return mToolbarController; + } + + boolean isSearchRunning() + { + return mSearchRunning; } public static native void nativeShowItem(int position); public static native void nativeShowAllSearchResults(); - private static native SearchResult nativeGetResult(int position, int queryId, - double lat, double lon, boolean mode, double north); + private static native SearchResult nativeGetResult(int position, int queryId, boolean hasPosition, double lat, double lon, double north); private native void nativeConnectSearchListener(); private native void nativeDisconnectSearchListener(); - private native boolean nativeRunSearch(String s, String lang, double lat, double lon, int flags, int queryId); + private native boolean nativeRunSearch(String s, String lang, int queryId, boolean force, boolean hasPosition, double lat, double lon); private native String nativeGetLastQuery(); diff --git a/android/src/com/mapswithme/maps/search/SearchHistoryAdapter.java b/android/src/com/mapswithme/maps/search/SearchHistoryAdapter.java new file mode 100644 index 0000000000..1c4897c119 --- /dev/null +++ b/android/src/com/mapswithme/maps/search/SearchHistoryAdapter.java @@ -0,0 +1,99 @@ +package com.mapswithme.maps.search; + +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import com.mapswithme.maps.R; +import com.mapswithme.maps.widget.SearchToolbarController; + +class SearchHistoryAdapter extends RecyclerView.Adapter +{ + private static final int TYPE_ITEM = 0; + private static final int TYPE_CLEAR = 1; + + private final SearchToolbarController mSearchToolbarController; + + public static class ViewHolder extends RecyclerView.ViewHolder + { + private final TextView mText; + + public ViewHolder(View itemView) + { + super(itemView); + mText = (TextView) itemView; + } + } + + private void refresh() + { + SearchRecentsTemp.INSTANCE.refresh(); + notifyDataSetChanged(); + } + + public SearchHistoryAdapter(SearchToolbarController searchToolbarController) + { + mSearchToolbarController = searchToolbarController; + refresh(); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) + { + final ViewHolder res; + + switch (type) + { + case TYPE_ITEM: + res = new ViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_search_recent, viewGroup, false)); + res.mText.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + mSearchToolbarController.setQuery(res.mText.getText()); + } + }); + break; + + case TYPE_CLEAR: + res = new ViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_search_common, viewGroup, false)); + res.mText.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + SearchRecentsTemp.INSTANCE.clear(); + notifyDataSetChanged(); + } + }); + break; + + default: + throw new IllegalArgumentException("Unsupported ViewHolder type given"); + } + + return res; + } + + @Override + public void onBindViewHolder(ViewHolder viewHolder, int position) + { + if (getItemViewType(position) == TYPE_ITEM) + viewHolder.mText.setText(SearchRecentsTemp.INSTANCE.get(position)); + } + + @Override + public int getItemCount() + { + int res = SearchRecentsTemp.INSTANCE.getSize(); + return (res == 0 ? 0 : res + 1); + } + + @Override + public int getItemViewType(int position) + { + return (position < SearchRecentsTemp.INSTANCE.getSize() ? TYPE_ITEM : TYPE_CLEAR); + } +} diff --git a/android/src/com/mapswithme/maps/search/SearchHistoryFragment.java b/android/src/com/mapswithme/maps/search/SearchHistoryFragment.java new file mode 100644 index 0000000000..f900fed061 --- /dev/null +++ b/android/src/com/mapswithme/maps/search/SearchHistoryFragment.java @@ -0,0 +1,58 @@ +package com.mapswithme.maps.search; + +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import com.mapswithme.maps.R; +import com.mapswithme.maps.base.BaseMwmRecyclerFragment; +import com.mapswithme.maps.widget.SearchToolbarController; +import com.mapswithme.util.UiUtils; + +public class SearchHistoryFragment extends BaseMwmRecyclerFragment +{ + private View mPlaceHolder; + private SearchHistoryAdapter mAdapter; + + private void updatePlaceholder() + { + UiUtils.showIf(mAdapter.getItemCount() == 0, mPlaceHolder); + } + + @Override + protected @LayoutRes int getLayoutRes() + { + return R.layout.fragment_search_recent; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) + { + return super.onCreateView(inflater, container, savedInstanceState); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) + { + super.onViewCreated(view, savedInstanceState); + mPlaceHolder = view.findViewById(R.id.placeholder); + + if (mAdapter == null) + { + mAdapter = new SearchHistoryAdapter(((SearchToolbarController.Container) getParentFragment()).getController()); + mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() + { + @Override + public void onChanged() + { + updatePlaceholder(); + } + }); + } + + getRecyclerView().setAdapter(mAdapter); + updatePlaceholder(); + } +} diff --git a/android/src/com/mapswithme/maps/search/SearchRecentsTemp.java b/android/src/com/mapswithme/maps/search/SearchRecentsTemp.java new file mode 100644 index 0000000000..1294e4b618 --- /dev/null +++ b/android/src/com/mapswithme/maps/search/SearchRecentsTemp.java @@ -0,0 +1,55 @@ +package com.mapswithme.maps.search; + +import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.util.Pair; +import com.mapswithme.maps.Framework; +import com.mapswithme.util.Language; + +import java.util.ArrayList; +import java.util.List; + +// KILLME: Temp class to be moved into SearchEngine +enum SearchRecentsTemp +{ + INSTANCE; + + private final List mQueries = new ArrayList<>(); + + public void refresh() + { + List> pairs = new ArrayList<>(); + Framework.nativeGetRecentSearchQueries(pairs); + mQueries.clear(); + + for (Pair pair : pairs) + mQueries.add(pair.second); + } + + public int getSize() + { + return mQueries.size(); + } + + public String get(int position) + { + return mQueries.get(position); + } + + public boolean add(@NonNull String query) + { + query = query.trim(); + if (TextUtils.isEmpty(query) || mQueries.contains(query)) + return false; + + Framework.nativeAddRecentSearchQuery(Language.getKeyboardLocale(), query); + refresh(); + return true; + } + + public void clear() + { + Framework.nativeClearRecentSearchQueries(); + mQueries.clear(); + } +} diff --git a/android/src/com/mapswithme/maps/search/SearchResult.java b/android/src/com/mapswithme/maps/search/SearchResult.java index fb3bb32f77..c7e76b611e 100644 --- a/android/src/com/mapswithme/maps/search/SearchResult.java +++ b/android/src/com/mapswithme/maps/search/SearchResult.java @@ -3,39 +3,58 @@ package com.mapswithme.maps.search; /** * Class instances are created from native code. */ +@SuppressWarnings("unused") public class SearchResult { - public String mName; - public String mSuggestion; - public String mCountry; - public String mAmenity; - public String mDistance; + public static final int TYPE_SUGGEST = 0; + public static final int TYPE_RESULT = 1; - // 0 - suggestion result - // 1 - feature result - public static final int TYPE_SUGGESTION = 0; - public static final int TYPE_FEATURE = 1; + public static class Description + { + public final String featureType; + public final String region; + public final String distance; + public final String cuisine; + public final int stars; + public final boolean closedNow; - public int mType; - // consecutive pairs of indexes (each pair contains : start index, length), specifying highlighted matches of original query in result - public int[] mHighlightRanges; + public Description(String featureType, String region, String distance, String cuisine, int stars, boolean closedNow) + { + this.featureType = featureType; + this.region = region; + this.distance = distance; + this.cuisine = cuisine; + this.stars = stars; + this.closedNow = closedNow; + } + } + + + public final String name; + public final String suggestion; + + public final int type; + public final Description description; + + // Consecutive pairs of indexes (each pair contains : start index, length), specifying highlighted matches of original query in result + public final int[] highlightRanges; public SearchResult(String name, String suggestion, int[] highlightRanges) { - mName = name; - mSuggestion = suggestion; - mType = TYPE_SUGGESTION; - mHighlightRanges = highlightRanges; + this.name = name; + this.suggestion = suggestion; + description = null; + type = TYPE_SUGGEST; + + this.highlightRanges = highlightRanges; } - public SearchResult(String name, String country, String amenity, - String distance, int[] highlightRanges) + public SearchResult(String name, Description description, int[] highlightRanges) { - mName = name; - mCountry = country; - mAmenity = amenity; - mDistance = distance; - mType = TYPE_FEATURE; - mHighlightRanges = highlightRanges; + this.name = name; + suggestion = null; + type = TYPE_RESULT; + this.description = description; + this.highlightRanges = highlightRanges; } } \ No newline at end of file diff --git a/android/src/com/mapswithme/maps/search/SearchToolbarController.java b/android/src/com/mapswithme/maps/search/SearchToolbarController.java deleted file mode 100644 index d0f4655a82..0000000000 --- a/android/src/com/mapswithme/maps/search/SearchToolbarController.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.mapswithme.maps.search; - -import android.app.Activity; -import android.support.v7.widget.Toolbar; -import android.text.TextUtils; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.TextView; - -import com.mapswithme.maps.Framework; -import com.mapswithme.maps.MwmActivity; -import com.mapswithme.maps.R; -import com.mapswithme.maps.api.ParsedMwmRequest; -import com.mapswithme.util.UiUtils; - -public class SearchToolbarController implements OnClickListener -{ - private final Activity mActivity; - - private final TextView mQuery; - private final View mProgress; - private final View mVoiceInput; - private final Toolbar mToolbar; - - private static String sSavedQuery = ""; - - - public SearchToolbarController(Activity activity) - { - mActivity = activity; - mToolbar = (Toolbar) mActivity.findViewById(R.id.toolbar_search); - UiUtils.showHomeUpButton(mToolbar); - mToolbar.setNavigationOnClickListener(new OnClickListener() - { - @Override - public void onClick(View v) - { - MwmActivity.startSearch(mActivity, mQuery.getText().toString()); - cancelSearchApiAndHide(); - } - }); - View searchBox = mToolbar.findViewById(R.id.search_box); - mQuery = (TextView) searchBox.findViewById(R.id.search_text_query); - mProgress = searchBox.findViewById(R.id.search_progress); - mVoiceInput = searchBox.findViewById(R.id.search_voice_input); - - mQuery.setOnClickListener(this); - searchBox.findViewById(R.id.search_image_clear).setOnClickListener(this); - } - - public void refreshToolbar() - { - mQuery.setFocusable(false); - UiUtils.hide(mProgress, mVoiceInput); - - if (ParsedMwmRequest.hasRequest()) - { - UiUtils.appearSlidingDown(mToolbar, null); - mQuery.setText(ParsedMwmRequest.getCurrentRequest().getTitle()); - } - else if (!TextUtils.isEmpty(sSavedQuery)) - { - UiUtils.appearSlidingDown(mToolbar, null); - mQuery.setText(sSavedQuery); - } - else - hide(); - } - - @Override - public void onClick(View v) - { - final int id = v.getId(); - if (R.id.search_text_query == id) - { - final String query = mQuery.getText().toString(); - MwmActivity.startSearch(mActivity, query); - hide(); - } - else if (R.id.search_image_clear == id) - cancelSearchApiAndHide(); - else - throw new IllegalArgumentException("Unknown id"); - } - - public static void setQuery(String query) - { - sSavedQuery = (query == null) ? "" : query; - } - - public static String getQuery() - { - return sSavedQuery; - } - - public static void cancelApiCall() - { - if (ParsedMwmRequest.hasRequest()) - ParsedMwmRequest.setCurrentRequest(null); - Framework.nativeClearApiPoints(); - } - - public static void cancelSearch() - { - setQuery(null); - Framework.cleanSearchLayerOnMap(); - } - - private void cancelSearchApiAndHide() - { - cancelApiCall(); - cancelSearch(); - mQuery.setText(null); - hide(); - } - - public boolean hide() { - if (mToolbar.getVisibility() != View.VISIBLE) - return false; - - UiUtils.disappearSlidingUp(mToolbar, null); - return true; - } - - public Toolbar getToolbar() - { - return mToolbar; - } -} diff --git a/android/src/com/mapswithme/maps/search/TabAdapter.java b/android/src/com/mapswithme/maps/search/TabAdapter.java new file mode 100644 index 0000000000..079cc48da0 --- /dev/null +++ b/android/src/com/mapswithme/maps/search/TabAdapter.java @@ -0,0 +1,197 @@ +package com.mapswithme.maps.search; + +import android.content.Context; +import android.content.res.Configuration; +import android.support.design.widget.TabLayout; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.widget.TextView; + +import com.mapswithme.maps.R; +import com.mapswithme.util.Graphics; + +import java.util.ArrayList; +import java.util.List; + +class TabAdapter extends FragmentPagerAdapter +{ + enum Tab + { + HISTORY + { + @Override + public int getIconRes() + { + return R.drawable.ic_search_tab_history; + } + + @Override + public int getTitleRes() + { + return R.string.history; + } + + @Override + public Class getFragmentClass() + { + return SearchHistoryFragment.class; + } + }, + + CATEGORIES + { + @Override + public int getIconRes() + { + return R.drawable.ic_search_tab_categories; + } + + @Override + public int getTitleRes() + { + return R.string.categories; + } + + @Override + public Class getFragmentClass() + { + return SearchCategoriesFragment.class; + } + }; + + + public abstract int getIconRes(); + public abstract int getTitleRes(); + public abstract Class getFragmentClass(); + } + + // Workaround for https://code.google.com/p/android/issues/detail?id=180454 + // TODO: Remove this class and replace with TabLayout.TabLayoutOnPageChangeListener after library fix + private static class CustomTabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener + { + private final TabLayout mTabs; + private int mScrollState; + private int mPreviousScrollState; + + + public CustomTabLayoutOnPageChangeListener(TabLayout tabs) + { + mTabs = tabs; + } + + @Override + public void onPageScrollStateChanged(int state) + { + mPreviousScrollState = mScrollState; + mScrollState = state; + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) + { + boolean update = (mScrollState == ViewPager.SCROLL_STATE_DRAGGING || + (mScrollState == ViewPager.SCROLL_STATE_SETTLING && + mPreviousScrollState == ViewPager.SCROLL_STATE_DRAGGING)); + mTabs.setScrollPosition(position, positionOffset, update); + } + + @Override + public void onPageSelected(int position) + { + mTabs.getTabAt(position).select(); + } + } + + + public static final Tab[] TABS = Tab.values(); + + private final ViewPager mPager; + private final List> mClasses = new ArrayList<>(); + private final SparseArray mFragments = new SparseArray<>(); + + + public TabAdapter(FragmentManager fragmentManager, ViewPager pager, TabLayout tabs) + { + super(fragmentManager); + + for (Tab tab : TABS) + mClasses.add(tab.getFragmentClass()); + + List fragments = fragmentManager.getFragments(); + if (fragments != null) + { + // Recollect already attached fragments + for (Fragment f : fragments) + { + if (f == null) + continue; + + int idx = mClasses.indexOf(f.getClass()); + if (idx > -1) + mFragments.put(idx, f); + } + } + + mPager = pager; + mPager.setAdapter(this); + + attachTo(tabs); + } + + private void attachTo(TabLayout tabs) + { + Context context = tabs.getContext(); + LayoutInflater inflater = LayoutInflater.from(context); + boolean landscape = (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE); + + for (Tab tab : TABS) + { + TextView tabView = (TextView) inflater.inflate(R.layout.tab, tabs, false); + tabView.setText(tab.getTitleRes()); + tabView.setCompoundDrawablesWithIntrinsicBounds(landscape ? tab.getIconRes() : 0, landscape ? 0 : tab.getIconRes(), 0, 0); + Graphics.tintTextView(tabView, context.getResources().getColorStateList(R.color.text_highlight)); + + tabs.addTab(tabs.newTab().setCustomView(tabView), true); + } + + ViewPager.OnPageChangeListener listener = new CustomTabLayoutOnPageChangeListener(tabs); + mPager.addOnPageChangeListener(listener); + tabs.setOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mPager)); + listener.onPageSelected(0); + } + + @Override + public Fragment getItem(int position) + { + Fragment res = mFragments.get(position); + if (res == null || res.getClass() != mClasses.get(position)) + { + //noinspection TryWithIdenticalCatches + try + { + res = mClasses.get(position).newInstance(); + mFragments.put(position, res); + } catch (InstantiationException ignored) + {} + catch (IllegalAccessException ignored) + {} + } + + return res; + } + + @Override + public int getCount() + { + return mClasses.size(); + } + + public Tab getCurrentTab() + { + return TABS[mPager.getCurrentItem()]; + } +} diff --git a/android/src/com/mapswithme/maps/widget/FadeView.java b/android/src/com/mapswithme/maps/widget/FadeView.java index de35c4e40d..0d264ce72c 100644 --- a/android/src/com/mapswithme/maps/widget/FadeView.java +++ b/android/src/com/mapswithme/maps/widget/FadeView.java @@ -25,7 +25,6 @@ public class FadeView extends FrameLayout public void onAnimationEnd(Animator animation) { UiUtils.hide(FadeView.this); - UiUtils.clearAnimationAfterAlpha(FadeView.this); } }; diff --git a/android/src/com/mapswithme/maps/widget/HeightLimitedFrameLayout.java b/android/src/com/mapswithme/maps/widget/HeightLimitedFrameLayout.java new file mode 100644 index 0000000000..029a597f52 --- /dev/null +++ b/android/src/com/mapswithme/maps/widget/HeightLimitedFrameLayout.java @@ -0,0 +1,74 @@ +package com.mapswithme.maps.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import com.mapswithme.maps.MwmApplication; +import com.mapswithme.maps.R; +import com.mapswithme.util.UiUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * FrameLayout which presses out the children which can not be fully fit by height.
+ * Child views should be marked with {@code @string/tag_height_limited} tag. + */ +public class HeightLimitedFrameLayout extends FrameLayout +{ + private static final String TAG_LIMIT = MwmApplication.get().getString(R.string.tag_height_limited); + private final List mLimitedViews = new ArrayList<>(); + + public HeightLimitedFrameLayout(Context context, AttributeSet attrs) + { + super(context, attrs); + } + + private void collectViews(View v) + { + if (TAG_LIMIT.equals(v.getTag())) + { + mLimitedViews.add(v); + return; + } + + if (v instanceof ViewGroup) + { + ViewGroup vg = (ViewGroup) v; + for (int i = 0; i < vg.getChildCount(); i++) + collectViews(vg.getChildAt(i)); + } + } + + @Override + protected void onFinishInflate() + { + super.onFinishInflate(); + + if (!isInEditMode()) + collectViews(this); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) + { + if (isInEditMode()) + { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + return; + } + + for (View v : mLimitedViews) + UiUtils.show(v); + + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + + if (getMeasuredHeight() > MeasureSpec.getSize(heightMeasureSpec)) + for (View v : mLimitedViews) + UiUtils.hide(v); + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} diff --git a/android/src/com/mapswithme/maps/widget/SearchToolbarController.java b/android/src/com/mapswithme/maps/widget/SearchToolbarController.java new file mode 100644 index 0000000000..70cab3ae8b --- /dev/null +++ b/android/src/com/mapswithme/maps/widget/SearchToolbarController.java @@ -0,0 +1,155 @@ +package com.mapswithme.maps.widget; + +import android.app.Activity; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.TextView; +import com.mapswithme.maps.R; +import com.mapswithme.util.InputUtils; +import com.mapswithme.util.StringUtils; +import com.mapswithme.util.UiUtils; + +public class SearchToolbarController extends ToolbarController + implements View.OnClickListener +{ + public interface Container + { + SearchToolbarController getController(); + } + + private final EditText mQuery; + private final View mProgress; + private final View mClear; + private final View mVoiceInput; + + private final boolean mVoiceInputSuported = InputUtils.isVoiceInputSupported(mActivity); + + private final TextWatcher mTextWatcher = new StringUtils.SimpleTextWatcher() + { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) + { + updateButtons(TextUtils.isEmpty(s)); + SearchToolbarController.this.onTextChanged(s.toString()); + } + }; + + public SearchToolbarController(View root, Activity activity) + { + super(root, activity); + + mQuery = (EditText) mToolbar.findViewById(R.id.query); + mQuery.setOnClickListener(this); + mQuery.addTextChangedListener(mTextWatcher); + mQuery.setOnEditorActionListener(new TextView.OnEditorActionListener() + { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) + { + boolean isSearchDown = (event != null && + event.getAction() == KeyEvent.ACTION_DOWN && + event.getKeyCode() == KeyEvent.KEYCODE_SEARCH); + + boolean isSearchAction = (actionId == EditorInfo.IME_ACTION_SEARCH); + + return (isSearchDown || isSearchAction) && onStartSearchClick(); + } + }); + + mProgress = mToolbar.findViewById(R.id.progress); + + mVoiceInput = mToolbar.findViewById(R.id.voice_input); + mVoiceInput.setOnClickListener(this); + + mClear = mToolbar.findViewById(R.id.clear); + mClear.setOnClickListener(this); + + showProgress(false); + updateButtons(true); + } + + private void updateButtons(boolean queryEmpty) + { + UiUtils.showIf(queryEmpty, mVoiceInput); + UiUtils.showIf(!queryEmpty && mVoiceInputSuported, mClear); + } + + protected void onQueryClick(String query) + {} + + protected void onTextChanged(String query) + {} + + protected boolean onStartSearchClick() + { + return true; + } + + protected void onClearClick() + { + clear(); + } + + protected void onVoiceInputClick() + {} + + public String getQuery() + { + return mQuery.getText().toString().trim(); + } + + public void setQuery(CharSequence query) + { + final String text = query.toString().trim(); + mQuery.setText(text); + if (!TextUtils.isEmpty(text)) + mQuery.selectAll(); + } + + public void clear() + { + setQuery(""); + } + + public void setActive(boolean active) + { + if (active) + { + mQuery.requestFocus(); + InputUtils.showKeyboard(mQuery); + } + else + { + InputUtils.hideKeyboard(mQuery); + InputUtils.removeFocusEditTextHack(mQuery); + } + } + + public void showProgress(boolean show) + { + UiUtils.showIf(show, mProgress); + } + + @Override + public void onClick(View v) + { + switch (v.getId()) + { + case R.id.query: + onQueryClick(getQuery()); + break; + + case R.id.clear: + onClearClick(); + break; + + case R.id.voice_input: + onVoiceInputClick(); + break; + } + } +} diff --git a/android/src/com/mapswithme/maps/widget/ToolbarController.java b/android/src/com/mapswithme/maps/widget/ToolbarController.java new file mode 100644 index 0000000000..5504e5d696 --- /dev/null +++ b/android/src/com/mapswithme/maps/widget/ToolbarController.java @@ -0,0 +1,53 @@ +package com.mapswithme.maps.widget; + +import android.app.Activity; +import android.support.annotation.StringRes; +import android.support.v7.widget.Toolbar; +import android.view.View; +import com.mapswithme.maps.R; +import com.mapswithme.util.UiUtils; +import com.mapswithme.util.Utils; + +public class ToolbarController +{ + protected final Toolbar mToolbar; + protected final Activity mActivity; + + + public ToolbarController(View root, Activity activity) + { + mActivity = activity; + mToolbar = (Toolbar) root.findViewById(R.id.toolbar); + UiUtils.showHomeUpButton(mToolbar); + mToolbar.setNavigationOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + onUpClick(); + } + }); + } + + protected void onUpClick() + { + Utils.navigateToParent(mActivity); + } + + public ToolbarController setTitle(CharSequence title) + { + mToolbar.setTitle(title); + return this; + } + + public ToolbarController setTitle(@StringRes int title) + { + mToolbar.setTitle(title); + return this; + } + + public Toolbar getToolbar() + { + return mToolbar; + } +} diff --git a/android/src/com/mapswithme/util/Graphics.java b/android/src/com/mapswithme/util/Graphics.java new file mode 100644 index 0000000000..e35648e502 --- /dev/null +++ b/android/src/com/mapswithme/util/Graphics.java @@ -0,0 +1,56 @@ +package com.mapswithme.util; + +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.widget.TextView; + +public final class Graphics +{ + public static Drawable drawCircle(int color, int sizeResId, Resources res) + { + final int size = res.getDimensionPixelSize(sizeResId); + final Bitmap bmp = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + + final Paint paint = new Paint(); + paint.setColor(color); + paint.setAntiAlias(true); + + final Canvas c = new Canvas(bmp); + final float radius = size / 2.0f; + c.drawCircle(radius, radius, radius, paint); + + return new BitmapDrawable(res, bmp); + } + + public static void tintTextView(TextView view, ColorStateList tintColors) + { + view.setTextColor(tintColors); + + final Drawable[] dlist = view.getCompoundDrawables(); + for (int i = 0; i < dlist.length; i++) + dlist[i] = tintDrawable(dlist[i], tintColors); + + view.setCompoundDrawablesWithIntrinsicBounds(dlist[0], dlist[1], dlist[2], dlist[3]); + } + + public static Drawable tintDrawable(Drawable src, ColorStateList tintColors) + { + if (src == null) + return null; + + final Rect tmp = src.getBounds(); + final Drawable res = DrawableCompat.wrap(src); + DrawableCompat.setTintList(res.mutate(), tintColors); + res.setBounds(tmp); + return res; + } + + private Graphics() {} +} diff --git a/android/src/com/mapswithme/util/InputUtils.java b/android/src/com/mapswithme/util/InputUtils.java index be517d2895..8e127b7451 100644 --- a/android/src/com/mapswithme/util/InputUtils.java +++ b/android/src/com/mapswithme/util/InputUtils.java @@ -35,10 +35,9 @@ public class InputUtils } /** - * @param vrIntentResult - * @return most confident result or null if nothing is available + * Get most confident recognition result or null if nothing is available */ - public static String getMostConfidentResult(Intent vrIntentResult) + public static String getBestRecognitionResult(Intent vrIntentResult) { final ArrayList recognizedStrings = vrIntentResult.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); diff --git a/android/src/com/mapswithme/util/Language.java b/android/src/com/mapswithme/util/Language.java index d3c44cb5f9..cc1ed97527 100644 --- a/android/src/com/mapswithme/util/Language.java +++ b/android/src/com/mapswithme/util/Language.java @@ -1,7 +1,6 @@ package com.mapswithme.util; import android.content.Context; -import android.os.Build; import android.text.TextUtils; import android.util.Log; import android.view.inputmethod.InputMethodManager; @@ -25,15 +24,12 @@ public class Language // sometime it always returns the same value as getDefaultLocale() public static String getKeyboardLocale() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + final InputMethodManager imm = (InputMethodManager) MwmApplication.get().getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { - final InputMethodManager imm = (InputMethodManager) MwmApplication.get().getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) - { - final InputMethodSubtype ims = imm.getCurrentInputMethodSubtype(); - if (ims != null) - return ims.getLocale(); - } + final InputMethodSubtype ims = imm.getCurrentInputMethodSubtype(); + if (ims != null) + return ims.getLocale(); } return getDefaultLocale(); diff --git a/android/src/com/mapswithme/util/UiUtils.java b/android/src/com/mapswithme/util/UiUtils.java index eca36d5891..93ae4f2234 100644 --- a/android/src/com/mapswithme/util/UiUtils.java +++ b/android/src/com/mapswithme/util/UiUtils.java @@ -5,13 +5,7 @@ import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.provider.Settings; @@ -28,14 +22,10 @@ import android.view.ViewTreeObserver; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; -import android.widget.ImageView; import android.widget.TextView; - import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; -import static com.mapswithme.util.Utils.checkNotNull; - public final class UiUtils { private static float sScreenDensity; @@ -170,46 +160,14 @@ public final class UiUtils hide(views); } - /* - Views after alpha animations with 'setFillAfter' on 2.3 can't become GONE, until clearAnimationAfterAlpha is called. - */ - public static void clearAnimationAfterAlpha(View... views) + public static void setTextAndShow(TextView tv, CharSequence text) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) - for (final View view : views) - view.clearAnimation(); - } - - public static Drawable drawCircle(int color, int sizeResId, Resources res) - { - final int size = res.getDimensionPixelSize(sizeResId); - final Bitmap bmp = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - - final Paint paint = new Paint(); - paint.setColor(color); - paint.setAntiAlias(true); - - final Canvas c = new Canvas(bmp); - final float radius = size / 2.0f; - c.drawCircle(radius, radius, radius, paint); - - return new BitmapDrawable(res, bmp); - } - - public static TextView setTextAndShow(TextView tv, CharSequence text) - { - checkNotNull(tv); - tv.setText(text); show(tv); - - return tv; } public static void setTextAndHideIfEmpty(TextView tv, CharSequence text) { - checkNotNull(tv); - tv.setText(text); showIf(!TextUtils.isEmpty(text), tv); } diff --git a/android/src/com/mapswithme/util/Utils.java b/android/src/com/mapswithme/util/Utils.java index cfcc0620cf..6f13c5a80e 100644 --- a/android/src/com/mapswithme/util/Utils.java +++ b/android/src/com/mapswithme/util/Utils.java @@ -11,21 +11,18 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Build; +import android.support.v4.app.NavUtils; import android.text.TextUtils; import android.util.Log; import android.view.Window; -import android.widget.EditText; import android.widget.Toast; import com.mapswithme.maps.BuildConfig; import com.mapswithme.maps.MwmApplication; +import com.mapswithme.maps.activity.CustomNavigateUpListener; import com.mapswithme.util.statistics.AlohaHelper; -import java.io.Closeable; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; +import java.io.*; import java.util.List; import java.util.Locale; import java.util.Map; @@ -71,11 +68,8 @@ public class Utils } /** - * Enable to keep screen on - * Disable to let system turn it off automatically - * - * @param enable - * @param w + * Enable to keep screen on. + * Disable to let system turn it off automatically. */ public static void keepScreenOn(boolean enable, Window w) { @@ -85,22 +79,6 @@ public class Utils w.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } - public static float getAttributeDimension(Activity activity, int attr) - { - final android.util.TypedValue value = new android.util.TypedValue(); - final boolean b = activity.getTheme().resolveAttribute(attr, value, true); - assert (b); - final android.util.DisplayMetrics metrics = new android.util.DisplayMetrics(); - activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); - return value.getDimension(metrics); - } - - public static void setTextAndCursorToEnd(EditText edit, String s) - { - edit.setText(s); - edit.setSelection(s.length()); - } - public static void toastShortcut(Context context, String message) { Toast.makeText(context, message, Toast.LENGTH_LONG).show(); @@ -117,38 +95,17 @@ public class Utils return context.getPackageManager().resolveActivity(intent, 0) != null; } - public static boolean apiEqualOrGreaterThan(int api) - { - return Build.VERSION.SDK_INT >= api; - } - - public static boolean apiLowerThan(int api) - { - return Build.VERSION.SDK_INT < api; - } - public static void checkNotNull(Object object) { if (null == object) throw new NullPointerException("Argument here must not be NULL"); } - @SuppressWarnings("deprecation") public static void copyTextToClipboard(Context context, String text) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) - { - final android.text.ClipboardManager clipboard = - (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setText(text); - } - else - { - // This is different classes in different packages - final android.content.ClipboardManager clipboard = - (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - final ClipData clip = ClipData.newPlainText("maps.me: " + text, text); - clipboard.setPrimaryClip(clip); - } + final android.content.ClipboardManager clipboard = + (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + final ClipData clip = ClipData.newPlainText("maps.me: " + text, text); + clipboard.setPrimaryClip(clip); } public static String mapPrettyPrint(Map map) @@ -301,8 +258,7 @@ public class Utils try { activity.startActivity(marketIntent); - } - catch (ActivityNotFoundException e) + } catch (ActivityNotFoundException e) { AlohaHelper.logException(e); } @@ -320,10 +276,17 @@ public class Utils try { activity.startActivity(intent); - } - catch (ActivityNotFoundException e) + } catch (ActivityNotFoundException e) { AlohaHelper.logException(e); } } + + public static void navigateToParent(Activity activity) + { + if (activity instanceof CustomNavigateUpListener) + ((CustomNavigateUpListener) activity).customOnNavigateUp(); + else + NavUtils.navigateUpFromSameTask(activity); + } } diff --git a/android/src/com/mapswithme/util/statistics/Statistics.java b/android/src/com/mapswithme/util/statistics/Statistics.java index ff96030734..858c9fbf03 100644 --- a/android/src/com/mapswithme/util/statistics/Statistics.java +++ b/android/src/com/mapswithme/util/statistics/Statistics.java @@ -3,7 +3,6 @@ package com.mapswithme.util.statistics; import android.app.Activity; import android.content.Context; import android.util.Log; - import com.facebook.appevents.AppEventsLogger; import com.flurry.android.FlurryAgent; import com.mapswithme.country.ActiveCountryTree; @@ -14,13 +13,12 @@ import com.mapswithme.maps.api.ParsedMwmRequest; import com.mapswithme.util.log.Logger; import com.mapswithme.util.log.SimpleLogger; import com.mapswithme.util.log.StubLogger; +import ru.mail.android.mytracker.MRMyTracker; +import ru.mail.android.mytracker.MRMyTrackerParams; import java.util.HashMap; import java.util.Map; -import ru.mail.android.mytracker.MRMyTracker; -import ru.mail.android.mytracker.MRMyTrackerParams; - public enum Statistics { INSTANCE; diff --git a/android/src/com/nvidia/devtech/Egl10Wrapper.java b/android/src/com/nvidia/devtech/Egl10Wrapper.java index a2d4fe0ecc..1f4b6c8d9d 100644 --- a/android/src/com/nvidia/devtech/Egl10Wrapper.java +++ b/android/src/com/nvidia/devtech/Egl10Wrapper.java @@ -3,7 +3,6 @@ package com.nvidia.devtech; import android.os.Build; import android.view.SurfaceHolder; -import com.mapswithme.util.Utils; import com.mapswithme.util.log.Logger; import java.util.Arrays; @@ -83,7 +82,7 @@ public class Egl10Wrapper extends BaseEglWrapper return false; } - if (version[0] != 1 && version[1] >= 4 && Utils.apiLowerThan(Build.VERSION_CODES.JELLY_BEAN_MR1)) + if (version[0] != 1 && version[1] >= 4 && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { LogIt("Incorrect EGL wrapper choosed"); return false; diff --git a/android/src/com/nvidia/devtech/EglWrapper.java b/android/src/com/nvidia/devtech/EglWrapper.java index c876810341..315c812a81 100644 --- a/android/src/com/nvidia/devtech/EglWrapper.java +++ b/android/src/com/nvidia/devtech/EglWrapper.java @@ -2,6 +2,7 @@ package com.nvidia.devtech; import android.opengl.EGL14; import android.opengl.EGLDisplay; +import android.os.Build; import android.view.SurfaceHolder; import com.mapswithme.util.Utils; @@ -54,7 +55,7 @@ abstract public class EglWrapper static private EglVersion QueryEglVersion() { - if (Utils.apiLowerThan(android.os.Build.VERSION_CODES.JELLY_BEAN_MR1)) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return EglVersion.Egl; EglVersion result = EglVersion.NonEgl;