diff --git a/android/jni/com/mapswithme/maps/SearchEngine.cpp b/android/jni/com/mapswithme/maps/SearchEngine.cpp index 10a624a698..88480bfae3 100644 --- a/android/jni/com/mapswithme/maps/SearchEngine.cpp +++ b/android/jni/com/mapswithme/maps/SearchEngine.cpp @@ -13,6 +13,7 @@ #include "com/mapswithme/core/jni_helper.hpp" #include "com/mapswithme/platform/Platform.hpp" +#include #include #include #include @@ -242,6 +243,68 @@ private: bool m_initialized = false; } g_hotelsFilterBuilder; +class BookingAvailabilityParamsBuilder +{ +public: + void Init(JNIEnv * env) + { + if (m_initialized) + return; + + m_bookingFilterParamsClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/search/BookingFilterParams"); + m_roomClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/search/BookingFilterParams$Room"); + m_checkinMillisecId = env->GetFieldID(m_bookingFilterParamsClass, "mCheckinMillisec", "J"); + m_checkoutMillisecId = env->GetFieldID(m_bookingFilterParamsClass, "mCheckoutMillisec", "J"); + m_roomsId = env->GetFieldID(m_bookingFilterParamsClass, "mRooms", + "[Lcom/mapswithme/maps/search/BookingFilterParams$Room;"); + m_roomAdultsCountId = env->GetFieldID(m_roomClass, "mAdultsCount", "I"); + m_roomAgeOfChildId = env->GetFieldID(m_roomClass, "mAgeOfChild", "I"); + + m_initialized = true; + } + + booking::AvailabilityParams Build(JNIEnv * env, jobject bookingFilterParams) + { + booking::AvailabilityParams result; + + if (!m_initialized || bookingFilterParams == nullptr) + return result; + + jlong const jcheckin = env->GetLongField(bookingFilterParams, m_checkinMillisecId) / 1000; + result.m_checkin = booking::AvailabilityParams::Clock::from_time_t(jcheckin); + + jlong const jcheckout = env->GetLongField(bookingFilterParams, m_checkoutMillisecId) / 1000; + result.m_checkout = booking::AvailabilityParams::Clock::from_time_t(jcheckout); + + jobjectArray const jrooms = + static_cast(env->GetObjectField(bookingFilterParams, m_roomsId)); + auto const length = static_cast(env->GetArrayLength(jrooms)); + result.m_rooms.resize(length); + for (size_t i = 0; i < length; ++i) + { + jobject jroom = env->GetObjectArrayElement(jrooms, i); + + booking::AvailabilityParams::Room room; + room.SetAdultsCount(static_cast(env->GetIntField(jroom, m_roomAdultsCountId))); + room.SetAgeOfChild(static_cast(env->GetIntField(jroom, m_roomAgeOfChildId))); + result.m_rooms[i] = std::move(room); + } + + return result; + } + +private: + jclass m_bookingFilterParamsClass = nullptr; + jclass m_roomClass = nullptr; + jfieldID m_checkinMillisecId = nullptr; + jfieldID m_checkoutMillisecId = nullptr; + jfieldID m_roomsId = nullptr; + jfieldID m_roomAdultsCountId = nullptr; + jfieldID m_roomAgeOfChildId = nullptr; + + bool m_initialized = false; +} g_bookingAvailabilityParamsBuilder; + // TODO yunitsky // Do not cache search results here, after new search will be implemented. // Currently we cannot serialize FeatureID of search result properly. @@ -422,6 +485,7 @@ extern "C" g_mapResultCtor = jni::GetConstructorID(env, g_mapResultClass, "(Ljava/lang/String;Ljava/lang/String;)V"); g_hotelsFilterBuilder.Init(env); + g_bookingAvailabilityParamsBuilder.Init(env); } JNIEXPORT jboolean JNICALL Java_com_mapswithme_maps_search_SearchEngine_nativeRunSearch( @@ -442,12 +506,14 @@ extern "C" JNIEXPORT void JNICALL Java_com_mapswithme_maps_search_SearchEngine_nativeRunInteractiveSearch( JNIEnv * env, jclass clazz, jbyteArray bytes, jstring lang, jlong timestamp, - jboolean isMapAndTable, jobject hotelsFilter) + jboolean isMapAndTable, jobject hotelsFilter, jobject bookingFilterParams) { search::ViewportSearchParams vparams; vparams.m_query = jni::ToNativeString(env, bytes); vparams.m_inputLocale = jni::ToNativeString(env, lang); vparams.m_hotelsFilter = g_hotelsFilterBuilder.Build(env, hotelsFilter); + vparams.m_bookingFilterParams.m_params = + g_bookingAvailabilityParamsBuilder.Build(env, bookingFilterParams); // TODO (@alexzatsepin): set up vparams.m_onCompleted here and use // HotelsClassifier for hotel queries detection. diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index 85a540699a..0d90905923 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -586,7 +586,8 @@ public class MwmActivity extends BaseMwmFragmentActivity { SearchEngine.searchInteractive(mSearchController.getQuery(), System.nanoTime(), false /* isMapAndTable */, - mFilterController != null ? mFilterController.getFilter() : null); + mFilterController != null ? mFilterController.getFilter() : null, + null /* bookingParams */); SearchEngine.showAllResults(mSearchController.getQuery()); } diff --git a/android/src/com/mapswithme/maps/routing/SearchWheel.java b/android/src/com/mapswithme/maps/routing/SearchWheel.java index da2c6a72a7..e9c2eccec3 100644 --- a/android/src/com/mapswithme/maps/routing/SearchWheel.java +++ b/android/src/com/mapswithme/maps/routing/SearchWheel.java @@ -288,7 +288,8 @@ class SearchWheel implements View.OnClickListener private void startSearch(SearchOption searchOption) { mCurrentOption = searchOption; - SearchEngine.searchInteractive(searchOption.mSearchQuery, System.nanoTime(), false /* isMapAndTable */, null /* hotelsFilter */); + SearchEngine.searchInteractive(searchOption.mSearchQuery, System.nanoTime(), false /* isMapAndTable */, + null /* hotelsFilter */, null /* bookingParams */); refreshSearchButtonImage(); toggleSearchLayout(); diff --git a/android/src/com/mapswithme/maps/search/BookingFilterParams.java b/android/src/com/mapswithme/maps/search/BookingFilterParams.java new file mode 100644 index 0000000000..2dedd73800 --- /dev/null +++ b/android/src/com/mapswithme/maps/search/BookingFilterParams.java @@ -0,0 +1,55 @@ +package com.mapswithme.maps.search; + +import android.support.annotation.NonNull; + +class BookingFilterParams +{ + static class Room + { + // This value is corresponds to AvailabilityParams::Room::kNoChildren in core. + static final int NO_CHILDREN = -1; + + private int mAdultsCount; + private int mAgeOfChild; + + Room(int adultsCount) + { + mAdultsCount = adultsCount; + mAgeOfChild = NO_CHILDREN; + } + + Room(int adultsCount, int ageOfChild) + { + mAdultsCount = adultsCount; + mAgeOfChild = ageOfChild; + } + } + + private long mCheckinMillisec; + private long mCheckoutMillisec; + @NonNull + private Room[] mRooms; + + BookingFilterParams(long checkinMillisec, long checkoutMillisec, @NonNull Room[] rooms) + { + mCheckinMillisec = checkinMillisec; + mCheckoutMillisec = checkoutMillisec; + mRooms = rooms; + } + + public long getCheckinMillisec() + { + return mCheckinMillisec; + } + + public long getCheckoutMillisec() + { + return mCheckoutMillisec; + } + + @NonNull + public Room[] getRooms() + { + return mRooms; + } +} diff --git a/android/src/com/mapswithme/maps/search/SearchEngine.java b/android/src/com/mapswithme/maps/search/SearchEngine.java index 4d715478ec..79274ce52d 100644 --- a/android/src/com/mapswithme/maps/search/SearchEngine.java +++ b/android/src/com/mapswithme/maps/search/SearchEngine.java @@ -110,18 +110,19 @@ public enum SearchEngine implements NativeSearchListener, } public static void searchInteractive(@NonNull String query, @NonNull String locale, long timestamp, - boolean isMapAndTable, @Nullable HotelsFilter hotelsFilter) + boolean isMapAndTable, @Nullable HotelsFilter hotelsFilter, + @Nullable BookingFilterParams bookingParams) { try { - nativeRunInteractiveSearch(query.getBytes("utf-8"), locale, timestamp, isMapAndTable, hotelsFilter); + nativeRunInteractiveSearch(query.getBytes("utf-8"), locale, timestamp, isMapAndTable, hotelsFilter, bookingParams); } catch (UnsupportedEncodingException ignored) { } } public static void searchInteractive(@NonNull String query, long timestamp, boolean isMapAndTable, - @Nullable HotelsFilter hotelsFilter) + @Nullable HotelsFilter hotelsFilter, @Nullable BookingFilterParams bookingParams) { - searchInteractive(query, Language.getKeyboardLocale(), timestamp, isMapAndTable, hotelsFilter); + searchInteractive(query, Language.getKeyboardLocale(), timestamp, isMapAndTable, hotelsFilter, bookingParams); } public static void searchMaps(String query, long timestamp) @@ -177,12 +178,16 @@ public enum SearchEngine implements NativeSearchListener, /** * @param bytes utf-8 formatted bytes of query. */ - private static native boolean nativeRunSearch(byte[] bytes, String language, long timestamp, boolean hasLocation, double lat, double lon, @Nullable HotelsFilter hotelsFilter); + private static native boolean nativeRunSearch(byte[] bytes, String language, long timestamp, boolean hasLocation, + double lat, double lon, @Nullable HotelsFilter hotelsFilter); /** * @param bytes utf-8 formatted query bytes + * @param bookingParams */ - private static native void nativeRunInteractiveSearch(byte[] bytes, String language, long timestamp, boolean isMapAndTable, @Nullable HotelsFilter hotelsFilter); + private static native void nativeRunInteractiveSearch(byte[] bytes, String language, long timestamp, + boolean isMapAndTable, @Nullable HotelsFilter hotelsFilter, + BookingFilterParams bookingParams); /** * @param bytes utf-8 formatted query bytes diff --git a/android/src/com/mapswithme/maps/search/SearchFragment.java b/android/src/com/mapswithme/maps/search/SearchFragment.java index 2a3c286a82..9fc62ea46a 100644 --- a/android/src/com/mapswithme/maps/search/SearchFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchFragment.java @@ -585,7 +585,7 @@ public class SearchFragment extends BaseMwmFragment SearchEngine.searchInteractive( query, !TextUtils.isEmpty(mInitialLocale) ? mInitialLocale : com.mapswithme.util.Language.getKeyboardLocale(), - mLastQueryTimestamp, false /* isMapAndTable */, hotelsFilter); + mLastQueryTimestamp, false /* isMapAndTable */, hotelsFilter, null /* bookingParams */); SearchEngine.showAllResults(query); Utils.navigateToParent(getActivity()); @@ -629,7 +629,7 @@ public class SearchFragment extends BaseMwmFragment if (isInteractiveSearch()) { SearchEngine.searchInteractive( - getQuery(), mLastQueryTimestamp, true /* isMapAndTable */, hotelsFilter); + getQuery(), mLastQueryTimestamp, true /* isMapAndTable */, hotelsFilter, null /* bookingParams */); } else { diff --git a/partners_api/booking_availability_params.cpp b/partners_api/booking_availability_params.cpp index a87de239a6..dc27ab6cac 100644 --- a/partners_api/booking_availability_params.cpp +++ b/partners_api/booking_availability_params.cpp @@ -3,6 +3,8 @@ #include "base/string_utils.hpp" +#include + using namespace base; namespace @@ -15,6 +17,43 @@ std::string FormatTime(booking::AvailabilityParams::Time p) namespace booking { +AvailabilityParams::Room::Room(uint8_t adultsCount, int8_t ageOfChild) + : m_adultsCount(adultsCount), m_ageOfChild(ageOfChild) +{ +} + +void AvailabilityParams::Room::SetAdultsCount(uint8_t adultsCount) +{ + m_adultsCount = adultsCount; +} + +void AvailabilityParams::Room::SetAgeOfChild(int8_t ageOfChild) +{ + m_ageOfChild = ageOfChild; +} + +std::string AvailabilityParams::Room::ToString() const +{ + static std::string const kAdult = "A"; + std::vector adults(m_adultsCount, kAdult); + std::string child = m_ageOfChild == kNoChildren ? "" : "," + std::to_string(m_ageOfChild); + + std::ostringstream os; + os << strings::JoinStrings(adults, ',') << child; + + return os.str(); +} + +bool AvailabilityParams::Room::operator!=(AvailabilityParams::Room const & rhs) const +{ + return m_adultsCount != rhs.m_adultsCount || m_ageOfChild != rhs.m_ageOfChild; +} + +bool AvailabilityParams::Room::operator==(AvailabilityParams::Room const & rhs) const +{ + return !this->operator!=(rhs); +} + url::Params AvailabilityParams::Get() const { url::Params result; @@ -24,7 +63,7 @@ url::Params AvailabilityParams::Get() const result.push_back({"checkout", FormatTime(m_checkout)}); for (size_t i = 0; i < m_rooms.size(); ++i) - result.push_back({"room" + to_string(i + 1), m_rooms[i]}); + result.push_back({"room" + to_string(i + 1), m_rooms[i].ToString()}); if (m_minReviewScore != 0.0) result.push_back({"min_review_score", to_string(m_minReviewScore)}); @@ -43,7 +82,7 @@ bool AvailabilityParams::IsEmpty() const bool AvailabilityParams::operator!=(AvailabilityParams const & rhs) const { return m_checkin != rhs.m_checkin || m_checkout != rhs.m_checkout || m_rooms != rhs.m_rooms || - m_minReviewScore != m_minReviewScore || m_stars != rhs.m_stars; + m_minReviewScore != rhs.m_minReviewScore || m_stars != rhs.m_stars; } bool AvailabilityParams::operator==(AvailabilityParams const & rhs) const { diff --git a/partners_api/booking_availability_params.hpp b/partners_api/booking_availability_params.hpp index 29854c212c..cb07de5dbd 100644 --- a/partners_api/booking_availability_params.hpp +++ b/partners_api/booking_availability_params.hpp @@ -12,9 +12,29 @@ namespace booking /// [m_hotelIds], [m_checkin], [m_checkout], [m_rooms] are required. struct AvailabilityParams { - using Time = std::chrono::system_clock::time_point; + struct Room + { + static constexpr int8_t kNoChildren = -1; + Room() = default; + Room(uint8_t adultsCount, int8_t ageOfChild); + + void SetAdultsCount(uint8_t adultsCount); + void SetAgeOfChild(int8_t ageOfChild); + + std::string ToString() const; + + bool operator!=(Room const & rhs) const; + bool operator==(Room const & rhs) const; + private: + uint8_t m_adultsCount = 0; + // No children by default. + int8_t m_ageOfChild = kNoChildren; + }; + + using Clock = std::chrono::system_clock; + using Time = Clock::time_point; using Hotels = std::vector; - using Rooms = std::vector; + using Rooms = std::vector; using Stars = std::vector; base::url::Params Get() const; diff --git a/partners_api/partners_api_tests/booking_tests.cpp b/partners_api/partners_api_tests/booking_tests.cpp index 6ec377e6a6..fdf741e2b8 100644 --- a/partners_api/partners_api_tests/booking_tests.cpp +++ b/partners_api/partners_api_tests/booking_tests.cpp @@ -45,7 +45,7 @@ UNIT_TEST(Booking_HotelAvailability) { AvailabilityParams params; params.m_hotelIds = {"98251"}; - params.m_rooms = {"A,A"}; + params.m_rooms = {{2, AvailabilityParams::Room::kNoChildren}}; params.m_checkin = std::chrono::system_clock::now() + std::chrono::hours(24); params.m_checkout = std::chrono::system_clock::now() + std::chrono::hours(24 * 7); params.m_stars = {"4", "5"}; @@ -141,7 +141,7 @@ UNIT_CLASS_TEST(AsyncGuiThreadBooking, GetHotelAvailability) { AvailabilityParams params; params.m_hotelIds = {"0"}; // Internal hotel id for testing. - params.m_rooms = {"A,A"}; + params.m_rooms = {{2, AvailabilityParams::Room::kNoChildren}}; params.m_checkin = std::chrono::system_clock::now() + std::chrono::hours(24); params.m_checkout = std::chrono::system_clock::now() + std::chrono::hours(24 * 7); params.m_stars = {"4"};