diff --git a/android/jni/Android.mk b/android/jni/Android.mk index a1421e2c63..ed3750fbd4 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -116,6 +116,7 @@ LOCAL_SRC_FILES := \ com/mapswithme/util/statistics/PushwooshHelper.cpp \ com/mapswithme/util/LoggerFactory.cpp \ com/mapswithme/util/NetworkPolicy.cpp \ + com/mapswithme/maps/viator/Viator.cpp \ LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv2 -latomic -lz diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index bab9af98f6..2b98b70ebf 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -560,6 +560,17 @@ uber::RideRequestLinks Framework::GetUberLinks(string const & productId, ms::Lat return uber::Api::GetRideRequestLinks(productId, from, to); } +void Framework::RequestViatorProducts(JNIEnv * env, jobject policy, std::string const & destId, + std::string const & currency, + viator::GetTop5ProductsCallback const & callback) +{ + auto const viatorApi = m_work.GetViatorApi(ToNativeNetworkPolicy(env, policy)); + if (!viatorApi) + return; + + viatorApi->GetTop5Products(destId, currency, callback); +} + int Framework::ToDoAfterUpdate() const { return (int) m_work.ToDoAfterUpdate(); diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp index e83860979e..a1f79107ba 100644 --- a/android/jni/com/mapswithme/maps/Framework.hpp +++ b/android/jni/com/mapswithme/maps/Framework.hpp @@ -187,6 +187,10 @@ namespace android uber::ErrorCallback const & errorCallback); static uber::RideRequestLinks GetUberLinks(std::string const & productId, ms::LatLon const & from, ms::LatLon const & to); + void RequestViatorProducts(JNIEnv * env, jobject policy, std::string const & destId, + std::string const & currency, + viator::GetTop5ProductsCallback const & callback); + int ToDoAfterUpdate() const; void LogLocalAdsEvent(local_ads::EventType event, double lat, double lon, uint16_t accuracy); diff --git a/android/jni/com/mapswithme/maps/viator/Viator.cpp b/android/jni/com/mapswithme/maps/viator/Viator.cpp new file mode 100644 index 0000000000..4d2c90ce99 --- /dev/null +++ b/android/jni/com/mapswithme/maps/viator/Viator.cpp @@ -0,0 +1,75 @@ +#include "../Framework.hpp" + +#include "../../core/jni_helper.hpp" +#include "partners_api/viator_api.hpp" + +namespace +{ +jclass g_viatorClass; +jclass g_viatorProductClass; +jmethodID g_viatorProductConstructor; +jmethodID g_viatorCallback; +std::string g_lastRequestId; + +void PrepareClassRefs(JNIEnv * env) +{ + if (g_viatorClass) + return; + + g_viatorClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/viator/Viator"); + g_viatorProductClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/viator/ViatorProduct"); + g_viatorProductConstructor = + jni::GetConstructorID(env, g_viatorProductClass, + "(Ljava/lang/String;DILjava/lang/String;DLjava/lang/String;Ljava/lang/" + "String;Ljava/lang/String;Ljava/lang/String;)V"); + g_viatorCallback = + jni::GetStaticMethodID(env, g_viatorClass, "onViatorProductsReceived", + "(Ljava/lang/String;[Lcom/mapswithme/maps/viator/ViatorProduct;)V"); +} + +void OnViatorProductsReceived(std::string const & destId, + std::vector const & products) +{ + LOG(LINFO, ("Received Viator products for id = ", destId)); + GetPlatform().RunOnGuiThread([=]() { + if (g_lastRequestId != destId) + return; + + CHECK(!products.empty(), ("List of the products cannot be empty")); + + JNIEnv * env = jni::GetEnv(); + + jni::TScopedLocalRef jDestId(env, jni::ToJavaString(env, destId)); + jni::TScopedLocalRef jProducts( + env, + jni::ToJavaArray(env, g_viatorProductClass, products, [](JNIEnv * env, + viator::Product const & item) { + jni::TScopedLocalRef jTitle(env, jni::ToJavaString(env, item.m_title)); + jni::TScopedLocalRef jDuration(env, jni::ToJavaString(env, item.m_duration)); + jni::TScopedLocalRef jPriceFormatted(env, jni::ToJavaString(env, item.m_priceFormatted)); + jni::TScopedLocalRef jCurrency(env, jni::ToJavaString(env, item.m_currency)); + jni::TScopedLocalRef jPhotoUrl(env, jni::ToJavaString(env, item.m_photoUrl)); + jni::TScopedLocalRef jPageUrl(env, jni::ToJavaString(env, item.m_pageUrl)); + return env->NewObject(g_viatorProductClass, g_viatorProductConstructor, jTitle.get(), + item.m_rating, item.m_reviewCount, jDuration.get(), item.m_price, + jPriceFormatted.get(), jCurrency.get(), jPhotoUrl.get(), + jPageUrl.get()); + })); + + env->CallStaticVoidMethod(g_viatorClass, g_viatorCallback, jDestId.get(), jProducts.get()); + }); +} +} // namespace + +extern "C" { + +JNIEXPORT void JNICALL Java_com_mapswithme_maps_viator_Viator_nativeRequestViatorProducts( + JNIEnv * env, jclass clazz, jobject policy, jstring destId, jstring currency) +{ + PrepareClassRefs(env); + + g_lastRequestId = jni::ToNativeString(env, destId); + g_framework->RequestViatorProducts(env, policy, g_lastRequestId, + jni::ToNativeString(env, currency), &OnViatorProductsReceived); +} +} // extern "C" diff --git a/android/res/drawable-hdpi/ic_star_small.png b/android/res/drawable-hdpi/ic_star_small.png new file mode 100755 index 0000000000..253edd52f0 Binary files /dev/null and b/android/res/drawable-hdpi/ic_star_small.png differ diff --git a/android/res/drawable-hdpi/ic_star_small_full.png b/android/res/drawable-hdpi/ic_star_small_full.png new file mode 100755 index 0000000000..dbee20e4ae Binary files /dev/null and b/android/res/drawable-hdpi/ic_star_small_full.png differ diff --git a/android/res/drawable-mdpi/ic_star_small.png b/android/res/drawable-mdpi/ic_star_small.png new file mode 100755 index 0000000000..5f953b7b5f Binary files /dev/null and b/android/res/drawable-mdpi/ic_star_small.png differ diff --git a/android/res/drawable-mdpi/ic_star_small_full.png b/android/res/drawable-mdpi/ic_star_small_full.png new file mode 100755 index 0000000000..c60061494b Binary files /dev/null and b/android/res/drawable-mdpi/ic_star_small_full.png differ diff --git a/android/res/drawable-xhdpi/ic_star_small.png b/android/res/drawable-xhdpi/ic_star_small.png new file mode 100755 index 0000000000..a7d9feab86 Binary files /dev/null and b/android/res/drawable-xhdpi/ic_star_small.png differ diff --git a/android/res/drawable-xhdpi/ic_star_small_full.png b/android/res/drawable-xhdpi/ic_star_small_full.png new file mode 100755 index 0000000000..7e39ea7ed9 Binary files /dev/null and b/android/res/drawable-xhdpi/ic_star_small_full.png differ diff --git a/android/res/drawable-xxhdpi/ic_star_small.png b/android/res/drawable-xxhdpi/ic_star_small.png new file mode 100755 index 0000000000..5a45335c27 Binary files /dev/null and b/android/res/drawable-xxhdpi/ic_star_small.png differ diff --git a/android/res/drawable-xxhdpi/ic_star_small_full.png b/android/res/drawable-xxhdpi/ic_star_small_full.png new file mode 100755 index 0000000000..85595bc1a6 Binary files /dev/null and b/android/res/drawable-xxhdpi/ic_star_small_full.png differ diff --git a/android/res/drawable-xxxhdpi/ic_star_small.png b/android/res/drawable-xxxhdpi/ic_star_small.png new file mode 100755 index 0000000000..2891faf2e6 Binary files /dev/null and b/android/res/drawable-xxxhdpi/ic_star_small.png differ diff --git a/android/res/drawable-xxxhdpi/ic_star_small_full.png b/android/res/drawable-xxxhdpi/ic_star_small_full.png new file mode 100755 index 0000000000..d3d78aac8f Binary files /dev/null and b/android/res/drawable-xxxhdpi/ic_star_small_full.png differ diff --git a/android/res/drawable/ic_viator_more.xml b/android/res/drawable/ic_viator_more.xml new file mode 100644 index 0000000000..0f55f1fdab --- /dev/null +++ b/android/res/drawable/ic_viator_more.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/android/res/drawable/ic_viator_more_night.xml b/android/res/drawable/ic_viator_more_night.xml new file mode 100644 index 0000000000..8eafe7072c --- /dev/null +++ b/android/res/drawable/ic_viator_more_night.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/android/res/drawable/rating_bar_small.xml b/android/res/drawable/rating_bar_small.xml new file mode 100644 index 0000000000..1bc225b97b --- /dev/null +++ b/android/res/drawable/rating_bar_small.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/android/res/layout/item_viator_more.xml b/android/res/layout/item_viator_more.xml new file mode 100644 index 0000000000..96628244f4 --- /dev/null +++ b/android/res/layout/item_viator_more.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/android/res/layout/item_viator_product.xml b/android/res/layout/item_viator_product.xml new file mode 100644 index 0000000000..42e372e9b7 --- /dev/null +++ b/android/res/layout/item_viator_product.xml @@ -0,0 +1,72 @@ + + + + + + + + + + diff --git a/android/res/layout/place_page_details.xml b/android/res/layout/place_page_details.xml index 7ae16a4539..eb7441740c 100644 --- a/android/res/layout/place_page_details.xml +++ b/android/res/layout/place_page_details.xml @@ -17,6 +17,8 @@ android:layout_height="1dp" android:layout_marginBottom="@dimen/margin_half"/> + + diff --git a/android/res/layout/place_page_viator.xml b/android/res/layout/place_page_viator.xml new file mode 100644 index 0000000000..0bd519d30c --- /dev/null +++ b/android/res/layout/place_page_viator.xml @@ -0,0 +1,50 @@ + + + + + + + + + + diff --git a/android/res/values/dimens.xml b/android/res/values/dimens.xml index ad83575ee6..52bb0c5009 100644 --- a/android/res/values/dimens.xml +++ b/android/res/values/dimens.xml @@ -198,4 +198,10 @@ @dimen/match_parent 40dp 240dp + + + 208dp + 160dp + 100dp + 284dp diff --git a/android/res/values/styles.xml b/android/res/values/styles.xml index e9a14a578a..3aa7b4e1d8 100644 --- a/android/res/values/styles.xml +++ b/android/res/values/styles.xml @@ -235,6 +235,14 @@ @drawable/rating_bar + + @@ -204,5 +206,7 @@ @color/search_local_ads_customer_result_night @drawable/bg_tag_night + + @drawable/ic_viator_more_night diff --git a/android/src/com/mapswithme/maps/viator/Viator.java b/android/src/com/mapswithme/maps/viator/Viator.java new file mode 100644 index 0000000000..d2d8ec4349 --- /dev/null +++ b/android/src/com/mapswithme/maps/viator/Viator.java @@ -0,0 +1,37 @@ +package com.mapswithme.maps.viator; + +import android.support.annotation.NonNull; + +import com.mapswithme.util.NetworkPolicy; + +import java.lang.ref.WeakReference; + +public final class Viator +{ + @NonNull + private static WeakReference sViatorListener = new WeakReference<>(null); + + public static void setViatorListener(@NonNull ViatorListener listener) + { + sViatorListener = new WeakReference<>(listener); + } + + public static void onViatorProductsReceived(@NonNull String destId, + @NonNull ViatorProduct[] products) + { + ViatorListener listener = sViatorListener.get(); + if (listener != null) + listener.onViatorProductsReceived(destId, products); + } + + private Viator() {} + + public static native void nativeRequestViatorProducts(@NonNull NetworkPolicy policy, + @NonNull String destId, + @NonNull String currency); + + public interface ViatorListener + { + void onViatorProductsReceived(@NonNull String destId, @NonNull ViatorProduct[] products); + } +} diff --git a/android/src/com/mapswithme/maps/viator/ViatorAdapter.java b/android/src/com/mapswithme/maps/viator/ViatorAdapter.java new file mode 100644 index 0000000000..5f305ab431 --- /dev/null +++ b/android/src/com/mapswithme/maps/viator/ViatorAdapter.java @@ -0,0 +1,213 @@ +package com.mapswithme.maps.viator; + +import android.content.Context; +import android.support.annotation.CallSuper; +import android.support.annotation.IntDef; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.RatingBar; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.mapswithme.maps.MwmApplication; +import com.mapswithme.maps.R; +import com.mapswithme.util.UiUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +public final class ViatorAdapter extends RecyclerView.Adapter +{ + private static final int MAX_ITEMS = 5; + + private static final int TYPE_PRODUCT = 0; + private static final int TYPE_MORE = 1; + + private static final String MORE = MwmApplication.get().getString(R.string.placepage_more_button); + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ TYPE_PRODUCT, TYPE_MORE }) + private @interface ViewType{} + + @NonNull + private final List mItems; + @Nullable + private final ItemSelectedListener mListener; + + public ViatorAdapter(@NonNull ViatorProduct[] items, @NonNull String cityUrl, + @Nullable ItemSelectedListener listener) + { + mItems = new ArrayList<>(); + mListener = listener; + int size = items.length > MAX_ITEMS ? MAX_ITEMS : items.length; + for (int i = 0; i < size; i++) + { + ViatorProduct product = items[i]; + mItems.add(new Item(TYPE_PRODUCT, product.getPhotoUrl(), product.getTitle(), + product.getDuration(), product.getRating(), product.getPriceFormatted(), + product.getPageUrl())); + } + mItems.add(new Item(TYPE_MORE, null, MORE, null, 0.0, null, cityUrl)); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, @ViewType int viewType) + { + switch (viewType) + { + case TYPE_PRODUCT: + return new ProductViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_viator_product, + parent, false), this); + case TYPE_MORE: + return new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_viator_more, + parent, false), this); + } + return null; + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) + { + holder.bind(mItems.get(position)); + } + + @Override + public int getItemCount() + { + return mItems.size(); + } + + @Override + @ViewType + public int getItemViewType(int position) + { + return mItems.get(position).mType; + } + + static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener + { + @NonNull + TextView mTitle; + @NonNull + ViatorAdapter mAdapter; + + ViewHolder(@NonNull View itemView, @NonNull ViatorAdapter adapter) + { + super(itemView); + mTitle = (TextView) itemView.findViewById(R.id.tv__title); + mAdapter = adapter; + itemView.setOnClickListener(this); + } + + @CallSuper + void bind(@NonNull Item item) + { + mTitle.setText(item.mTitle); + } + + @Override + public void onClick(View v) + { + int position = getAdapterPosition(); + if (position == RecyclerView.NO_POSITION) + return; + + onItemSelected(mAdapter.mItems.get(position)); + } + + void onItemSelected(@NonNull Item item) + { + if (mAdapter.mListener != null) + mAdapter.mListener.onViatorItemSelected(item.mUrl); + } + } + + private static class ProductViewHolder extends ViewHolder + { + @NonNull + ImageView mImage; + @NonNull + TextView mDuration; + @NonNull + RatingBar mRating; + @NonNull + TextView mPrice; + + @NonNull + Context mContext; + + ProductViewHolder(@NonNull View itemView, @NonNull ViatorAdapter adapter) + { + super(itemView, adapter); + mContext = itemView.getContext(); + mImage = (ImageView) itemView.findViewById(R.id.iv__image); + mDuration = (TextView) itemView.findViewById(R.id.tv__duration); + mRating = (RatingBar) itemView.findViewById(R.id.rb__rate); + mPrice = (TextView) itemView.findViewById(R.id.tv__price); + } + + @Override + void bind(@NonNull Item item) + { + super.bind(item); + + if (item.mPhotoUrl != null) + { + Glide.with(mContext) + .load(item.mPhotoUrl) + .centerCrop() + .into(mImage); + } + + UiUtils.visibleIf(item.mDuration != null, mDuration); + mDuration.setText(item.mDuration); + UiUtils.visibleIf(item.mPrice != null, mPrice); + mPrice.setText(item.mPrice); + mRating.setRating((float) item.mRating); + } + } + + private static final class Item + { + @ViewType + private final int mType; + @Nullable + private final String mPhotoUrl; + @NonNull + private final String mTitle; + @Nullable + private final String mDuration; + private final double mRating; + @Nullable + private final String mPrice; + @NonNull + private final String mUrl; + + private Item(int type, @Nullable String photoUrl, @NonNull String title, + @Nullable String duration, double rating, @Nullable String price, + @NonNull String url) + { + mType = type; + mPhotoUrl = photoUrl; + mTitle = title; + mDuration = duration; + mRating = rating; + mPrice = price; + mUrl = url; + } + } + + public interface ItemSelectedListener + { + void onViatorItemSelected(@NonNull String url); + } +} diff --git a/android/src/com/mapswithme/maps/viator/ViatorProduct.java b/android/src/com/mapswithme/maps/viator/ViatorProduct.java new file mode 100644 index 0000000000..346703fe45 --- /dev/null +++ b/android/src/com/mapswithme/maps/viator/ViatorProduct.java @@ -0,0 +1,177 @@ +package com.mapswithme.maps.viator; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + +public final class ViatorProduct implements Parcelable +{ + @NonNull + private final String mTitle; + private final double mRating; + private final int mReviewCount; + @NonNull + private final String mDuration; + private final double mPrice; + @NonNull + private final String mPriceFormatted; + @NonNull + private final String mCurrency; + @NonNull + private final String mPhotoUrl; + @NonNull + private final String mPageUrl; + + public static final Creator CREATOR = new Creator() + { + @Override + public ViatorProduct createFromParcel(Parcel in) + { + return new ViatorProduct(in); + } + + @Override + public ViatorProduct[] newArray(int size) + { + return new ViatorProduct[size]; + } + }; + + @SuppressWarnings("unused") + public ViatorProduct(@NonNull String title, double rating, int reviewCount, + @NonNull String duration, double price, @NonNull String priceFormatted, + @NonNull String currency, @NonNull String photoUrl, @NonNull String pageUrl) + { + mTitle = title; + mRating = rating; + mReviewCount = reviewCount; + mDuration = duration; + mPrice = price; + mPriceFormatted = priceFormatted; + mCurrency = currency; + mPhotoUrl = photoUrl; + mPageUrl = pageUrl; + } + + private ViatorProduct(Parcel in) + { + mTitle = in.readString(); + mRating = in.readDouble(); + mReviewCount = in.readInt(); + mDuration = in.readString(); + mPrice = in.readDouble(); + mPriceFormatted = in.readString(); + mCurrency = in.readString(); + mPhotoUrl = in.readString(); + mPageUrl = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) + { + dest.writeString(mTitle); + dest.writeDouble(mRating); + dest.writeInt(mReviewCount); + dest.writeString(mDuration); + dest.writeDouble(mPrice); + dest.writeString(mPriceFormatted); + dest.writeString(mCurrency); + dest.writeString(mPhotoUrl); + dest.writeString(mPageUrl); + } + + @Override + public int describeContents() + { + return 0; + } + + @NonNull + String getTitle() + { + return mTitle; + } + + double getRating() + { + return mRating; + } + + int getReviewCount() + { + return mReviewCount; + } + + @NonNull + String getDuration() + { + return mDuration; + } + + double getPrice() + { + return mPrice; + } + + @NonNull + String getPriceFormatted() + { + return mPriceFormatted; + } + + @NonNull + String getCurrency() + { + return mCurrency; + } + + @NonNull + String getPhotoUrl() + { + return mPhotoUrl; + } + + @NonNull + String getPageUrl() + { + return mPageUrl; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ViatorProduct that = (ViatorProduct) o; + + if (Double.compare(that.mRating, mRating) != 0) return false; + if (mReviewCount != that.mReviewCount) return false; + if (Double.compare(that.mPrice, mPrice) != 0) return false; + if (!mTitle.equals(that.mTitle)) return false; + if (!mDuration.equals(that.mDuration)) return false; + if (!mPriceFormatted.equals(that.mPriceFormatted)) return false; + if (!mCurrency.equals(that.mCurrency)) return false; + if (!mPhotoUrl.equals(that.mPhotoUrl)) return false; + return mPageUrl.equals(that.mPageUrl); + } + + @Override + public int hashCode() + { + int result; + long temp; + result = mTitle.hashCode(); + temp = Double.doubleToLongBits(mRating); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + mReviewCount; + result = 31 * result + mDuration.hashCode(); + temp = Double.doubleToLongBits(mPrice); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + mPriceFormatted.hashCode(); + result = 31 * result + mCurrency.hashCode(); + result = 31 * result + mPhotoUrl.hashCode(); + result = 31 * result + mPageUrl.hashCode(); + return result; + } +} diff --git a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java index 34babed468..372c1248f1 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java +++ b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.location.Location; import android.os.Build; import android.support.annotation.ColorInt; @@ -64,6 +65,9 @@ import com.mapswithme.maps.gallery.Image; import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.maps.review.Review; import com.mapswithme.maps.routing.RoutingController; +import com.mapswithme.maps.viator.Viator; +import com.mapswithme.maps.viator.ViatorAdapter; +import com.mapswithme.maps.viator.ViatorProduct; import com.mapswithme.maps.widget.ArrowView; import com.mapswithme.maps.widget.BaseShadowController; import com.mapswithme.maps.widget.LineCountTextView; @@ -111,7 +115,9 @@ public class PlacePageView extends RelativeLayout NearbyAdapter.OnItemClickListener, BottomPlacePageAnimationController.OnBannerOpenListener, EditBookmarkFragment.EditBookmarkListener, - BannerController.BannerListener + BannerController.BannerListener, + Viator.ViatorListener, + ViatorAdapter.ItemSelectedListener { private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); private static final String TAG = PlacePageView.class.getSimpleName(); @@ -181,6 +187,8 @@ public class PlacePageView extends RelativeLayout private TextView mHotelRating; private TextView mHotelRatingBase; private View mHotelMore; + private View mViatorView; + private RecyclerView mRvViatorProducts; @Nullable BannerController mBannerController; @@ -402,6 +410,8 @@ public class PlacePageView extends RelativeLayout initHotelNearbyView(); initHotelRatingView(); + initViatorView(); + View bannerView = findViewById(R.id.banner); if (bannerView != null) { @@ -599,6 +609,7 @@ public class PlacePageView extends RelativeLayout Sponsored.setPriceListener(this); Sponsored.setInfoListener(this); + Viator.setViatorListener(this); } private void initHotelRatingView() @@ -793,6 +804,47 @@ public class PlacePageView extends RelativeLayout mHotelRatingBase.setText(""); } + @Override + public void onViatorProductsReceived(@NonNull String destId, @NonNull ViatorProduct[] products) + { + if (mSponsored != null) + updateViatorView(products, mSponsored.getUrl()); + } + + private void initViatorView() + { + mViatorView = findViewById(R.id.ll__place_viator); + mRvViatorProducts = (RecyclerView) mViatorView.findViewById(R.id.rv__viator_products); + mRvViatorProducts.setLayoutManager(new LinearLayoutManager(getContext(), + LinearLayoutManager.HORIZONTAL, + false)); + Drawable divider = ContextCompat.getDrawable(getContext(), R.drawable.divider_transparent_half); + mRvViatorProducts.addItemDecoration(new DividerItemDecoration(divider, true)); + mViatorView.findViewById(R.id.btn__viator_more).setOnClickListener(this); + } + + private void updateViatorView(@NonNull ViatorProduct[] products, @NonNull String cityUrl) + { + UiUtils.showIf(products.length > 0, mViatorView); + mRvViatorProducts.setAdapter(new ViatorAdapter(products, cityUrl, this)); + } + + private void hideViatorViews() + { + UiUtils.hide(mViatorView); + } + + private void clearViatorViews() + { + mRvViatorProducts.setAdapter(new ViatorAdapter(new ViatorProduct[]{}, "", null)); + } + + @Override + public void onViatorItemSelected(@NonNull String url) + { + Utils.openUrl(getContext(), url); + } + @Override public void onLineCountCalculated(boolean grater) { @@ -1065,14 +1117,20 @@ public class PlacePageView extends RelativeLayout if (mMapObject != null) { clearHotelViews(); + clearViatorViews(); if (mSponsored != null) { mSponsored.updateId(mMapObject); mSponsoredPrice = mSponsored.getPrice(); String currencyCode = Utils.getCurrencyCode(); - if (mSponsored.getType() == Sponsored.TYPE_BOOKING && mSponsored.getId() != null && !TextUtils.isEmpty(currencyCode)) - Sponsored.requestPrice(mSponsored.getId(), currencyCode, policy); + if (mSponsored.getId() != null && !TextUtils.isEmpty(currencyCode)) + { + if (mSponsored.getType() == Sponsored.TYPE_BOOKING) + Sponsored.requestPrice(mSponsored.getId(), currencyCode, policy); + else if (mSponsored.getType() == Sponsored.TYPE_VIATOR) + Viator.nativeRequestViatorProducts(policy, mSponsored.getId(), currencyCode); + } Sponsored.requestInfo(mSponsored, Locale.getDefault().toString(), policy); } @@ -1225,6 +1283,7 @@ public class PlacePageView extends RelativeLayout refreshMetadataOrHide(TextUtils.isEmpty(website) ? mapObject.getMetadata(Metadata.MetadataType.FMD_URL) : website, mWebsite, mTvWebsite); hideHotelViews(); + hideViatorViews(); } else { @@ -1675,6 +1734,10 @@ public class PlacePageView extends RelativeLayout hide(); Framework.nativeDeactivatePopup(); break; + case R.id.btn__viator_more: + if (mSponsored != null) + Utils.openUrl(getContext(), mSponsored.getUrl()); + break; } } diff --git a/android/src/com/mapswithme/maps/widget/placepage/Sponsored.java b/android/src/com/mapswithme/maps/widget/placepage/Sponsored.java index a8cd3927b3..da8de71333 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/Sponsored.java +++ b/android/src/com/mapswithme/maps/widget/placepage/Sponsored.java @@ -25,9 +25,10 @@ public final class Sponsored public static final int TYPE_BOOKING = 1; public static final int TYPE_OPENTABLE = 2; public static final int TYPE_GEOCHAT = 3; + public static final int TYPE_VIATOR = 4; @Retention(RetentionPolicy.SOURCE) - @IntDef({TYPE_NONE, TYPE_BOOKING, TYPE_OPENTABLE, TYPE_GEOCHAT}) + @IntDef({TYPE_NONE, TYPE_BOOKING, TYPE_OPENTABLE, TYPE_GEOCHAT, TYPE_VIATOR}) @interface SponsoredType {} private static class Price diff --git a/android/src/com/mapswithme/maps/widget/recycler/DividerItemDecoration.java b/android/src/com/mapswithme/maps/widget/recycler/DividerItemDecoration.java index ea3d6f0da1..2019982ab6 100644 --- a/android/src/com/mapswithme/maps/widget/recycler/DividerItemDecoration.java +++ b/android/src/com/mapswithme/maps/widget/recycler/DividerItemDecoration.java @@ -18,6 +18,7 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration @NonNull private final Drawable mDivider; private int mOrientation; + private final boolean mUsePaddingOnBorders; /** * Sole constructor. Takes in a {@link Drawable} to be used as the interior @@ -28,6 +29,20 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration public DividerItemDecoration(@NonNull Drawable divider) { mDivider = divider; + mUsePaddingOnBorders = false; + } + + /** + * Sole constructor. Takes in a {@link Drawable} to be used as the interior + * divider. + * + * @param divider A divider {@code Drawable} to be drawn on the RecyclerView + * @param usePaddingOnBorders A flag to use padding on borders + */ + public DividerItemDecoration(@NonNull Drawable divider, boolean usePaddingOnBorders) + { + mDivider = divider; + mUsePaddingOnBorders = usePaddingOnBorders; } /** @@ -61,14 +76,23 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration { super.getItemOffsets(outRect, view, parent, state); - if (parent.getChildAdapterPosition(view) == 0) + if (!mUsePaddingOnBorders && parent.getChildAdapterPosition(view) == 0) return; + boolean isLastItem = parent.getChildAdapterPosition(view) == parent.getChildCount() - 1; mOrientation = ((LinearLayoutManager) parent.getLayoutManager()).getOrientation(); if (mOrientation == LinearLayoutManager.HORIZONTAL) + { outRect.left = mDivider.getIntrinsicWidth(); + if (isLastItem && !mUsePaddingOnBorders) + outRect.right = mDivider.getIntrinsicWidth(); + } else if (mOrientation == LinearLayoutManager.VERTICAL) + { outRect.top = mDivider.getIntrinsicHeight(); + if (isLastItem && !mUsePaddingOnBorders) + outRect.bottom = mDivider.getIntrinsicHeight(); + } } /**