[android] Added booking gallery on discovery screen

This commit is contained in:
Александр Зацепин 2018-02-02 19:47:34 +03:00 committed by Arsentiy Milchakov
parent e27293a071
commit 0a71695220
21 changed files with 399 additions and 165 deletions

View file

@ -20,39 +20,21 @@
android:layout_height="wrap_content"
android:paddingBottom="@dimen/margin_base_plus"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/thingsToDoLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/height_block_base"
android:paddingLeft="@dimen/margin_base"
android:paddingRight="@dimen/margin_base">
<TextView
android:id="@+id/thingsToDoTitle"
android:textAppearance="@style/MwmTextAppearance.Discovery.Subtitle"
style="@style/MwmWidget.Discovery.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginRight="@dimen/margin_half"
android:layout_marginEnd="@dimen/margin_half"
android:layout_toLeftOf="@+id/viatorLogo"
android:layout_toStartOf="@+id/viatorLogo"
android:text="@string/discovery_button_subtitle_things_to_do"/>
<ImageView
android:id="@id/viatorLogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_half"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:src="?viatorLogo"
android:background="?clickableBackground"/>
</RelativeLayout>
<TextView
android:id="@+id/hotelsTitle"
android:textAppearance="@style/MwmTextAppearance.Discovery.Subtitle"
style="@style/MwmWidget.Discovery.Subtitle"
android:layout_marginLeft="@dimen/margin_base"
android:layout_marginStart="@dimen/margin_base"
android:layout_marginRight="@dimen/margin_base"
android:layout_marginEnd="@dimen/margin_base"
android:layout_marginTop="@dimen/margin_half_plus"
android:layout_marginBottom="@dimen/margin_half_plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/discovery_button_subtitle_book_hotels"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/thingsToDo"
android:id="@+id/hotels"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/item_viator_product"/>
@ -67,7 +49,6 @@
android:layout_marginEnd="@dimen/margin_base"
android:layout_marginBottom="@dimen/margin_half_plus"
android:layout_marginTop="@dimen/margin_base_plus"
android:layout_gravity="start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<android.support.v7.widget.RecyclerView
@ -85,7 +66,6 @@
android:layout_marginRight="@dimen/margin_base"
android:layout_marginEnd="@dimen/margin_base"
android:layout_marginBottom="@dimen/margin_half_plus"
android:layout_gravity="start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<android.support.v7.widget.RecyclerView
@ -103,7 +83,6 @@
android:layout_marginRight="@dimen/margin_base"
android:layout_marginEnd="@dimen/margin_base"
android:layout_marginBottom="@dimen/margin_half_plus"
android:layout_gravity="start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<android.support.v7.widget.RecyclerView

