From cf324e4bbba41daf816942b6a2a7fa6e6832fee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D1=83=D0=B7=D0=BD=D0=B5=D1=86=D0=BE=D0=B2=D0=B0=20?= =?UTF-8?q?=D0=9A=D1=81=D0=B5=D0=BD=D0=B8=D1=8F?= Date: Tue, 22 Aug 2017 11:49:12 +0300 Subject: [PATCH] Added Google Ads --- android/build.gradle | 1 + android/res/values/attrs.xml | 25 +++ android/res/values/styles-google-ads.xml | 52 ++++++ .../mapswithme/maps/ads/GoogleSearchAd.java | 6 +- .../maps/search/GoogleAdsBanner.java | 28 ++++ .../maps/search/GoogleAdsLoader.java | 151 +++++++++++++++++ .../mapswithme/maps/search/SearchAdapter.java | 142 ++++++++-------- .../mapswithme/maps/search/SearchData.java | 6 + .../maps/search/SearchFragment.java | 157 ++++++++++++++++-- .../mapswithme/maps/search/SearchResult.java | 16 +- .../maps/search/SearchResultTypes.java | 9 + 11 files changed, 507 insertions(+), 86 deletions(-) create mode 100644 android/res/values/styles-google-ads.xml create mode 100644 android/src/com/mapswithme/maps/search/GoogleAdsBanner.java create mode 100644 android/src/com/mapswithme/maps/search/GoogleAdsLoader.java create mode 100644 android/src/com/mapswithme/maps/search/SearchData.java create mode 100644 android/src/com/mapswithme/maps/search/SearchResultTypes.java diff --git a/android/build.gradle b/android/build.gradle index c956cc4175..787d69fc0f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -45,6 +45,7 @@ dependencies { compile 'com.google.android.gms:play-services-analytics:10.0.1' compile 'com.google.android.gms:play-services-plus:10.0.1' compile 'com.google.android.gms:play-services-gcm:10.0.1' + compile 'com.google.android.gms:play-services-ads:10.0.1' // statistics compile 'com.flurry.android:analytics:6.7.0' // crash reporting diff --git a/android/res/values/attrs.xml b/android/res/values/attrs.xml index f90b460c45..70de1715d2 100644 --- a/android/res/values/attrs.xml +++ b/android/res/values/attrs.xml @@ -39,6 +39,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + false false diff --git a/android/res/values/styles-google-ads.xml b/android/res/values/styles-google-ads.xml new file mode 100644 index 0000000000..c3181d0178 --- /dev/null +++ b/android/res/values/styles-google-ads.xml @@ -0,0 +1,52 @@ + + + + + + diff --git a/android/src/com/mapswithme/maps/ads/GoogleSearchAd.java b/android/src/com/mapswithme/maps/ads/GoogleSearchAd.java index 10e7b2d4ad..1612acbdad 100644 --- a/android/src/com/mapswithme/maps/ads/GoogleSearchAd.java +++ b/android/src/com/mapswithme/maps/ads/GoogleSearchAd.java @@ -1,9 +1,12 @@ package com.mapswithme.maps.ads; +import android.support.annotation.NonNull; + import com.mapswithme.maps.Framework; public class GoogleSearchAd { + @NonNull private String mAdUnitId = ""; public GoogleSearchAd() @@ -22,5 +25,6 @@ public class GoogleSearchAd } } - String getAdUnitId() { return mAdUnitId; } + @NonNull + public String getAdUnitId() { return mAdUnitId; } } diff --git a/android/src/com/mapswithme/maps/search/GoogleAdsBanner.java b/android/src/com/mapswithme/maps/search/GoogleAdsBanner.java new file mode 100644 index 0000000000..cd156ffd94 --- /dev/null +++ b/android/src/com/mapswithme/maps/search/GoogleAdsBanner.java @@ -0,0 +1,28 @@ +package com.mapswithme.maps.search; + +import android.support.annotation.NonNull; + +import com.google.android.gms.ads.search.SearchAdView; + +class GoogleAdsBanner implements SearchData +{ + @NonNull + private SearchAdView mAdView; + + GoogleAdsBanner(@NonNull SearchAdView adView) + { + this.mAdView = adView; + } + + @NonNull + SearchAdView getAdView() + { + return mAdView; + } + + @Override + public int getItemViewType() + { + return SearchResultTypes.TYPE_GOOGLE_ADS; + } +} diff --git a/android/src/com/mapswithme/maps/search/GoogleAdsLoader.java b/android/src/com/mapswithme/maps/search/GoogleAdsLoader.java new file mode 100644 index 0000000000..1e6b131c1a --- /dev/null +++ b/android/src/com/mapswithme/maps/search/GoogleAdsLoader.java @@ -0,0 +1,151 @@ +package com.mapswithme.maps.search; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.google.ads.mediation.admob.AdMobAdapter; +import com.google.android.gms.ads.AdListener; +import com.google.android.gms.ads.AdSize; +import com.google.android.gms.ads.search.SearchAdRequest; +import com.google.android.gms.ads.search.SearchAdView; +import com.mapswithme.maps.R; +import com.mapswithme.maps.ads.GoogleSearchAd; +import com.mapswithme.util.ThemeUtils; +import com.mapswithme.util.concurrency.UiThread; + +class GoogleAdsLoader +{ + private long mLoadingDelay; + @NonNull + private final Bundle mStyleParams = new Bundle(); + @Nullable + private GoogleSearchAd mGoogleSearchAd; + @Nullable + private Runnable mLoadingTask; + @NonNull + private String mQuery = ""; + @Nullable + private AdvertLoadingListener mLoadingListener; + + GoogleAdsLoader(@NonNull Context context, long loadingDelay) + { + this.mLoadingDelay = loadingDelay; + initStyle(context); + } + + void scheduleAdsLoading(@NonNull final Context context, @NonNull final String query) + { + cancelAdsLoading(); + mQuery = query; + + mGoogleSearchAd = new GoogleSearchAd(); + if (mGoogleSearchAd.getAdUnitId().isEmpty()) + return; + + mLoadingTask = new Runnable() + { + @Override + public void run() + { + performLoading(context); + } + }; + UiThread.runLater(mLoadingTask, mLoadingDelay); + } + + void cancelAdsLoading() + { + if (mLoadingTask != null) + { + UiThread.cancelDelayedTasks(mLoadingTask); + mLoadingTask = null; + } + } + + private void updateAdView(SearchAdView searchAdView) + { + SearchAdRequest.Builder builder = new SearchAdRequest.Builder() + .setQuery(mQuery) + .addNetworkExtrasBundle(AdMobAdapter.class, mStyleParams); + + searchAdView.loadAd(builder.build()); + } + + void attach(@NonNull AdvertLoadingListener listener) + { + mLoadingListener = listener; + } + + void detach() + { + mLoadingListener = null; + } + + private void initStyle(@NonNull Context context) + { + TypedArray attrs = context.obtainStyledAttributes(ThemeUtils.isNightTheme() ? + R.style.GoogleAdsDark : R.style.GoogleAdsLight, R.styleable.GoogleAds); + + mStyleParams.putString("csa_width", "auto"); + mStyleParams.putString("csa_colorLocation", attrs.getString(R.styleable.GoogleAds_colorLocation)); + mStyleParams.putString("csa_fontSizeLocation", attrs.getString(R.styleable.GoogleAds_fontSizeLocation)); + mStyleParams.putString("csa_clickToCall", attrs.getString(R.styleable.GoogleAds_clickToCall)); + mStyleParams.putString("csa_location", attrs.getString(R.styleable.GoogleAds_location)); + mStyleParams.putString("csa_sellerRatings", attrs.getString(R.styleable.GoogleAds_sellerRatings)); + mStyleParams.putString("csa_siteLinks", attrs.getString(R.styleable.GoogleAds_siteLinks)); + mStyleParams.putString("csa_number", attrs.getString(R.styleable.GoogleAds_number)); + mStyleParams.putString("csa_fontSizeAnnotation", attrs.getString(R.styleable.GoogleAds_fontSizeAnnotation)); + mStyleParams.putString("csa_fontSizeAttribution", attrs.getString(R.styleable.GoogleAds_fontSizeAttribution)); + mStyleParams.putString("csa_fontSizeDescription", attrs.getString(R.styleable.GoogleAds_fontSizeDescription)); + mStyleParams.putString("csa_fontSizeDomainLink", attrs.getString(R.styleable.GoogleAds_fontSizeDomainLink)); + mStyleParams.putString("csa_fontSizeTitle", attrs.getString(R.styleable.GoogleAds_fontSizeTitle)); + mStyleParams.putString("csa_colorAdBorder", attrs.getString(R.styleable.GoogleAds_colorAdBorder)); + mStyleParams.putString("csa_colorAnnotation", attrs.getString(R.styleable.GoogleAds_colorAnnotation)); + mStyleParams.putString("csa_colorAttribution", attrs.getString(R.styleable.GoogleAds_colorAttribution)); + mStyleParams.putString("csa_colorBackground", attrs.getString(R.styleable.GoogleAds_colorBackground)); + mStyleParams.putString("csa_colorDomainLink", attrs.getString(R.styleable.GoogleAds_colorDomainLink)); + mStyleParams.putString("csa_colorText", attrs.getString(R.styleable.GoogleAds_colorText)); + mStyleParams.putString("csa_colorTitleLink", attrs.getString(R.styleable.GoogleAds_colorTitleLink)); + mStyleParams.putString("csa_attributionSpacingBelow", attrs.getString(R.styleable.GoogleAds_attributionSpacingBelow)); + mStyleParams.putString("csa_noTitleUnderline", attrs.getString(R.styleable.GoogleAds_noTitleUnderline)); + mStyleParams.putString("csa_titleBold", attrs.getString(R.styleable.GoogleAds_titleBold)); + attrs.recycle(); + } + + private void performLoading(@NonNull Context context) + { + if (mGoogleSearchAd == null) + throw new AssertionError("mGoogleSearchAd can't be null here"); + + final SearchAdView view = new SearchAdView(context); + view.setAdSize(AdSize.SEARCH); + view.setAdUnitId(mGoogleSearchAd.getAdUnitId()); + updateAdView(view); + view.setAdListener(new AdListener() + { + @Override + public void onAdLoaded() + { + mLoadingTask = null; + if (mLoadingListener != null) + { + mLoadingListener.onLoadingFinished(view); + } + } + + @Override + public void onAdFailedToLoad(int i) + { + mLoadingTask = null; + } + }); + } + + interface AdvertLoadingListener + { + void onLoadingFinished(@NonNull SearchAdView searchAdView); + } +} diff --git a/android/src/com/mapswithme/maps/search/SearchAdapter.java b/android/src/com/mapswithme/maps/search/SearchAdapter.java index 5e0069bbc8..aa1300f871 100644 --- a/android/src/com/mapswithme/maps/search/SearchAdapter.java +++ b/android/src/com/mapswithme/maps/search/SearchAdapter.java @@ -13,6 +13,7 @@ import android.text.style.StyleSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.TextView; import com.mapswithme.maps.R; @@ -21,23 +22,29 @@ import com.mapswithme.util.Graphics; import com.mapswithme.util.ThemeUtils; import com.mapswithme.util.UiUtils; -class SearchAdapter extends RecyclerView.Adapter +class SearchAdapter extends RecyclerView.Adapter { - private static final int TYPE_SUGGEST = 0; - private static final int TYPE_RESULT = 1; - private static final int TYPE_LOCAL_ADS_CUSTOMER = 2; - private final SearchFragment mSearchFragment; - private SearchResult[] mResults; + private SearchData[] mResults; private final Drawable mClosedMarkerBackground; - static abstract class BaseViewHolder extends RecyclerView.ViewHolder + static abstract class SearchDataViewHolder extends RecyclerView.ViewHolder + { + SearchDataViewHolder(@NonNull View itemView) + { + super(itemView); + } + + abstract void bind(@NonNull SearchData searchData, int position); + } + + private static abstract class BaseResultViewHolder extends SearchDataViewHolder { SearchResult mResult; // Position within search results int mOrder; - BaseViewHolder(View view) + BaseResultViewHolder(@NonNull View view) { super(view); if (view instanceof TextView) @@ -46,25 +53,6 @@ class SearchAdapter extends RecyclerView.Adapter if (tintAttr != 0) Graphics.tint((TextView)view, tintAttr); } - } - - @AttrRes int getTintAttr() - { - return R.attr.colorAccent; - } - - void bind(@NonNull SearchResult result, int order) - { - mResult = result; - mOrder = order; - } - } - - private static abstract class BaseResultViewHolder extends BaseViewHolder - { - BaseResultViewHolder(View view) - { - super(view); view.setOnClickListener(new View.OnClickListener() { @Override @@ -76,26 +64,33 @@ class SearchAdapter extends RecyclerView.Adapter } @Override - void bind(@NonNull SearchResult result, int order) + void bind(@NonNull SearchData result, int order) { - super.bind(result, order); - - SpannableStringBuilder builder = new SpannableStringBuilder(result.name); - if (result.highlightRanges != null) + mResult = (SearchResult)result; + mOrder = order; + SpannableStringBuilder builder = new SpannableStringBuilder(mResult.name); + if (mResult.highlightRanges != null) { - final int size = result.highlightRanges.length / 2; + final int size = mResult.highlightRanges.length / 2; int index = 0; for (int i = 0; i < size; i++) { - final int start = result.highlightRanges[index++]; - final int len = result.highlightRanges[index++]; + final int start = mResult.highlightRanges[index++]; + final int len = mResult.highlightRanges[index++]; builder.setSpan(new StyleSpan(Typeface.BOLD), start, start + len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } - getTitleView().setText(builder); + TextView titleView = getTitleView(); + if (titleView != null) + titleView.setText(builder); + } + + @AttrRes int getTintAttr() + { + return R.attr.colorAccent; } abstract TextView getTitleView(); @@ -123,6 +118,25 @@ class SearchAdapter extends RecyclerView.Adapter } } + private static class GoogleAdsViewHolder extends SearchDataViewHolder + { + @NonNull + private ViewGroup container; + + GoogleAdsViewHolder(@NonNull View view) + { + super(view); + container = (FrameLayout)view; + } + + @Override + void bind(@NonNull SearchData searchData, int position) + { + container.removeAllViews(); + container.addView(((GoogleAdsBanner)searchData).getAdView()); + } + } + private class ResultViewHolder extends BaseResultViewHolder { final TextView mName; @@ -205,16 +219,16 @@ class SearchAdapter extends RecyclerView.Adapter } @Override - void bind(@NonNull SearchResult result, int order) + void bind(@NonNull SearchData result, int order) { super.bind(result, order); // TODO: Support also "Open Now" mark. - UiUtils.showIf(result.description.openNow == SearchResult.OPEN_NOW_NO, mClosedMarker); - UiUtils.setTextAndHideIfEmpty(mDescription, formatDescription(result)); - UiUtils.setTextAndHideIfEmpty(mRegion, result.description.region); - UiUtils.setTextAndHideIfEmpty(mDistance, result.description.distance); - UiUtils.setTextAndHideIfEmpty(mPriceCategory, result.description.pricing); + UiUtils.showIf(mResult.description.openNow == SearchResult.OPEN_NOW_NO, mClosedMarker); + UiUtils.setTextAndHideIfEmpty(mDescription, formatDescription(mResult)); + UiUtils.setTextAndHideIfEmpty(mRegion, mResult.description.region); + UiUtils.setTextAndHideIfEmpty(mDistance, mResult.description.distance); + UiUtils.setTextAndHideIfEmpty(mPriceCategory, mResult.description.pricing); } @Override @@ -245,29 +259,31 @@ class SearchAdapter extends RecyclerView.Adapter } @Override - public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) + public SearchDataViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); switch (viewType) { - case TYPE_SUGGEST: - return new SuggestViewHolder(inflater.inflate(R.layout.item_search_suggest, parent, false)); + case SearchResultTypes.TYPE_SUGGEST: + return new SuggestViewHolder(inflater.inflate(R.layout.item_search_suggest, parent, false)); - case TYPE_RESULT: - return new ResultViewHolder(inflater.inflate(R.layout.item_search_result, parent, false)); + case SearchResultTypes.TYPE_RESULT: + return new ResultViewHolder(inflater.inflate(R.layout.item_search_result, parent, false)); - case TYPE_LOCAL_ADS_CUSTOMER: - return new LocalAdsCustomerViewHolder(inflater.inflate(R.layout.item_search_result, parent, - false)); + case SearchResultTypes.TYPE_LOCAL_ADS_CUSTOMER: + return new LocalAdsCustomerViewHolder(inflater.inflate(R.layout.item_search_result, parent, false)); - default: - throw new IllegalArgumentException("Unhandled view type given"); + case SearchResultTypes.TYPE_GOOGLE_ADS: + return new GoogleAdsViewHolder(new FrameLayout(parent.getContext())); + + default: + throw new IllegalArgumentException("Unhandled view type given"); } } @Override - public void onBindViewHolder(BaseViewHolder holder, int position) + public void onBindViewHolder(@NonNull SearchDataViewHolder holder, int position) { holder.bind(mResults[position], position); } @@ -275,20 +291,7 @@ class SearchAdapter extends RecyclerView.Adapter @Override public int getItemViewType(int position) { - switch (mResults[position].type) - { - case SearchResult.TYPE_SUGGEST: - return TYPE_SUGGEST; - - case SearchResult.TYPE_RESULT: - return TYPE_RESULT; - - case SearchResult.TYPE_LOCAL_ADS_CUSTOMER: - return TYPE_LOCAL_ADS_CUSTOMER; - - default: - throw new IllegalArgumentException("Unhandled SearchResult type"); - } + return mResults[position].getItemViewType(); } boolean showPopulateButton() @@ -296,7 +299,8 @@ class SearchAdapter extends RecyclerView.Adapter return (!RoutingController.get().isWaitingPoiPick() && mResults != null && mResults.length > 0 && - mResults[0].type != SearchResult.TYPE_SUGGEST); + SearchResult.class.isInstance(mResults[0]) && + ((SearchResult) mResults[0]).type != SearchResultTypes.TYPE_SUGGEST); } @Override @@ -321,7 +325,7 @@ class SearchAdapter extends RecyclerView.Adapter refreshData(null); } - void refreshData(SearchResult[] results) + void refreshData(SearchData[] results) { mResults = results; notifyDataSetChanged(); diff --git a/android/src/com/mapswithme/maps/search/SearchData.java b/android/src/com/mapswithme/maps/search/SearchData.java new file mode 100644 index 0000000000..5a6e488f57 --- /dev/null +++ b/android/src/com/mapswithme/maps/search/SearchData.java @@ -0,0 +1,6 @@ +package com.mapswithme.maps.search; + +interface SearchData +{ + int getItemViewType(); +} diff --git a/android/src/com/mapswithme/maps/search/SearchFragment.java b/android/src/com/mapswithme/maps/search/SearchFragment.java index ff443d599c..7ff548ecc7 100644 --- a/android/src/com/mapswithme/maps/search/SearchFragment.java +++ b/android/src/com/mapswithme/maps/search/SearchFragment.java @@ -20,6 +20,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.google.android.gms.ads.search.SearchAdView; import com.mapswithme.maps.MwmActivity; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; @@ -35,12 +36,16 @@ import com.mapswithme.maps.routing.RoutingController; import com.mapswithme.maps.widget.PlaceholderView; import com.mapswithme.maps.widget.SearchToolbarController; import com.mapswithme.maps.widget.placepage.Sponsored; +import com.mapswithme.util.ConnectionState; +import com.mapswithme.util.SharedPropertiesUtils; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; +import com.mapswithme.util.concurrency.UiThread; import com.mapswithme.util.log.LoggerFactory; import com.mapswithme.util.statistics.Statistics; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; public class SearchFragment extends BaseMwmFragment @@ -51,9 +56,38 @@ public class SearchFragment extends BaseMwmFragment HotelsFilterHolder { public static final String PREFS_SHOW_ENABLE_LOGGING_SETTING = "ShowEnableLoggingSetting"; + private static final int MIN_QUERY_LENGTH_FOR_AD = 3; + private static final long ADS_DELAY_MS = 200; + private static final long RESULTS_DELAY_MS = 400; + private static final int AD_POSITION = 3; private long mLastQueryTimestamp; + @Nullable + private GoogleAdsLoader mAdsLoader; + private boolean mAdsRequested = false; + private int mAdsOrientation = -1; + @Nullable + private SearchAdView mGoogleAdView; + @NonNull + private final Runnable mResultsShowingTask = new Runnable() + { + @Override + public void run() + { + refreshSearchResults(); + } + }; + @NonNull + private final Runnable mSearchEndTask = new Runnable() + { + @Override + public void run() + { + onSearchEnd(); + } + }; + private static class LastPosition { double lat; @@ -87,6 +121,11 @@ public class SearchFragment extends BaseMwmFragment if (!isAdded()) return; + UiThread.cancelDelayedTasks(mSearchEndTask); + UiThread.cancelDelayedTasks(mResultsShowingTask); + mGoogleAdView = null; + stopAdsLoading(); + if (TextUtils.isEmpty(query)) { mSearchAdapter.clear(); @@ -105,6 +144,12 @@ public class SearchFragment extends BaseMwmFragment if (mCianCategorySelected) return; + if (mAdsLoader != null && !isInteractiveSearch() && query.length() >= MIN_QUERY_LENGTH_FOR_AD) + { + mAdsRequested = true; + mAdsLoader.scheduleAdsLoading(getActivity().getApplicationContext(), query); + } + runSearch(); } @@ -188,6 +233,10 @@ public class SearchFragment extends BaseMwmFragment @Nullable private HotelsFilter mInitialHotelsFilter; + private boolean mIsHotel; + @NonNull + private SearchResult[] mSearchResults = new SearchResult[0]; + private final LocationListener mLocationListener = new LocationListener.Simple() { @Override @@ -299,6 +348,20 @@ public class SearchFragment extends BaseMwmFragment super.onViewCreated(view, savedInstanceState); readArguments(); + if (ConnectionState.isWifiConnected() && SharedPropertiesUtils.isShowcaseSwitchedOnLocal()) + { + mAdsLoader = new GoogleAdsLoader(getContext(), ADS_DELAY_MS); + mAdsLoader.attach(new GoogleAdsLoader.AdvertLoadingListener() + { + @Override + public void onLoadingFinished(@NonNull SearchAdView searchAdView) + { + mGoogleAdView = searchAdView; + mAdsRequested = false; + } + }); + } + ViewGroup root = (ViewGroup) view; mAppBarLayout = (AppBarLayout) root.findViewById(R.id.app_bar); mToolbarLayout = (CollapsingToolbarLayout) mAppBarLayout.findViewById(R.id.collapsing_toolbar); @@ -398,7 +461,6 @@ public class SearchFragment extends BaseMwmFragment mFilterController.onSaveState(outState); } - public void onResume() { super.onResume(); @@ -425,6 +487,14 @@ public class SearchFragment extends BaseMwmFragment super.onDestroy(); } + @Override + public void onDestroyView() + { + if (mAdsLoader != null) + mAdsLoader.detach(); + super.onDestroyView(); + } + private String getQuery() { return mToolbarController.getQuery(); @@ -524,6 +594,12 @@ public class SearchFragment extends BaseMwmFragment } private void onSearchEnd() + { + if (mSearchRunning && isAdded()) + updateSearchView(); + } + + private void updateSearchView() { mSearchRunning = false; mToolbarController.showProgress(false); @@ -535,7 +611,13 @@ public class SearchFragment extends BaseMwmFragment { SearchEngine.cancelApiCall(); SearchEngine.cancelAllSearches(); - onSearchEnd(); + updateSearchView(); + } + + private boolean isInteractiveSearch() + { + // TODO @yunitsky Implement more elegant solution. + return getActivity() instanceof MwmActivity; } private void runSearch() @@ -545,8 +627,7 @@ public class SearchFragment extends BaseMwmFragment hotelsFilter = mFilterController.getFilter(); mLastQueryTimestamp = System.nanoTime(); - // TODO @yunitsky Implement more elegant solution. - if (getActivity() instanceof MwmActivity) + if (isInteractiveSearch()) { SearchEngine.searchInteractive( getQuery(), mLastQueryTimestamp, true /* isMapAndTable */, hotelsFilter); @@ -572,19 +653,32 @@ public class SearchFragment extends BaseMwmFragment if (!isAdded() || !mToolbarController.hasQuery()) return; - // Search is running hence results updated. - mSearchRunning = true; - updateFrames(); - mSearchAdapter.refreshData(results); - mToolbarController.showProgress(true); - updateFilterButton(isHotel); + mSearchResults = results; + mIsHotel = isHotel; + + if (mAdsRequested) + { + UiThread.cancelDelayedTasks(mResultsShowingTask); + UiThread.runLater(mResultsShowingTask, RESULTS_DELAY_MS); + } + else + { + refreshSearchResults(); + } } @Override public void onResultsEnd(long timestamp) { - if (mSearchRunning && isAdded()) + if (mAdsRequested) + { + UiThread.cancelDelayedTasks(mSearchEndTask); + UiThread.runLater(mSearchEndTask, RESULTS_DELAY_MS); + } + else + { onSearchEnd(); + } } @Override @@ -606,6 +700,38 @@ public class SearchFragment extends BaseMwmFragment } } + private void refreshSearchResults() + { + // Search is running hence results updated. + stopAdsLoading(); + mSearchRunning = true; + updateFrames(); + mSearchAdapter.refreshData(combineResultsWithAds()); + mToolbarController.showProgress(true); + updateFilterButton(mIsHotel); + } + + @NonNull + private SearchData[] combineResultsWithAds() + { + if (mSearchResults.length < AD_POSITION || mGoogleAdView == null) + return mSearchResults; + + List result = new LinkedList<>(); + int counter = 0; + for (SearchResult r : mSearchResults) + { + if (r.type != SearchResultTypes.TYPE_SUGGEST && counter++ == AD_POSITION) + result.add(new GoogleAdsBanner(mGoogleAdView)); + else + result.add(r); + } + + SearchData[] resultArray = new SearchData[result.size()]; + result.toArray(resultArray); + return resultArray; + } + private void updateFilterButton(boolean isHotel) { if (mFilterController != null) @@ -666,4 +792,13 @@ public class SearchFragment extends BaseMwmFragment { return mToolbarController; } + + private void stopAdsLoading() + { + if (mAdsLoader == null) + return; + + mAdsLoader.cancelAdsLoading(); + mAdsRequested = false; + } } diff --git a/android/src/com/mapswithme/maps/search/SearchResult.java b/android/src/com/mapswithme/maps/search/SearchResult.java index b1003af705..c087310a0f 100644 --- a/android/src/com/mapswithme/maps/search/SearchResult.java +++ b/android/src/com/mapswithme/maps/search/SearchResult.java @@ -1,15 +1,15 @@ package com.mapswithme.maps.search; +import static com.mapswithme.maps.search.SearchResultTypes.TYPE_LOCAL_ADS_CUSTOMER; +import static com.mapswithme.maps.search.SearchResultTypes.TYPE_RESULT; +import static com.mapswithme.maps.search.SearchResultTypes.TYPE_SUGGEST; + /** * Class instances are created from native code. */ @SuppressWarnings("unused") -public class SearchResult +public class SearchResult implements SearchData { - public static final int TYPE_SUGGEST = 0; - public static final int TYPE_RESULT = 1; - public static final int TYPE_LOCAL_ADS_CUSTOMER = 2; - // Values should match osm::YesNoUnknown enum. public static final int OPEN_NOW_UNKNOWN = 0; public static final int OPEN_NOW_YES = 1; @@ -78,4 +78,10 @@ public class SearchResult this.description = description; this.highlightRanges = highlightRanges; } + + @Override + public int getItemViewType() + { + return type; + } } diff --git a/android/src/com/mapswithme/maps/search/SearchResultTypes.java b/android/src/com/mapswithme/maps/search/SearchResultTypes.java new file mode 100644 index 0000000000..03a8c5463c --- /dev/null +++ b/android/src/com/mapswithme/maps/search/SearchResultTypes.java @@ -0,0 +1,9 @@ +package com.mapswithme.maps.search; + +class SearchResultTypes +{ + static final int TYPE_SUGGEST = 0; + static final int TYPE_RESULT = 1; + static final int TYPE_LOCAL_ADS_CUSTOMER = 2; + static final int TYPE_GOOGLE_ADS = 3; +}