From 4c610ad78bc8177d4bf512c0acfef80be11334ed Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Fri, 21 Jul 2017 13:51:14 +0400 Subject: [PATCH] [android] sponsored gallery loading item and animations --- .../res/layout-land/place_page_details.xml | 2 +- android/res/layout/card_loading.xml | 49 +++++ android/res/layout/item_cian_loading.xml | 19 ++ android/res/layout/item_cian_product.xml | 118 ++++++------ android/res/layout/item_viator_loading.xml | 19 ++ android/res/layout/item_viator_product.xml | 142 +++++++------- android/res/layout/place_page_details.xml | 2 +- ...r.xml => place_page_sponsored_gallery.xml} | 10 +- .../maps/base/BaseSponsoredAdapter.java | 118 +++++++++++- .../com/mapswithme/maps/cian/CianAdapter.java | 35 +++- .../com/mapswithme/maps/viator/Viator.java | 5 + .../mapswithme/maps/viator/ViatorAdapter.java | 30 ++- .../maps/widget/placepage/PlacePageView.java | 178 +++++++++++++++--- .../recycler/SingleChangeItemAnimator.java | 113 +++++++++++ .../util/statistics/PlacePageTracker.java | 2 +- 15 files changed, 675 insertions(+), 167 deletions(-) create mode 100644 android/res/layout/card_loading.xml create mode 100644 android/res/layout/item_cian_loading.xml create mode 100644 android/res/layout/item_viator_loading.xml rename android/res/layout/{place_page_viator.xml => place_page_sponsored_gallery.xml} (90%) create mode 100644 android/src/com/mapswithme/maps/widget/recycler/SingleChangeItemAnimator.java diff --git a/android/res/layout-land/place_page_details.xml b/android/res/layout-land/place_page_details.xml index 025fcdcaa2..6af5e25306 100644 --- a/android/res/layout-land/place_page_details.xml +++ b/android/res/layout-land/place_page_details.xml @@ -31,7 +31,7 @@ android:layout_height="1dp" android:layout_marginBottom="@dimen/margin_half"/> - + diff --git a/android/res/layout/card_loading.xml b/android/res/layout/card_loading.xml new file mode 100644 index 0000000000..add611f4ff --- /dev/null +++ b/android/res/layout/card_loading.xml @@ -0,0 +1,49 @@ + + + + + + + \ No newline at end of file diff --git a/android/res/layout/item_cian_loading.xml b/android/res/layout/item_cian_loading.xml new file mode 100644 index 0000000000..8f21061247 --- /dev/null +++ b/android/res/layout/item_cian_loading.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/android/res/layout/item_cian_product.xml b/android/res/layout/item_cian_product.xml index 31f7f9bff6..2648c9c5a9 100644 --- a/android/res/layout/item_cian_product.xml +++ b/android/res/layout/item_cian_product.xml @@ -1,59 +1,67 @@ - - - - - - - - + android:padding="@dimen/margin_quarter" + android:clipToPadding="false"> + + + + + + + + + diff --git a/android/res/layout/item_viator_loading.xml b/android/res/layout/item_viator_loading.xml new file mode 100644 index 0000000000..25b4cdcaf9 --- /dev/null +++ b/android/res/layout/item_viator_loading.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/android/res/layout/item_viator_product.xml b/android/res/layout/item_viator_product.xml index 42e372e9b7..052a2f0dbe 100644 --- a/android/res/layout/item_viator_product.xml +++ b/android/res/layout/item_viator_product.xml @@ -1,72 +1,78 @@ - - - - - - - - - + android:padding="@dimen/margin_quarter" + android:clipToPadding="false"> + + + + + + + + + + diff --git a/android/res/layout/place_page_details.xml b/android/res/layout/place_page_details.xml index 4a02b62b1e..a2d4d5e390 100644 --- a/android/res/layout/place_page_details.xml +++ b/android/res/layout/place_page_details.xml @@ -17,7 +17,7 @@ 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_sponsored_gallery.xml similarity index 90% rename from android/res/layout/place_page_viator.xml rename to android/res/layout/place_page_sponsored_gallery.xml index cefa71d253..a795467d81 100644 --- a/android/res/layout/place_page_viator.xml +++ b/android/res/layout/place_page_sponsored_gallery.xml @@ -2,9 +2,9 @@ diff --git a/android/src/com/mapswithme/maps/base/BaseSponsoredAdapter.java b/android/src/com/mapswithme/maps/base/BaseSponsoredAdapter.java index bec63cfecf..833f06aa19 100644 --- a/android/src/com/mapswithme/maps/base/BaseSponsoredAdapter.java +++ b/android/src/com/mapswithme/maps/base/BaseSponsoredAdapter.java @@ -8,11 +8,13 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ProgressBar; import android.widget.TextView; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.widget.placepage.Sponsored; +import com.mapswithme.util.UiUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -25,11 +27,16 @@ public abstract class BaseSponsoredAdapter extends RecyclerView.Adapter(); + mListener = listener; + mItems.add(new Item(TYPE_LOADING, sponsoredType, getLoadingTitle(), url, getLoadingSubtitle(), + hasError, false)); + } + public BaseSponsoredAdapter(@Sponsored.SponsoredType int sponsoredType, @NonNull List items, @NonNull String url, @Nullable ItemSelectedListener listener) @@ -51,7 +67,7 @@ public abstract class BaseSponsoredAdapter extends RecyclerView.Adapter 0, mViatorView); - mRvViatorProducts.setAdapter(new ViatorAdapter(products, cityUrl, this)); + } + else + { + if (mSponsoredAdapter == null || !mSponsoredAdapter.containsLoading()) + { + mSponsoredAdapter = new ViatorAdapter(products, cityUrl, this); + mRvSponsoredProducts.setAdapter(mSponsoredAdapter); + } + else + { + mRvSponsoredProducts.setItemAnimator(new SingleChangeItemAnimator() { + @Override + public void onAnimationFinished() + { + mRvSponsoredProducts.setItemAnimator(new DefaultItemAnimator()); + mSponsoredAdapter = new ViatorAdapter(products, cityUrl, PlacePageView.this); + mRvSponsoredProducts.setAdapter(mSponsoredAdapter); + } + }); + mSponsoredAdapter.setLoadingCompleted(Sponsored.TYPE_VIATOR, cityUrl); + } + } } - private void hideViatorViews() + private void updateCianView(@NonNull final RentPlace[] products, @NonNull final String url) { - UiUtils.hide(mViatorView); + if (products.length == 0) + { + if (mSponsoredAdapter == null || !mSponsoredAdapter.containsLoading()) + { + mSponsoredAdapter = new CianAdapter(url, true, this); + mRvSponsoredProducts.setAdapter(mSponsoredAdapter); + } + else + { + mSponsoredAdapter.setLoadingError(Sponsored.TYPE_CIAN, url); + } + Statistics.INSTANCE.trackSponsoredGalleryError(Sponsored.TYPE_CIAN); + } + else + { + if (mSponsoredAdapter == null || !mSponsoredAdapter.containsLoading()) + { + mSponsoredAdapter = new CianAdapter(products, url, this); + mRvSponsoredProducts.setAdapter(mSponsoredAdapter); + } + else + { + mRvSponsoredProducts.setItemAnimator(new SingleChangeItemAnimator() { + @Override + public void onAnimationFinished() + { + mRvSponsoredProducts.setItemAnimator(new DefaultItemAnimator()); + mSponsoredAdapter = new CianAdapter(products, url, PlacePageView.this); + mRvSponsoredProducts.setAdapter(mSponsoredAdapter); + } + }); + mSponsoredAdapter.setLoadingCompleted(Sponsored.TYPE_VIATOR, url); + } + } } - private void clearViatorViews() + private void showLoadingViatorProducts(@NonNull String id, @NonNull String cityUrl) { - mRvViatorProducts.setAdapter(new ViatorAdapter(new ViatorProduct[]{}, "", null)); + UiUtils.show(mSponsoredGalleryView); + if (!Viator.hasCache(id)) + { + mSponsoredAdapter = new ViatorAdapter(cityUrl, false, this); + mRvSponsoredProducts.setAdapter(mSponsoredAdapter); + } + } + + private void showLoadingCianProducts(@NonNull String url) + { + UiUtils.show(mSponsoredGalleryView); + if (mRvSponsoredProducts.getAdapter().getItemCount() == 0) + { + mSponsoredAdapter = new CianAdapter(url, false, this); + mRvSponsoredProducts.setAdapter(mSponsoredAdapter); + } + } + + private void hideSponsoredGalleryViews() + { + UiUtils.hide(mSponsoredGalleryView); + } + + private void clearSponsoredGalleryViews() + { + mSponsoredAdapter = null; + mRvSponsoredProducts.setAdapter(new ViatorAdapter(new ViatorProduct[]{}, "", null)); } @Override @@ -1173,7 +1289,7 @@ public class PlacePageView extends RelativeLayout return; } clearHotelViews(); - clearViatorViews(); + clearSponsoredGalleryViews(); if (mSponsored != null) { mSponsored.updateId(mMapObject); @@ -1183,9 +1299,14 @@ public class PlacePageView extends RelativeLayout 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.requestViatorProducts(policy, mSponsored.getId(), currencyCode); + showLoadingViatorProducts(mSponsored.getId(), mSponsored.getUrl()); + } } Sponsored.requestInfo(mSponsored, Locale.getDefault().toString(), policy); } @@ -1345,7 +1466,7 @@ public class PlacePageView extends RelativeLayout refreshMetadataOrHide(TextUtils.isEmpty(website) ? mapObject.getMetadata(Metadata.MetadataType.FMD_URL) : website, mWebsite, mTvWebsite); hideHotelViews(); - hideViatorViews(); + hideSponsoredGalleryViews(); } else { @@ -1354,8 +1475,11 @@ public class PlacePageView extends RelativeLayout if (mSponsored.getType() != Sponsored.TYPE_BOOKING) hideHotelViews(); - if (mSponsored.getType() != Sponsored.TYPE_VIATOR) - hideViatorViews(); + if (mSponsored.getType() != Sponsored.TYPE_VIATOR + && mSponsored.getType() != Sponsored.TYPE_CIAN) + { + hideSponsoredGalleryViews(); + } } refreshMetadataOrHide(mapObject.getMetadata(Metadata.MetadataType.FMD_PHONE_NUMBER), mPhone, mTvPhone); @@ -1824,7 +1948,7 @@ public class PlacePageView extends RelativeLayout } } break; - case R.id.btn__viator_more: + case R.id.btn__sponsored_more: if (mSponsored != null) Utils.openUrl(getContext(), mSponsored.getUrl()); break; diff --git a/android/src/com/mapswithme/maps/widget/recycler/SingleChangeItemAnimator.java b/android/src/com/mapswithme/maps/widget/recycler/SingleChangeItemAnimator.java new file mode 100644 index 0000000000..2175c48d79 --- /dev/null +++ b/android/src/com/mapswithme/maps/widget/recycler/SingleChangeItemAnimator.java @@ -0,0 +1,113 @@ +package com.mapswithme.maps.widget.recycler; + +import android.support.annotation.Nullable; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.support.v4.view.ViewPropertyAnimatorListener; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.SimpleItemAnimator; +import android.view.View; +import android.view.ViewGroup; + +import com.mapswithme.util.UiUtils; + +public class SingleChangeItemAnimator extends SimpleItemAnimator +{ + private static final int DURATION = 1000; + + @Nullable + private ViewPropertyAnimatorCompat mAnimation; + private boolean mFinished; + + @Override + public long getChangeDuration() + { + return DURATION; + } + + @Override + public boolean animateChange(final RecyclerView.ViewHolder oldHolder, + final RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, + int toLeft, int toTop) + { + mAnimation = ViewCompat.animate(oldHolder.itemView); + if (mAnimation == null) + return false; + + mFinished = false; + ViewGroup group = (ViewGroup) ((ViewGroup) oldHolder.itemView).getChildAt(0); + for (int i = 0; i < group.getChildCount(); i++) + UiUtils.hide(group.getChildAt(i)); + + int from = oldHolder.itemView.getWidth(); + int target = newHolder.itemView.getWidth(); + oldHolder.itemView.setPivotX(0.0f); + mAnimation + .setDuration(getChangeDuration()) + .scaleX(((float) target / (float) from)) + .setListener(new ViewPropertyAnimatorListener() + { + @Override + public void onAnimationStart(View view) {} + @Override + public void onAnimationCancel(View view) {} + + @Override + public void onAnimationEnd(View view) + { + mFinished = true; + mAnimation.setListener(null); + UiUtils.hide(oldHolder.itemView, newHolder.itemView); + dispatchChangeFinished(oldHolder, true); + dispatchChangeFinished(newHolder, false); + dispatchAnimationsFinished(); + onAnimationFinished(); + } + }) + .start(); + return false; + } + + public void onAnimationFinished() {} + + @Override + public void endAnimation(RecyclerView.ViewHolder item) + { + if (mAnimation != null) + mAnimation.cancel(); + } + + @Override + public void endAnimations() + { + if (mAnimation != null) + mAnimation.cancel(); + } + + @Override + public boolean isRunning() + { + return mAnimation != null && !mFinished; + } + + @Override + public boolean animateRemove(final RecyclerView.ViewHolder holder) + { + return false; + } + + @Override + public boolean animateAdd(RecyclerView.ViewHolder holder) + { + return false; + } + + @Override + public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) + { + return false; + } + + @Override + public void runPendingAnimations() {} +} diff --git a/android/src/com/mapswithme/util/statistics/PlacePageTracker.java b/android/src/com/mapswithme/util/statistics/PlacePageTracker.java index 7036a57938..f7ab81a01c 100644 --- a/android/src/com/mapswithme/util/statistics/PlacePageTracker.java +++ b/android/src/com/mapswithme/util/statistics/PlacePageTracker.java @@ -37,7 +37,7 @@ public class PlacePageTracker mPlacePageView = placePageView; mBottomButtons = mPlacePageView.findViewById(R.id.pp__buttons); mTaxi = mPlacePageView.findViewById(R.id.ll__place_page_taxi); - mViator = mPlacePageView.findViewById(R.id.ll__place_viator); + mViator = mPlacePageView.findViewById(R.id.ll__place_sponsored_gallery); } public void setMapObject(@Nullable MapObject mapObject)