View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:padding="@dimen/margin_quarter"
android:clipToPadding="false">
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="@dimen/viator_product_width"
android:layout_height="match_parent"
android:minHeight="@dimen/discovery_search_item_min_height"
android:orientation="vertical">
<LinearLayout
android:id="@+id/infoLayout"
android:paddingBottom="@dimen/margin_half_double_plus"
android:foreground="?clickableBackground"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_half_double_plus"
android:layout_marginLeft="@dimen/margin_half_plus"
android:layout_marginRight="@dimen/margin_half_plus"
style="@style/MwmWidget.Discovery.Item.Title"
android:maxLines="1"
android:minLines="1"
android:textAppearance="@style/MwmTextAppearance.Discovery.Item.Title"
tools:text="Vinnci Gala"/>
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_eighth"
android:layout_marginLeft="@dimen/margin_half_plus"
android:layout_marginRight="@dimen/margin_half_plus"
style="@style/MwmWidget.Discovery.Item.Subtitle"
android:ellipsize="middle"
android:textAppearance="@style/MwmTextAppearance.Body3"
tools:text="★ ★ ★ ★ ★"/>
<com.mapswithme.maps.widget.RatingView
android:id="@+id/ratingView"
android:layout_width="wrap_content"
android:layout_marginTop="@dimen/margin_eighth"
android:layout_marginBottom="@dimen/margin_half"
android:layout_height="@dimen/rating_view_height_small"
android:layout_marginStart="@dimen/margin_half"
android:layout_marginLeft="@dimen/margin_half"
android:paddingLeft="@dimen/margin_quarter_plus"
android:paddingStart="@dimen/margin_quarter_plus"
android:paddingRight="@dimen/margin_quarter_plus"
android:paddingEnd="@dimen/margin_quarter_plus"
android:paddingTop="@dimen/margin_quarter"
android:paddingBottom="@dimen/margin_quarter"
android:layout_gravity="center_horizontal|start"
android:textSize="@dimen/text_size_body_4"
tools:visibility="visible"/>
<TextView
android:id="@+id/distance"
android:layout_marginTop="@dimen/margin_eighth"
android:layout_marginLeft="@dimen/margin_half_plus"
android:layout_marginStart="@dimen/margin_half_plus"
android:layout_marginRight="@dimen/margin_half_plus"
android:layout_marginEnd="@dimen/margin_half_plus"
android:textAppearance="@style/MwmTextAppearance.Discovery.Item.Distance"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="950 m"/>
</LinearLayout>
<include
layout="@layout/item_discovery_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</FrameLayout>

View file

@ -9,17 +9,19 @@
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RelativeLayout
<LinearLayout
android:layout_width="@dimen/viator_product_width"
android:layout_height="wrap_content">
android:layout_height="match_parent"
android:minHeight="@dimen/discovery_search_item_min_height"
android:orientation="vertical">
<LinearLayout
android:id="@+id/infoLayout"
android:layout_alignParentTop="true"
android:paddingBottom="@dimen/margin_half_double_plus"
android:foreground="?clickableBackground"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
@ -56,8 +58,7 @@
<include
layout="@layout/item_discovery_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/infoLayout"/>
</RelativeLayout>
android:layout_height="wrap_content"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</FrameLayout>

View file

@ -233,6 +233,6 @@
<!-- Discovery-->
<dimen name="discovery_button_min_height">40dp</dimen>
<dimen name="discovery_search_item_min_height">148dp</dimen>
<dimen name="discovery_expert_item_min_height">196dp</dimen>
<dimen name="discovery_search_item_min_height">166dp</dimen>
<dimen name="discovery_expert_item_min_height">198dp</dimen>
</resources>

View file

@ -195,6 +195,7 @@
<style name="MwmTextAppearance.Discovery.Subtitle" parent="MwmTextAppearance.Body3">
<item name="android:fontFamily" tools:targetApi="jelly_bean">@string/robotoMedium</item>
<item name="android:textAllCaps">true</item>
<item name="android:gravity">start</item>
</style>
<style name="MwmTextAppearance.Discovery.Item.Title" parent="MwmTextAppearance.Body3.Primary">

View file

