diff --git a/android/jni/com/mapswithme/maps/SearchEngine.cpp b/android/jni/com/mapswithme/maps/SearchEngine.cpp index 921d599b07..ad5c8e87de 100644 --- a/android/jni/com/mapswithme/maps/SearchEngine.cpp +++ b/android/jni/com/mapswithme/maps/SearchEngine.cpp @@ -4,6 +4,7 @@ #include "com/mapswithme/platform/Platform.hpp" #include "map/everywhere_search_params.hpp" +#include "map/place_page_info.hpp" #include "map/viewport_search_params.hpp" #include "search/hotels_filter.hpp" @@ -331,8 +332,8 @@ jmethodID g_mapResultCtor; booking::AvailabilityParams g_lastBookingFilterParams; -jobject ToJavaResult(Result & result, bool isLocalAdsCustomer, bool hasPosition, double lat, - double lon) +jobject ToJavaResult(Result & result, bool isLocalAdsCustomer, float ugcRating, bool hasPosition, + double lat, double lon) { JNIEnv * env = jni::GetEnv(); ::Framework * fr = g_framework->NativeFramework(); @@ -373,8 +374,13 @@ jobject ToJavaResult(Result & result, bool isLocalAdsCustomer, bool hasPosition, jni::TScopedLocalRef address(env, jni::ToJavaString(env, result.GetAddress())); jni::TScopedLocalRef dist(env, jni::ToJavaString(env, distance)); jni::TScopedLocalRef cuisine(env, jni::ToJavaString(env, result.GetCuisine())); - jni::TScopedLocalRef rating(env, jni::ToJavaString(env, result.GetHotelRating())); jni::TScopedLocalRef pricing(env, jni::ToJavaString(env, result.GetHotelApproximatePricing())); + + string ratingTmp = result.GetHotelRating(); + if (ratingTmp.empty() && ugcRating > 0.f) + ratingTmp = place_page::rating::GetRatingFormatted(ugcRating); + jni::TScopedLocalRef rating(env, jni::ToJavaString(env, ratingTmp)); + jni::TScopedLocalRef desc(env, env->NewObject(g_descriptionClass, g_descriptionConstructor, featureId.get(), featureType.get(), address.get(), dist.get(), cuisine.get(), @@ -391,7 +397,8 @@ jobject ToJavaResult(Result & result, bool isLocalAdsCustomer, bool hasPosition, } void OnResults(Results const & results, vector const & isLocalAdsCustomer, - long long timestamp, bool isMapAndTable, bool hasPosition, double lat, double lon) + vector const & ugcRatings, long long timestamp, bool isMapAndTable, + bool hasPosition, double lat, double lon) { // Ignore results from obsolete searches. if (g_queryTimestamp > timestamp) @@ -402,7 +409,7 @@ void OnResults(Results const & results, vector const & isLocalAdsCustomer, if (!results.IsEndMarker() || results.IsEndedNormal()) { jni::TScopedLocalObjectArrayRef jResults( - env, BuildSearchResults(results, isLocalAdsCustomer, hasPosition, lat, lon)); + env, BuildSearchResults(results, isLocalAdsCustomer, ugcRatings, hasPosition, lat, lon)); env->CallVoidMethod(g_javaListener, g_updateResultsId, jResults.get(), static_cast(timestamp), search::HotelsClassifier::IsHotelResults(results)); @@ -460,7 +467,8 @@ void OnBookingFilterResults(booking::AvailabilityParams const & params, } // namespace jobjectArray BuildSearchResults(Results const & results, vector const & isLocalAdsCustomer, - bool hasPosition, double lat, double lon) + vector const & ugcRatings, bool hasPosition, double lat, + double lon) { JNIEnv * env = jni::GetEnv(); @@ -470,10 +478,12 @@ jobjectArray BuildSearchResults(Results const & results, vector const & is jobjectArray const jResults = env->NewObjectArray(count, g_resultClass, nullptr); ASSERT_EQUAL(results.GetCount(), isLocalAdsCustomer.size(), ()); + ASSERT_EQUAL(results.GetCount(), ugcRatings.size(), ()); + for (int i = 0; i < count; i++) { - jni::TScopedLocalRef jRes( - env, ToJavaResult(g_results[i], isLocalAdsCustomer[i], hasPosition, lat, lon)); + jni::TScopedLocalRef jRes(env, ToJavaResult(g_results[i], isLocalAdsCustomer[i], ugcRatings[i], + hasPosition, lat, lon)); env->SetObjectArrayElement(jResults, i, jRes.get()); } return jResults; @@ -519,7 +529,7 @@ extern "C" search::EverywhereSearchParams params; params.m_query = jni::ToNativeString(env, bytes); params.m_inputLocale = jni::ToNativeString(env, lang); - params.m_onResults = bind(&OnResults, _1, _2, timestamp, false, hasPosition, lat, lon); + params.m_onResults = bind(&OnResults, _1, _2, _3, timestamp, false, hasPosition, lat, lon); params.m_hotelsFilter = g_hotelsFilterBuilder.Build(env, hotelsFilter); g_lastBookingFilterParams = g_bookingAvailabilityParamsBuilder.Build(env, bookingFilterParams); params.m_bookingFilterParams.m_params = g_lastBookingFilterParams; @@ -553,7 +563,7 @@ extern "C" search::EverywhereSearchParams eparams; eparams.m_query = vparams.m_query; eparams.m_inputLocale = vparams.m_inputLocale; - eparams.m_onResults = bind(&OnResults, _1, _2, timestamp, isMapAndTable, + eparams.m_onResults = bind(&OnResults, _1, _2, _3, timestamp, isMapAndTable, false /* hasPosition */, 0.0 /* lat */, 0.0 /* lon */); eparams.m_hotelsFilter = vparams.m_hotelsFilter; if (g_framework->NativeFramework()->SearchEverywhere(eparams)) diff --git a/android/jni/com/mapswithme/maps/SearchEngine.hpp b/android/jni/com/mapswithme/maps/SearchEngine.hpp index 949c289a32..7942b95342 100644 --- a/android/jni/com/mapswithme/maps/SearchEngine.hpp +++ b/android/jni/com/mapswithme/maps/SearchEngine.hpp @@ -8,4 +8,5 @@ jobjectArray BuildSearchResults(search::Results const & results, std::vector const & isLocalAdsCustomer, - bool hasPosition, double lat, double lon); + std::vector const & ugcRatings, bool hasPosition, double lat, + double lon); diff --git a/android/jni/com/mapswithme/maps/discovery/DiscoveryManager.cpp b/android/jni/com/mapswithme/maps/discovery/DiscoveryManager.cpp index 2118b82a3c..963218e1e3 100644 --- a/android/jni/com/mapswithme/maps/discovery/DiscoveryManager.cpp +++ b/android/jni/com/mapswithme/maps/discovery/DiscoveryManager.cpp @@ -69,9 +69,9 @@ struct DiscoveryCallback auto const lat = MercatorBounds::YToLat(viewportCenter.y); auto const lon = MercatorBounds::XToLon(viewportCenter.x); std::vector customers(results.GetCount(), false); - jni::TScopedLocalObjectArrayRef jResults(env, BuildSearchResults(results, customers, - true /* hasPosition */, - lat, lon)); + std::vector ugcRatings(results.GetCount(), -1.f); + jni::TScopedLocalObjectArrayRef jResults( + env, BuildSearchResults(results, customers, ugcRatings, true /* hasPosition */, lat, lon)); jobject discoveryManagerInstance = env->GetStaticObjectField(g_discoveryManagerClass, g_discoveryManagerInstanceField); env->CallVoidMethod(discoveryManagerInstance, g_onResultReceivedMethod, diff --git a/iphone/Maps/Core/Search/MWMSearch.h b/iphone/Maps/Core/Search/MWMSearch.h index b73e917b63..36f96d6799 100644 --- a/iphone/Maps/Core/Search/MWMSearch.h +++ b/iphone/Maps/Core/Search/MWMSearch.h @@ -22,6 +22,7 @@ class Result; + (NSUInteger)containerIndexWithRow:(NSUInteger)row; + (search::Result const &)resultWithContainerIndex:(NSUInteger)index; + (BOOL)isLocalAdsWithContainerIndex:(NSUInteger)index; ++ (CGFloat)ugcRatingWithContainerIndex:(NSUInteger)index; + (id)adWithContainerIndex:(NSUInteger)index; + (BOOL)isBookingAvailableWithContainerIndex:(NSUInteger)index; diff --git a/iphone/Maps/Core/Search/MWMSearch.mm b/iphone/Maps/Core/Search/MWMSearch.mm index a4407dc490..2b57ea788c 100644 --- a/iphone/Maps/Core/Search/MWMSearch.mm +++ b/iphone/Maps/Core/Search/MWMSearch.mm @@ -51,6 +51,7 @@ using Observers = NSHashTable; search::Results m_viewportResults; std::vector m_isLocalAdsCustomer; std::vector m_bookingAvailableFeatureIDs; + std::vector m_ugcRatings; } #pragma mark - Instance @@ -81,12 +82,14 @@ using Observers = NSHashTable; self.lastSearchTimestamp += 1; NSUInteger const timestamp = self.lastSearchTimestamp; m_everywhereParams.m_onResults = [self, timestamp](search::Results const & results, - vector const & isLocalAdsCustomer) { + vector const & isLocalAdsCustomer, + vector const & ugcRatings) { if (timestamp == self.lastSearchTimestamp) { self->m_everywhereResults = results; self->m_isLocalAdsCustomer = isLocalAdsCustomer; + self->m_ugcRatings = ugcRatings; self.suggestionsCount = results.GetSuggestsCount(); [self onSearchResultsUpdated]; @@ -218,6 +221,11 @@ using Observers = NSHashTable; return [MWMSearch manager]->m_isLocalAdsCustomer[index]; } ++ (CGFloat)ugcRatingWithContainerIndex:(NSUInteger)index +{ + return [MWMSearch manager]->m_ugcRatings[index]; +} + + (id)adWithContainerIndex:(NSUInteger)index { return [[MWMSearch manager].banners bannerAtIndex:index]; diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchCommonCell.h b/iphone/Maps/UI/Search/TableView/MWMSearchCommonCell.h index 1807591968..decf8114f9 100644 --- a/iphone/Maps/UI/Search/TableView/MWMSearchCommonCell.h +++ b/iphone/Maps/UI/Search/TableView/MWMSearchCommonCell.h @@ -6,6 +6,7 @@ - (void)config:(search::Result const &)result isLocalAds:(BOOL)isLocalAds - isAvailable:(BOOL)isAvailable; + isAvailable:(BOOL)isAvailable + ugcRating:(CGFloat)ugcRating; @end diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchCommonCell.mm b/iphone/Maps/UI/Search/TableView/MWMSearchCommonCell.mm index c443107b4d..41a50817d3 100644 --- a/iphone/Maps/UI/Search/TableView/MWMSearchCommonCell.mm +++ b/iphone/Maps/UI/Search/TableView/MWMSearchCommonCell.mm @@ -3,8 +3,11 @@ #import "MWMLocationManager.h" #include "geometry/mercator.hpp" + #include "platform/measurement_utils.hpp" +#include "map/place_page_info.hpp" + @interface MWMSearchCommonCell () @property(nonatomic) IBOutletCollection(UIImageView) NSArray * infoRatingStars; @@ -27,11 +30,14 @@ - (void)config:(search::Result const &)result isLocalAds:(BOOL)isLocalAds - isAvailable:(BOOL)isAvailable + isAvailable:(BOOL)isAvailable + ugcRating:(CGFloat)ugcRating { [super config:result]; self.typeLabel.text = @(result.GetFeatureTypeName().c_str()).capitalizedString; - auto const & ratingStr = result.GetHotelRating(); + auto ratingStr = result.GetHotelRating(); + if (ratingStr.empty() && ugcRating > 0.f) + ratingStr = place_page::rating::GetRatingFormatted(ugcRating); self.ratingLabel.text = ratingStr.empty() ? @"" : [NSString stringWithFormat:L(@"place_page_booking_rating"), ratingStr.c_str()]; diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm b/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm index 6366b747ea..dd80ad164b 100644 --- a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm +++ b/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm @@ -89,7 +89,8 @@ auto const & result = [MWMSearch resultWithContainerIndex:containerIndex]; auto const isLocalAds = [MWMSearch isLocalAdsWithContainerIndex:containerIndex]; auto const isBookingAvailable = [MWMSearch isBookingAvailableWithContainerIndex:containerIndex]; - [cell config:result isLocalAds:isLocalAds isAvailable:isBookingAvailable]; + auto const ugcRating = [MWMSearch ugcRatingWithContainerIndex:containerIndex]; + [cell config:result isLocalAds:isLocalAds isAvailable:isBookingAvailable ugcRating:ugcRating]; return cell; } case MWMSearchItemTypeMopub: diff --git a/map/everywhere_search_params.hpp b/map/everywhere_search_params.hpp index 49f8c6ce00..ec1226e4ee 100644 --- a/map/everywhere_search_params.hpp +++ b/map/everywhere_search_params.hpp @@ -15,7 +15,8 @@ namespace search struct EverywhereSearchParams { using OnResults = - std::function const & isLocalAdsCustomer)>; + std::function const & isLocalAdsCustomer, + std::vector const & ugcRatings)>; std::string m_query; std::string m_inputLocale; diff --git a/map/framework.cpp b/map/framework.cpp index c912a36fb1..607471b84c 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -3256,6 +3256,18 @@ bool Framework::IsLocalAdsCustomer(search::Result const & result) const return m_localAdsManager.Contains(result.GetFeatureID()); } +float Framework::GetUgcRating(search::Result const & result) const +{ + ASSERT(m_ugcApi, ()); + + if (result.GetResultType() != search::Result::Type::Feature) + return -1.f; + + auto const ugc = m_ugcApi->GetLoader().GetUGC(result.GetFeatureID()); + + return ugc.m_totalRating; +} + double Framework::GetMinDistanceBetweenResults() const { return m_searchMarks.GetMaxDimension(m_currentModelView); diff --git a/map/framework.hpp b/map/framework.hpp index 5c1a4db61e..13c7f84d63 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -359,6 +359,7 @@ public: boost::optional GetCurrentPosition() const override; bool ParseSearchQueryCommand(search::SearchParams const & params) override; bool IsLocalAdsCustomer(search::Result const & result) const override; + float GetUgcRating(search::Result const & result) const override; double GetMinDistanceBetweenResults() const override; private: diff --git a/map/search_api.cpp b/map/search_api.cpp index de3bd15661..7136831a64 100644 --- a/map/search_api.cpp +++ b/map/search_api.cpp @@ -176,10 +176,11 @@ bool SearchAPI::SearchEverywhere(EverywhereSearchParams const & params) p.m_onResults = EverywhereSearchCallback( static_cast(*this), - [this, params](Results const & results, vector const & isLocalAdsCustomer) { + [this, params](Results const & results, vector const & isLocalAdsCustomer, + vector const & ugcRatings) { if (params.m_onResults) - RunUITask([params, results, isLocalAdsCustomer] { - params.m_onResults(results, isLocalAdsCustomer); + RunUITask([params, results, isLocalAdsCustomer, ugcRatings] { + params.m_onResults(results, isLocalAdsCustomer, ugcRatings); }); if (results.IsEndedNormal() && !params.m_bookingFilterParams.IsEmpty()) { @@ -339,6 +340,11 @@ bool SearchAPI::IsLocalAdsCustomer(Result const & result) const return m_delegate.IsLocalAdsCustomer(result); } +float SearchAPI::GetUgcRating(search::Result const & result) const +{ + return m_delegate.GetUgcRating(result); +} + void SearchAPI::OnBookmarksCreated(vector> const & marks) { vector data; diff --git a/map/search_api.hpp b/map/search_api.hpp index 20a3d4ec65..e81783e8b9 100644 --- a/map/search_api.hpp +++ b/map/search_api.hpp @@ -94,9 +94,9 @@ public: { } - virtual void OnBookingFilterParamsUpdate(booking::AvailabilityParams const & params) - { - } + virtual void OnBookingFilterParamsUpdate(booking::AvailabilityParams const & params) {} + + virtual float GetUgcRating(search::Result const & /* result */) const { return -1.f; } }; SearchAPI(Index & index, storage::Storage const & storage, @@ -141,6 +141,7 @@ public: void ShowViewportSearchResults(bool clear, search::Results::ConstIter begin, search::Results::ConstIter end) override; bool IsLocalAdsCustomer(search::Result const & result) const override; + float GetUgcRating(search::Result const & result) const override; void OnBookmarksCreated(std::vector> const & marks); void OnBookmarksUpdated(std::vector> const & marks); diff --git a/qt/search_panel.cpp b/qt/search_panel.cpp index bb30680135..c39e619205 100644 --- a/qt/search_panel.cpp +++ b/qt/search_panel.cpp @@ -261,7 +261,8 @@ void SearchPanel::OnSearchTextChanged(QString const & str) m_params.m_query = normalized.toUtf8().constData(); auto const timestamp = ++m_timestamp; m_params.m_onResults = [this, timestamp](search::Results const & results, - vector const & /* isLocalAdsCustomer */) { + vector const & /* isLocalAdsCustomer */, + vector const & /* ugcRatings */) { GetPlatform().RunTask(Platform::Thread::Gui, bind(&SearchPanel::OnSearchResults, this, timestamp, results)); }; diff --git a/search/everywhere_search_callback.cpp b/search/everywhere_search_callback.cpp index 9b80cda188..52b9db7d9b 100644 --- a/search/everywhere_search_callback.cpp +++ b/search/everywhere_search_callback.cpp @@ -12,13 +12,18 @@ EverywhereSearchCallback::EverywhereSearchCallback(Delegate & delegate, OnResult void EverywhereSearchCallback::operator()(Results const & results) { + ASSERT_EQUAL(m_isLocalAdsCustomer.size(), m_ugcRatings.size(), ()); + auto const prevSize = m_isLocalAdsCustomer.size(); ASSERT_LESS_OR_EQUAL(prevSize, results.GetCount(), ()); for (size_t i = prevSize; i < results.GetCount(); ++i) + { m_isLocalAdsCustomer.push_back(m_delegate.IsLocalAdsCustomer(results[i])); + m_ugcRatings.push_back(m_delegate.GetUgcRating(results[i])); + } ASSERT_EQUAL(m_isLocalAdsCustomer.size(), results.GetCount(), ()); - m_onResults(results, m_isLocalAdsCustomer); + m_onResults(results, m_isLocalAdsCustomer, m_ugcRatings); } } // namespace search diff --git a/search/everywhere_search_callback.hpp b/search/everywhere_search_callback.hpp index a31e5abd02..3d9e39b31b 100644 --- a/search/everywhere_search_callback.hpp +++ b/search/everywhere_search_callback.hpp @@ -20,12 +20,14 @@ public: virtual ~Delegate() = default; virtual bool IsLocalAdsCustomer(Result const & result) const = 0; + virtual float GetUgcRating(Result const & result) const = 0; }; // The signature of the callback should be the same as EverywhereSaerchParams::OnResults, but // EverywhereSaerchParams is located in map project and we do not need dependency. using OnResults = - std::function const & isLocalAdsCustomer)>; + std::function const & isLocalAdsCustomer, + std::vector const & ugcRatings)>; EverywhereSearchCallback(Delegate & delegate, OnResults onResults); @@ -35,5 +37,6 @@ private: Delegate & m_delegate; OnResults m_onResults; std::vector m_isLocalAdsCustomer; + std::vector m_ugcRatings; }; } // namespace search diff --git a/search/intermediate_result.cpp b/search/intermediate_result.cpp index 10c5267fa8..374f42243f 100644 --- a/search/intermediate_result.cpp +++ b/search/intermediate_result.cpp @@ -1,6 +1,6 @@ -#include "intermediate_result.hpp" -#include "geometry_utils.hpp" -#include "reverse_geocoder.hpp" +#include "search/intermediate_result.hpp" +#include "search/geometry_utils.hpp" +#include "search/reverse_geocoder.hpp" #include "storage/country_info_getter.hpp" diff --git a/ugc/api.cpp b/ugc/api.cpp index 65f77af714..f9b45acde3 100644 --- a/ugc/api.cpp +++ b/ugc/api.cpp @@ -44,6 +44,11 @@ void Api::SaveUGCOnDisk() m_thread.Push([this] { SaveUGCOnDiskImpl(); }); } +Loader & Api::GetLoader() +{ + return m_loader; +} + void Api::GetUGCImpl(FeatureID const & id, UGCCallbackUnsafe const & callback) { CHECK(callback, ()); diff --git a/ugc/api.hpp b/ugc/api.hpp index 9af3ef365a..6b08de71b6 100644 --- a/ugc/api.hpp +++ b/ugc/api.hpp @@ -32,6 +32,8 @@ public: void SendingCompleted(); void SaveUGCOnDisk(); + Loader & GetLoader(); + private: void GetUGCImpl(FeatureID const & id, UGCCallbackUnsafe const & callback); Storage::SettingResult SetUGCUpdateImpl(FeatureID const & id, UGCUpdate const & ugc); diff --git a/ugc/loader.cpp b/ugc/loader.cpp index 6fa54deffb..ce57c4c5bc 100644 --- a/ugc/loader.cpp +++ b/ugc/loader.cpp @@ -21,17 +21,15 @@ UGC Loader::GetUGC(FeatureID const & featureId) if (!value.m_cont.IsExist(UGC_FILE_TAG)) return {}; - if (m_currentMwmId != featureId.m_mwmId) - { - m_currentMwmId = featureId.m_mwmId; - m_d = binary::UGCDeserializer(); - } - - auto readerPtr = value.m_cont.GetReader(UGC_FILE_TAG); - UGC ugc; - if (!m_d.Deserialize(*readerPtr.GetPtr(), featureId.m_index, ugc)) - return {}; + { + std::lock_guard lock(m_mutex); + + auto deserializer = m_deserializers[featureId.m_mwmId]; + auto readerPtr = value.m_cont.GetReader(UGC_FILE_TAG); + + deserializer.Deserialize(*readerPtr.GetPtr(), featureId.m_index, ugc); + } return ugc; } diff --git a/ugc/loader.hpp b/ugc/loader.hpp index 0491fe8027..f141e841c7 100644 --- a/ugc/loader.hpp +++ b/ugc/loader.hpp @@ -5,11 +5,15 @@ #include "indexer/mwm_set.hpp" +#include +#include + class Index; struct FeatureID; namespace ugc { +// *NOTE* This class IS thread-safe. class Loader { public: @@ -18,7 +22,7 @@ public: private: Index const & m_index; - MwmSet::MwmId m_currentMwmId; - binary::UGCDeserializer m_d; + std::map m_deserializers; + std::mutex m_mutex; }; } // namespace ugc