[android] Place Page Viator.com frame
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
75
android/jni/com/mapswithme/maps/viator/Viator.cpp
Normal file
|
@ -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<viator::Product> 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"
|
BIN
android/res/drawable-hdpi/ic_star_small.png
Executable file
After Width: | Height: | Size: 347 B |
BIN
android/res/drawable-hdpi/ic_star_small_full.png
Executable file
After Width: | Height: | Size: 610 B |
BIN
android/res/drawable-mdpi/ic_star_small.png
Executable file
After Width: | Height: | Size: 220 B |
BIN
android/res/drawable-mdpi/ic_star_small_full.png
Executable file
After Width: | Height: | Size: 372 B |
BIN
android/res/drawable-xhdpi/ic_star_small.png
Executable file
After Width: | Height: | Size: 407 B |
BIN
android/res/drawable-xhdpi/ic_star_small_full.png
Executable file
After Width: | Height: | Size: 670 B |
BIN
android/res/drawable-xxhdpi/ic_star_small.png
Executable file
After Width: | Height: | Size: 682 B |
BIN
android/res/drawable-xxhdpi/ic_star_small_full.png
Executable file
After Width: | Height: | Size: 1.2 KiB |
BIN
android/res/drawable-xxxhdpi/ic_star_small.png
Executable file
After Width: | Height: | Size: 838 B |
BIN
android/res/drawable-xxxhdpi/ic_star_small_full.png
Executable file
After Width: | Height: | Size: 1.4 KiB |
11
android/res/drawable/ic_viator_more.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="oval"
|
||||
android:height="@dimen/margin_double_plus"
|
||||
android:width="@dimen/margin_double_plus">
|
||||
<solid android:color="@color/base_accent"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:drawable="@drawable/ic_ab_right"/>
|
||||
</layer-list>
|
11
android/res/drawable/ic_viator_more_night.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="oval"
|
||||
android:height="@dimen/margin_double_plus"
|
||||
android:width="@dimen/margin_double_plus">
|
||||
<solid android:color="@color/base_accent_night"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:drawable="@drawable/ic_ab_right"/>
|
||||
</layer-list>
|
9
android/res/drawable/rating_bar_small.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/background"
|
||||
android:drawable="@drawable/ic_star_small"/>
|
||||
<item android:id="@android:id/secondaryProgress"
|
||||
android:drawable="@drawable/ic_star_small_full"/>
|
||||
<item android:id="@android:id/progress"
|
||||
android:drawable="@drawable/ic_star_small_full"/>
|
||||
</layer-list>
|
27
android/res/layout/item_viator_more.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="@dimen/viator_product_width"
|
||||
android:layout_height="@dimen/viator_product_height"
|
||||
android:foreground="?clickableBackground">
|
||||
<ImageView
|
||||
android:id="@+id/iv__image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:src="?viatorMore"/>
|
||||
<TextView
|
||||
android:id="@+id/tv__title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_half_plus"
|
||||
android:layout_below="@id/iv__image"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:textAppearance="@style/MwmTextAppearance.Body2"
|
||||
android:fontFamily="@string/robotoMedium"
|
||||
android:textColor="?colorAccent"
|
||||
android:textAllCaps="true"
|
||||
tools:text="More"
|
||||
tools:targetApi="jelly_bean"/>
|
||||
</RelativeLayout>
|
72
android/res/layout/item_viator_product.xml
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.CardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<RelativeLayout
|
||||
android:layout_width="@dimen/viator_product_width"
|
||||
android:layout_height="@dimen/viator_product_height"
|
||||
android:foreground="?clickableBackground">
|
||||
<ImageView
|
||||
android:id="@+id/iv__image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/viator_product_image_height"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="#555555"/>
|
||||
<TextView
|
||||
android:id="@+id/tv__title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_half_plus"
|
||||
android:layout_marginLeft="@dimen/margin_half_plus"
|
||||
android:layout_marginRight="@dimen/margin_half_plus"
|
||||
android:layout_below="@id/iv__image"
|
||||
android:textAppearance="@style/MwmTextAppearance.Body3.Primary"
|
||||
android:maxLines="3"
|
||||
android:ellipsize="end"
|
||||
tools:text="Statue of Liberty and Ellis Island Guided Tour"/>
|
||||
<TextView
|
||||
android:id="@+id/tv__duration"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_quarter"
|
||||
android:layout_marginLeft="@dimen/margin_half_plus"
|
||||
android:layout_marginRight="@dimen/margin_half_plus"
|
||||
android:layout_above="@+id/rb__rate"
|
||||
android:textAppearance="@style/MwmTextAppearance.Body4"
|
||||
android:maxLines="1"
|
||||
tools:text="4 hours"/>
|
||||
<RatingBar
|
||||
android:id="@id/rb__rate"
|
||||
style="@style/MwmWidget.RatingBarSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/margin_half_plus"
|
||||
android:layout_marginStart="@dimen/margin_half_plus"
|
||||
android:layout_marginBottom="@dimen/margin_half"
|
||||
android:layout_marginTop="@dimen/margin_quarter_plus"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:numStars="5"
|
||||
android:stepSize="1"
|
||||
tools:rating="3"/>
|
||||
<TextView
|
||||
android:id="@+id/tv__price"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/margin_half"
|
||||
android:layout_marginRight="@dimen/margin_half_plus"
|
||||
android:layout_marginEnd="@dimen/margin_half_plus"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_toRightOf="@id/rb__rate"
|
||||
android:layout_toEndOf="@id/rb__rate"
|
||||
android:gravity="end"
|
||||
android:textAppearance="@style/MwmTextAppearance.Body4"
|
||||
android:textColor="?colorAccent"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
tools:text="$55"/>
|
||||
</RelativeLayout>
|
||||
</android.support.v7.widget.CardView>
|
|
@ -17,6 +17,8 @@
|
|||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="@dimen/margin_half"/>
|
||||
|
||||
<include layout="@layout/place_page_viator"/>
|
||||
|
||||
<include
|
||||
layout="@layout/divider_horizontal"/>
|
||||
|
||||
|
|
50
android/res/layout/place_page_viator.xml
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/ll__place_viator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/viator_height"
|
||||
android:orientation="vertical"
|
||||
android:clipToPadding="false"
|
||||
android:minHeight="@dimen/placepage_hotel_gallery_height"
|
||||
android:visibility="gone"
|
||||
tools:background="#20FF0000"
|
||||
tools:visibility="visible">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/height_block_base"
|
||||
android:paddingLeft="@dimen/margin_base"
|
||||
android:paddingRight="@dimen/margin_base">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:textAppearance="@style/MwmTextAppearance.Body3"
|
||||
android:fontFamily="@string/robotoMedium"
|
||||
android:text="Attractions and tours"
|
||||
tools:targetApi="jelly_bean"/>
|
||||
<TextView
|
||||
android:id="@+id/btn__viator_more"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:textAppearance="@style/MwmTextAppearance.Body3"
|
||||
android:fontFamily="@string/robotoMedium"
|
||||
android:textColor="?colorAccent"
|
||||
android:textAllCaps="true"
|
||||
android:text="@string/placepage_more_button"
|
||||
android:background="?clickableBackground"
|
||||
tools:targetApi="jelly_bean"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rv__viator_products"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="@dimen/margin_base_plus"
|
||||
tools:listitem="@layout/item_viator_product"/>
|
||||
</LinearLayout>
|
|
@ -198,4 +198,10 @@
|
|||
<dimen name="permissions_details_width">@dimen/match_parent</dimen>
|
||||
<dimen name="permissions_details_icon_size">40dp</dimen>
|
||||
<dimen name="info_page_image_size">240dp</dimen>
|
||||
|
||||
<!-- Viator-->
|
||||
<dimen name="viator_product_height">208dp</dimen>
|
||||
<dimen name="viator_product_width">160dp</dimen>
|
||||
<dimen name="viator_product_image_height">100dp</dimen>
|
||||
<dimen name="viator_height">284dp</dimen>
|
||||
</resources>
|
||||
|
|
|
@ -235,6 +235,14 @@
|
|||
<item name="android:indeterminateDrawable">@drawable/rating_bar</item>
|
||||
</style>
|
||||
|
||||
<style name="MwmWidget.RatingBarSmall" parent="android:Widget.RatingBar">
|
||||
<item name="android:progressDrawable">@drawable/rating_bar_small</item>
|
||||
<item name="android:indeterminateDrawable">@drawable/rating_bar_small</item>
|
||||
<item name="android:minHeight">12dp</item>
|
||||
<item name="android:maxHeight">12dp</item>
|
||||
<item name="android:isIndicator">true</item>
|
||||
</style>
|
||||
|
||||
<style name="MwmWidget.Counter">
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
|
|
|
@ -84,6 +84,8 @@
|
|||
<attr name="localAdSearchResBackground" format="color"/>
|
||||
|
||||
<attr name="tagBackground" format="reference"/>
|
||||
|
||||
<attr name="viatorMore" format="reference"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ThemeAttrs.NavButtons">
|
||||
|
|
|
@ -101,6 +101,8 @@
|
|||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||
|
||||
<item name="tagBackground">@drawable/bg_tag</item>
|
||||
|
||||
<item name="viatorMore">@drawable/ic_viator_more</item>
|
||||
</style>
|
||||
|
||||
<!-- Night theme -->
|
||||
|
@ -204,5 +206,7 @@
|
|||
<item name="localAdSearchResBackground">@color/search_local_ads_customer_result_night</item>
|
||||
|
||||
<item name="tagBackground">@drawable/bg_tag_night</item>
|
||||
|
||||
<item name="viatorMore">@drawable/ic_viator_more_night</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
37
android/src/com/mapswithme/maps/viator/Viator.java
Normal file
|
@ -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<ViatorListener> 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);
|
||||
}
|
||||
}
|
213
android/src/com/mapswithme/maps/viator/ViatorAdapter.java
Normal file
|
@ -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<ViatorAdapter.ViewHolder>
|
||||
{
|
||||
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<Item> 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);
|
||||
}
|
||||
}
|
177
android/src/com/mapswithme/maps/viator/ViatorProduct.java
Normal file
|
@ -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<ViatorProduct> CREATOR = new Creator<ViatorProduct>()
|
||||
{
|
||||
@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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|