@ -0,0 +1,30 @@
package com.mapswithme;
import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import com.mapswithme.maps.R;
public class HotelUtils
{
@NonNull
public static CharSequence formatStars(int stars, @NonNull Resources resources)
{
if (stars <= 0)
throw new AssertionError("Start count must be > 0");
stars = Math.min(stars, 5);
// Colorize last dimmed stars
final SpannableStringBuilder sb = new SpannableStringBuilder("★ ★ ★ ★ ★");
if (stars < 5)
{
final int start = sb.length() - ((5 - stars) * 2 - 1);
sb.setSpan(new ForegroundColorSpan(resources.getColor(R.color.search_star_dimmed)),
start, sb.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
return sb;
}
}

View file

@ -29,7 +29,6 @@ import com.mapswithme.maps.gallery.Items;
import com.mapswithme.maps.gallery.impl.BaseItemSelectedListener;
import com.mapswithme.maps.gallery.impl.Factory;
import com.mapswithme.maps.search.SearchResult;
import com.mapswithme.maps.viator.ViatorProduct;
import com.mapswithme.maps.widget.PlaceholderView;
import com.mapswithme.maps.widget.ToolbarController;
import com.mapswithme.maps.widget.recycler.ItemDecoratorFactory;
@ -47,18 +46,16 @@ import static com.mapswithme.util.statistics.Destination.ROUTING;
import static com.mapswithme.util.statistics.GalleryPlacement.DISCOVERY;
import static com.mapswithme.util.statistics.GalleryType.LOCAL_EXPERTS;
import static com.mapswithme.util.statistics.GalleryType.SEARCH_ATTRACTIONS;
import static com.mapswithme.util.statistics.GalleryType.SEARCH_HOTELS;
import static com.mapswithme.util.statistics.GalleryType.SEARCH_RESTAURANTS;
import static com.mapswithme.util.statistics.GalleryType.VIATOR;
public class DiscoveryFragment extends BaseMwmToolbarFragment implements UICallback
{
private static final int ITEMS_COUNT = 5;
private static final int[] ITEM_TYPES = { DiscoveryParams.ITEM_TYPE_VIATOR,
private static final int[] ITEM_TYPES = { DiscoveryParams.ITEM_TYPE_HOTELS,
DiscoveryParams.ITEM_TYPE_ATTRACTIONS,
DiscoveryParams.ITEM_TYPE_CAFES,
DiscoveryParams.ITEM_TYPE_LOCAL_EXPERTS };
@Nullable
private BaseItemSelectedListener<Items.Item> mDefaultListener;
private boolean mOnlineMode;
@Nullable
private CustomNavigateUpListener mNavigateUpListener;
@ -106,9 +103,9 @@ public class DiscoveryFragment extends BaseMwmToolbarFragment implements UICallb
return inflater.inflate(R.layout.fragment_discovery, container, false);
}
private void initViatorGallery()
private void initHotelGallery()
{
setLayoutManagerAndItemDecoration(getContext(), getGallery(R.id.thingsToDo));
setLayoutManagerAndItemDecoration(getContext(), getGallery(R.id.hotels));
}
private void initAttractionsGallery()
@ -143,7 +140,7 @@ public class DiscoveryFragment extends BaseMwmToolbarFragment implements UICallb
if (view == null)
throw new AssertionError("Root view is not initialized yet!");
RecyclerView rv = (RecyclerView) view.findViewById(id);
RecyclerView rv = view.findViewById(id);
if (rv == null)
throw new AssertionError("RecyclerView must be within root view!");
return rv;
@ -171,18 +168,7 @@ public class DiscoveryFragment extends BaseMwmToolbarFragment implements UICallb
{
super.onViewCreated(view, savedInstanceState);
mToolbarController.setTitle(R.string.discovery_button_title);
mDefaultListener = new BaseItemSelectedListener<>(getActivity());
view.findViewById(R.id.viatorLogo).setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Utils.openUrl(getActivity(), DiscoveryManager.nativeGetViatorUrl());
Statistics.INSTANCE.trackGalleryEvent(Statistics.EventName.PP_SPONSOR_LOGO_SELECTED,
VIATOR, DISCOVERY);
}
});
initViatorGallery();
initHotelGallery();
initAttractionsGallery();
initFoodGallery();
initLocalExpertsGallery();
@ -192,20 +178,17 @@ public class DiscoveryFragment extends BaseMwmToolbarFragment implements UICallb
private void requestDiscoveryInfoAndInitAdapters()
{
NetworkPolicy.checkNetworkPolicy(getFragmentManager(), new NetworkPolicy.NetworkPolicyListener()
NetworkPolicy.checkNetworkPolicy(getFragmentManager(), policy ->
{
@Override
public void onResult(@NonNull NetworkPolicy policy)
{
mOnlineMode = policy.сanUseNetwork();
initNetworkBasedAdapters();
requestDiscoveryInfo();
}
mOnlineMode = policy.сanUseNetwork();
initNetworkBasedAdapters();
requestDiscoveryInfo();
});
}
private void initSearchBasedAdapters()
{
getGallery(R.id.hotels).setAdapter(Factory.createSearchBasedLoadingAdapter());
getGallery(R.id.attractions).setAdapter(Factory.createSearchBasedLoadingAdapter());
getGallery(R.id.food).setAdapter(Factory.createSearchBasedLoadingAdapter());
}
@ -215,10 +198,6 @@ public class DiscoveryFragment extends BaseMwmToolbarFragment implements UICallb
UiUtils.showIf(mOnlineMode, getRootView(), R.id.localGuidesTitle, R.id.localGuides);
if (mOnlineMode)
{
RecyclerView thinsToDo = getGallery(R.id.thingsToDo);
thinsToDo.setAdapter(Factory.createViatorLoadingAdapter(DiscoveryManager.nativeGetViatorUrl(),
mDefaultListener));
RecyclerView localGuides = getGallery(R.id.localGuides);
localGuides.setAdapter(Factory.createLocalExpertsLoadingAdapter());
return;
@ -226,18 +205,8 @@ public class DiscoveryFragment extends BaseMwmToolbarFragment implements UICallb
// It means that the user doesn't permit mobile network usage, so network based galleries UI
// should be hidden in this case.
if (ConnectionState.isMobileConnected())
{
UiUtils.hide(getView(), R.id.thingsToDoLayout, R.id.thingsToDo,
R.id.localGuidesTitle, R.id.localGuides);
}
else
{
UiUtils.show(getView(), R.id.thingsToDoLayout, R.id.thingsToDo);
RecyclerView thinsToDo = getGallery(R.id.thingsToDo);
thinsToDo.setAdapter(Factory.createViatorOfflineAdapter(new ViatorOfflineSelectedListener
(getActivity()), DISCOVERY));
}
UiUtils.showIf(ConnectionState.isMobileConnected(), getView(), R.id.localGuidesTitle,
R.id.localGuides);
}
private void requestDiscoveryInfo()
@ -281,16 +250,15 @@ public class DiscoveryFragment extends BaseMwmToolbarFragment implements UICallb
DISCOVERY));
}
@MainThread
@Override
public void onViatorProductsReceived(@NonNull ViatorProduct[] products)
public void onHotelsReceived(@NonNull SearchResult[] results)
{
updateViewsVisibility(products, R.id.thingsToDoLayout, R.id.thingsToDo);
String url = DiscoveryManager.nativeGetViatorUrl();
ItemSelectedListener<Items.ViatorItem> listener
= createOnlineProductItemListener(VIATOR);
getGallery(R.id.thingsToDo).setAdapter(Factory.createViatorAdapter(products, url, listener,
DISCOVERY));
updateViewsVisibility(results, R.id.hotelsTitle, R.id.hotels);
ItemSelectedListener<Items.SearchItem> listener =
createSearchProductItemListener(SEARCH_HOTELS);
getGallery(R.id.hotels).setAdapter(Factory.createHotelAdapter(results, listener,
SEARCH_HOTELS,
DISCOVERY));
}
@MainThread
@ -310,10 +278,9 @@ public class DiscoveryFragment extends BaseMwmToolbarFragment implements UICallb
{
switch (type)
{
case VIATOR:
String url = DiscoveryManager.nativeGetViatorUrl();
getGallery(R.id.thingsToDo).setAdapter(Factory.createViatorErrorAdapter(url, mDefaultListener));
Statistics.INSTANCE.trackGalleryError(VIATOR, DISCOVERY, null);
case HOTELS:
getGallery(R.id.hotels).setAdapter(Factory.createSearchBasedErrorAdapter());
Statistics.INSTANCE.trackGalleryError(SEARCH_HOTELS, DISCOVERY, null);
break;
case ATTRACTIONS:
getGallery(R.id.attractions).setAdapter(Factory.createSearchBasedErrorAdapter());

View file

@ -50,6 +50,9 @@ enum DiscoveryManager
case DiscoveryParams.ITEM_TYPE_CAFES:
callback.onCafesReceived(results);
break;
case DiscoveryParams.ITEM_TYPE_HOTELS:
callback.onHotelsReceived(results);
break;
default:
throw new AssertionError("Unsupported discovery item type " +
"'" + type + "' for search results!");
@ -62,28 +65,15 @@ enum DiscoveryManager
@MainThread
private void onViatorProductsReceived(@NonNull final ViatorProduct[] products)
{
notifyUiWithCheck(products, ItemType.VIATOR, new Action()
{
@Override
public void run(@NonNull UICallback callback)
{
callback.onViatorProductsReceived(products);
}
});
throw new UnsupportedOperationException("Viator is not supported!");
}
// Called from JNI.
@MainThread
private void onLocalExpertsReceived(@NonNull final LocalExpert[] experts)
{
notifyUiWithCheck(experts, ItemType.LOCAL_EXPERTS, new Action()
{
@Override
public void run(@NonNull UICallback callback)
{
callback.onLocalExpertsReceived(experts);
}
});
notifyUiWithCheck(experts, ItemType.LOCAL_EXPERTS,
callback -> callback.onLocalExpertsReceived(experts));
}
// Called from JNI.

View file

@ -2,7 +2,6 @@ package com.mapswithme.maps.discovery;
public enum ItemType
{
VIATOR,
ATTRACTIONS,
CAFES,
HOTELS,

View file

@ -2,20 +2,18 @@ package com.mapswithme.maps.discovery;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.mapswithme.maps.search.SearchResult;
import com.mapswithme.maps.viator.ViatorProduct;
public interface UICallback
{
@MainThread
void onHotelsReceived(@NonNull SearchResult[] results);
@MainThread
void onAttractionsReceived(@NonNull SearchResult[] results);
@MainThread
void onCafesReceived(@NonNull SearchResult[] results);
@MainThread
void onViatorProductsReceived(@NonNull ViatorProduct[] products);
@MainThread
void onLocalExpertsReceived(@NonNull LocalExpert[] experts);
@MainThread
void onError(@NonNull ItemType type);

View file

@ -1,12 +1,15 @@
package com.mapswithme.maps.gallery;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.support.v7.widget.RecyclerView;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
@ -15,6 +18,7 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.BitmapImageViewTarget;
import com.mapswithme.HotelUtils;
import com.mapswithme.maps.R;
import com.mapswithme.maps.ugc.Impress;
import com.mapswithme.maps.ugc.UGC;
@ -177,26 +181,60 @@ public class Holders
}
}
public static final class SearchViewHolder extends BaseViewHolder<Items.SearchItem>
public static abstract class ActionButtonViewHolder<T extends RegularAdapterStrategy.Item>
extends BaseViewHolder<T>
{
@NonNull
private final TextView mButton;
ActionButtonViewHolder(@NonNull View itemView, @NonNull List<T> items, @NonNull
GalleryAdapter<?, T> adapter)
{
super(itemView, items, adapter);
mButton = itemView.findViewById(R.id.button);
mButton.setOnClickListener(this);
itemView.findViewById(R.id.infoLayout).setOnClickListener(this);
mButton.setText(R.string.p2p_to_here);
}
@Override
public void onClick(View v)
{
int position = getAdapterPosition();
if (position == RecyclerView.NO_POSITION || mItems.isEmpty())
return;
ItemSelectedListener<T> listener = mAdapter.getListener();
if (listener == null)
return;
T item = mItems.get(position);
switch (v.getId())
{
case R.id.infoLayout:
listener.onItemSelected(item, position);
break;
case R.id.button:
listener.onActionButtonSelected(item, position);
break;
}
}
}
public static final class SearchViewHolder extends ActionButtonViewHolder<Items.SearchItem>
{
@NonNull
private final TextView mSubtitle;
@NonNull
private final TextView mDistance;
@NonNull
private final TextView mButton;
public SearchViewHolder(@NonNull View itemView, @NonNull List<Items.SearchItem> items,
@NonNull GalleryAdapter<?, Items.SearchItem> adapter)
{
super(itemView, items, adapter);
mTitle = (TextView) itemView.findViewById(R.id.title);
mSubtitle = (TextView) itemView.findViewById(R.id.subtitle);
mDistance = (TextView) itemView.findViewById(R.id.distance);
mButton = (TextView) itemView.findViewById(R.id.button);
mButton.setOnClickListener(this);
itemView.findViewById(R.id.infoLayout).setOnClickListener(this);
mButton.setText(R.string.p2p_to_here);
mTitle = itemView.findViewById(R.id.title);
mSubtitle = itemView.findViewById(R.id.subtitle);
mDistance = itemView.findViewById(R.id.distance);
}
@Override
@ -207,28 +245,78 @@ public class Holders
UiUtils.setTextAndHideIfEmpty(mSubtitle, item.getSubtitle());
UiUtils.setTextAndHideIfEmpty(mDistance, item.getDistance());
}
}
public static final class HotelViewHolder extends ActionButtonViewHolder<Items.SearchItem>
{
@NonNull
private final TextView mTitle;
@NonNull
private final TextView mSubtitle;
@NonNull
private final RatingView mRatingView;
@NonNull
private final TextView mDistance;
public HotelViewHolder(@NonNull View itemView, @NonNull List<Items.SearchItem> items, @NonNull
GalleryAdapter<?, Items.SearchItem> adapter)
{
super(itemView, items, adapter);
mTitle = itemView.findViewById(R.id.title);
mSubtitle = itemView.findViewById(R.id.subtitle);
mRatingView = itemView.findViewById(R.id.ratingView);
mDistance = itemView.findViewById(R.id.distance);
}
@Override
public void onClick(View v)
public void bind(@NonNull Items.SearchItem item)
{
int position = getAdapterPosition();
if (position == RecyclerView.NO_POSITION || mItems.isEmpty())
return;
UiUtils.setTextAndHideIfEmpty(mTitle, item.getTitle());
UiUtils.setTextAndHideIfEmpty(mSubtitle, formatDescription(item.getStars(),
item.getFeatureType(),
item.getPrice(),
mSubtitle.getResources()));
ItemSelectedListener<Items.SearchItem> listener = mAdapter.getListener();
if (listener == null)
return;
float rating = formatRating(item.getRating());
Impress impress = Impress.values()[UGC.nativeToImpress(rating)];
mRatingView.setRating(impress, UGC.nativeFormatRating(rating));
UiUtils.setTextAndHideIfEmpty(mDistance, item.getDistance());
}
Items.SearchItem item = mItems.get(position);
switch (v.getId())
//TODO: refactor rating to float in core
private static float formatRating(@Nullable String rating)
{
if (TextUtils.isEmpty(rating))
return -1;
try
{
case R.id.infoLayout:
listener.onItemSelected(item, position);
break;
case R.id.button:
listener.onActionButtonSelected(item, position);
break;
return Float.valueOf(rating);
}
catch (NumberFormatException e)
{
return -1;
}
}
@NonNull
private static CharSequence formatDescription(int stars, @Nullable String type,
@Nullable String priceCategory,
@NonNull Resources res)
{
final SpannableStringBuilder sb = new SpannableStringBuilder();
if (stars > 0)
sb.append(HotelUtils.formatStars(stars, res));
else if (!TextUtils.isEmpty(type))
sb.append(type);
if (!TextUtils.isEmpty(priceCategory))
{
sb.append("");
sb.append(priceCategory);
}
return sb;
}
}
@ -252,7 +340,6 @@ public class Holders
mAdapter = adapter;
}
@CallSuper
public void bind(@NonNull I item)
{
mTitle.setText(item.getTitle());

View file

@ -132,6 +132,41 @@ public class Items
{
return mResult.lon;
}
public int getStars()
{
if (mResult.description == null)
return 0;
return mResult.description.stars;
}
@Nullable
public String getRating()
{
if (mResult.description == null)
return null;
return mResult.description.rating;
}
@Nullable
public String getPrice()
{
if (mResult.description == null)
return null;
return mResult.description.pricing;
}
@Nullable
public String getFeatureType()
{
if (mResult.description == null)
return null;
return mResult.description.featureType;
}
}
public static class Item

View file

@ -35,7 +35,7 @@ public abstract class RegularAdapterStrategy<T extends RegularAdapterStrategy.It
switch (viewType)
{
case TYPE_PRODUCT:
return createProductViewHodler(parent, viewType, adapter);
return createProductViewHolder(parent, viewType, adapter);
case TYPE_MORE:
return createMoreProductsViewHolder(parent, viewType, adapter);
default:
@ -57,7 +57,7 @@ public abstract class RegularAdapterStrategy<T extends RegularAdapterStrategy.It
}
@NonNull
protected abstract Holders.BaseViewHolder<T> createProductViewHodler(@NonNull ViewGroup parent,
protected abstract Holders.BaseViewHolder<T> createProductViewHolder(@NonNull ViewGroup parent,
int viewType,
@NonNull GalleryAdapter<?, T> adapter);
@NonNull

View file

@ -78,6 +78,17 @@ public class Factory
return new GalleryAdapter<>(new SimpleErrorAdapterStrategy(), null);
}
@NonNull
public static GalleryAdapter createHotelAdapter(@NonNull SearchResult[] results,
@Nullable ItemSelectedListener<Items
.SearchItem> listener,
@NonNull GalleryType type,
@NonNull GalleryPlacement placement)
{
trackProductGalleryShownOrError(results, type, OFFLINE, placement);
return new GalleryAdapter<>(new HotelAdapterStrategy(results), listener);
}
@NonNull
public static GalleryAdapter createLocalExpertsAdapter(@NonNull LocalExpert[] experts,
@Nullable String expertsUrl,

View file

@ -0,0 +1,51 @@
package com.mapswithme.maps.gallery.impl;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.mapswithme.maps.R;
import com.mapswithme.maps.gallery.GalleryAdapter;
import com.mapswithme.maps.gallery.Holders;
import com.mapswithme.maps.gallery.Items;
import com.mapswithme.maps.gallery.RegularAdapterStrategy;
import com.mapswithme.maps.search.SearchResult;
import java.util.List;
public class HotelAdapterStrategy extends RegularAdapterStrategy<Items.SearchItem>
{
HotelAdapterStrategy(@NonNull SearchResult[] results)
{
this(SearchBasedAdapterStrategy.convertItems(results), null/*TODO: coming soon*/);
}
private HotelAdapterStrategy(@NonNull List<Items.SearchItem> items, @Nullable Items.SearchItem
moreItem)
{
super(items, moreItem);
}
@NonNull
@Override
protected Holders.BaseViewHolder<Items.SearchItem> createProductViewHolder
(@NonNull ViewGroup parent, int viewType,
@NonNull GalleryAdapter<?, Items.SearchItem> adapter)
{
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_discovery_hotel_product, parent, false);
return new Holders.HotelViewHolder(view, mItems, adapter);
}
@NonNull
@Override
protected Holders.BaseViewHolder<Items.SearchItem> createMoreProductsViewHolder
(@NonNull ViewGroup parent, int viewType,
@NonNull GalleryAdapter<?, Items.SearchItem> adapter)
{
//TODO: filter item
return null;
}
}

View file

@ -27,7 +27,7 @@ public class LocalExpertsAdapterStrategy extends RegularAdapterStrategy<Items.Lo
@NonNull
@Override
protected Holders.BaseViewHolder<Items.LocalExpertItem> createProductViewHodler
protected Holders.BaseViewHolder<Items.LocalExpertItem> createProductViewHolder
(@NonNull ViewGroup parent, int viewType, @NonNull GalleryAdapter<?, Items.LocalExpertItem> adapter)
{
View view = LayoutInflater.from(parent.getContext())

View file

@ -31,7 +31,7 @@ class SearchBasedAdapterStrategy extends RegularAdapterStrategy<Items.SearchItem
@NonNull
@Override
protected Holders.BaseViewHolder<Items.SearchItem> createProductViewHodler
protected Holders.BaseViewHolder<Items.SearchItem> createProductViewHolder
(@NonNull ViewGroup parent, int viewType, @NonNull GalleryAdapter<?, Items.SearchItem> adapter)
{
View view = LayoutInflater.from(parent.getContext())
@ -48,7 +48,7 @@ class SearchBasedAdapterStrategy extends RegularAdapterStrategy<Items.SearchItem
}
@NonNull
private static List<Items.SearchItem> convertItems(@NonNull SearchResult[] results)
static List<Items.SearchItem> convertItems(@NonNull SearchResult[] results)
{
List<Items.SearchItem> viewItems = new ArrayList<>();
for (SearchResult result : results)

View file

@ -26,7 +26,7 @@ public class ViatorAdapterStrategy
@NonNull
@Override
protected Holders.BaseViewHolder<Items.ViatorItem> createProductViewHodler
protected Holders.BaseViewHolder<Items.ViatorItem> createProductViewHolder
(@NonNull ViewGroup parent, int viewType, @NonNull GalleryAdapter<?, Items.ViatorItem> adapter)
{
View view = LayoutInflater.from(parent.getContext())

View file

@ -19,6 +19,7 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.mapswithme.HotelUtils;
import com.mapswithme.maps.R;
import com.mapswithme.maps.bookmarks.data.FeatureId;
import com.mapswithme.maps.routing.RoutingController;
@ -170,22 +171,13 @@ class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.SearchDataViewHol
final SpannableStringBuilder res = new SpannableStringBuilder(result.description.featureType);
final SpannableStringBuilder tail = new SpannableStringBuilder();
final int stars = Math.min(result.description.stars, 5);
int stars = result.description.stars;
if (stars > 0 || !result.description.rating.isEmpty() || isHotelAvailable)
{
if (stars > 0)
{
// Colorize last dimmed stars
final SpannableStringBuilder sb = new SpannableStringBuilder("★ ★ ★ ★ ★");
if (stars < 5)
{
final int start = sb.length() - ((5 - stars) * 2 - 1);
sb.setSpan(new ForegroundColorSpan(itemView.getResources().getColor(R.color.search_star_dimmed)),
start, sb.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
tail.append("");
tail.append(sb);
tail.append(HotelUtils.formatStars(stars, itemView.getResources()));
}
if (!result.description.rating.isEmpty())

View file

@ -39,6 +39,15 @@ public enum GalleryType
{
return Statistics.ParamValue.SEARCH_ATTRACTIONS;
}
},
SEARCH_HOTELS
{
@NonNull
@Override
public String getProvider()
{
return Statistics.ParamValue.BOOKING_COM;
}
};
@NonNull

View file

@ -360,7 +360,7 @@ public enum Statistics
public static class ParamValue
{
static final String BOOKING_COM = "Booking.Com";
public static final String BOOKING_COM = "Booking.Com";
static final String SEARCH_BOOKING_COM = "Search.Booking.Com";
static final String OPENTABLE = "OpenTable";
static final String VIATOR = "Viator.Com";