Commit b6b3464e authored by Zhiyuan Cai's avatar Zhiyuan Cai Committed by Chromium LUCI CQ

Initial changes for PriceWelcomeMessageCard component

This change adds a new message type PRICE_WELCOME to current message
scheme. A new layout tab_grid_price_message_card_item.xml is created
and a new UiType PRICE_MESSAGE is added correspondingly.
This feature is hidden behind the flag ENABLE_PRICE_TRACKING.
Some more specific logics such as where to show the message card
will be added in the later CLs, as well as the tests for this CL.

Bug: 1148020
Change-Id: If4d6b11762c9a16f6d73ee05bcee7f6bacfdcbf0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2556657
Commit-Queue: Zhiyuan Cai <zhiyuancai@google.com>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Reviewed-by: default avatarYue Zhang <yuezhanggg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834477}
parent b5274f04
...@@ -64,6 +64,7 @@ android_resources("java_resources") { ...@@ -64,6 +64,7 @@ android_resources("java_resources") {
"java/res/layout/iph_drag_and_drop_dialog_layout.xml", "java/res/layout/iph_drag_and_drop_dialog_layout.xml",
"java/res/layout/new_tab_tile_card_item.xml", "java/res/layout/new_tab_tile_card_item.xml",
"java/res/layout/price_card.xml", "java/res/layout/price_card.xml",
"java/res/layout/price_welcome_message_card_item.xml",
"java/res/layout/selectable_tab_grid_card_item.xml", "java/res/layout/selectable_tab_grid_card_item.xml",
"java/res/layout/selectable_tab_list_card_item.xml", "java/res/layout/selectable_tab_list_card_item.xml",
"java/res/layout/single_tab_view_layout.xml", "java/res/layout/single_tab_view_layout.xml",
...@@ -123,6 +124,10 @@ android_library("java") { ...@@ -123,6 +124,10 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/tasks/tab_management/NewTabTileViewBinder.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/NewTabTileViewBinder.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/NewTabTileViewProperties.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/NewTabTileViewProperties.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardView.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/PriceCardView.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageCardView.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageCardViewBinder.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageCardViewModel.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/PriceWelcomeMessageService.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabGridView.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabGridView.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java",
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<org.chromium.chrome.browser.tasks.tab_management.PriceWelcomeMessageCardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tab_grid_price_welcome_message_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/message_card_background_with_inset">
<LinearLayout
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical">
<org.chromium.chrome.browser.tasks.tab_management.PriceCardView
android:id="@+id/price_info_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.TextLarge.Primary"
android:gravity="center_horizontal" />
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
android:layout_marginStart ="24dp"
android:layout_marginEnd="24dp" />
<org.chromium.ui.widget.ButtonCompat
android:id="@+id/action_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/FilledButton.Flat"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:layout_marginStart ="24dp"
android:layout_marginEnd="24dp"
android:paddingTop="8dp"
android:paddingBottom="8dp" />
</LinearLayout>
<org.chromium.ui.widget.ChromeImageView
android:id="@+id/close_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@string/close"
android:layout_gravity="end"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:tint="@color/default_icon_color"/>
</org.chromium.chrome.browser.tasks.tab_management.PriceWelcomeMessageCardView>
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.tasks.tab_management; package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType.IPH; import static org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType.IPH;
import static org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType.PRICE_WELCOME;
import static org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType.TAB_SUGGESTION; import static org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType.TAB_SUGGESTION;
import android.content.Context; import android.content.Context;
...@@ -103,6 +104,11 @@ public class MessageCardProviderMediator implements MessageService.MessageObserv ...@@ -103,6 +104,11 @@ public class MessageCardProviderMediator implements MessageService.MessageObserv
assert data instanceof IphMessageService.IphMessageData; assert data instanceof IphMessageService.IphMessageData;
return IphMessageCardViewModel.create(mContext, this::invalidateShownMessage, return IphMessageCardViewModel.create(mContext, this::invalidateShownMessage,
(IphMessageService.IphMessageData) data); (IphMessageService.IphMessageData) data);
case PRICE_WELCOME:
assert data instanceof PriceWelcomeMessageService.PriceWelcomeMessageData;
return PriceWelcomeMessageCardViewModel.create(mContext,
this::invalidateShownMessage,
(PriceWelcomeMessageService.PriceWelcomeMessageData) data);
default: default:
return new PropertyModel.Builder(MessageCardViewProperties.ALL_KEYS) return new PropertyModel.Builder(MessageCardViewProperties.ALL_KEYS)
.with(MessageCardViewProperties.IS_INCOGNITO, false) .with(MessageCardViewProperties.IS_INCOGNITO, false)
......
...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.tasks.tab_management; ...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_ALPHA; import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_ALPHA;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_TYPE; import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_TYPE;
import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
...@@ -46,10 +47,16 @@ class MessageCardViewProperties { ...@@ -46,10 +47,16 @@ class MessageCardViewProperties {
new PropertyModel.WritableBooleanPropertyKey(); new PropertyModel.WritableBooleanPropertyKey();
public static final PropertyModel.WritableBooleanPropertyKey IS_INCOGNITO = public static final PropertyModel.WritableBooleanPropertyKey IS_INCOGNITO =
new PropertyModel.WritableBooleanPropertyKey(); new PropertyModel.WritableBooleanPropertyKey();
public static final PropertyModel.WritableObjectPropertyKey<String> TITLE_TEXT =
new PropertyModel.WritableObjectPropertyKey<>();
public static final PropertyModel
.WritableObjectPropertyKey<ShoppingPersistedTabData.PriceDrop> PRICE_DROP =
new PropertyModel.WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {ACTION_TEXT, DESCRIPTION_TEXT, public static final PropertyKey[] ALL_KEYS =
DESCRIPTION_TEXT_TEMPLATE, MESSAGE_TYPE, ICON_PROVIDER, UI_ACTION_PROVIDER, new PropertyKey[] {ACTION_TEXT, DESCRIPTION_TEXT, DESCRIPTION_TEXT_TEMPLATE,
UI_DISMISS_ACTION_PROVIDER, MESSAGE_SERVICE_ACTION_PROVIDER, MESSAGE_TYPE, ICON_PROVIDER, UI_ACTION_PROVIDER, UI_DISMISS_ACTION_PROVIDER,
MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER, DISMISS_BUTTON_CONTENT_DESCRIPTION, MESSAGE_SERVICE_ACTION_PROVIDER, MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER,
SHOULD_KEEP_AFTER_REVIEW, IS_ICON_VISIBLE, CARD_TYPE, CARD_ALPHA, IS_INCOGNITO}; DISMISS_BUTTON_CONTENT_DESCRIPTION, SHOULD_KEEP_AFTER_REVIEW, IS_ICON_VISIBLE,
CARD_TYPE, CARD_ALPHA, IS_INCOGNITO, TITLE_TEXT, PRICE_DROP};
} }
...@@ -18,13 +18,15 @@ import java.lang.annotation.RetentionPolicy; ...@@ -18,13 +18,15 @@ import java.lang.annotation.RetentionPolicy;
* understands. * understands.
*/ */
public class MessageService { public class MessageService {
@IntDef({MessageType.TAB_SUGGESTION, MessageType.IPH, MessageType.ALL}) @IntDef({MessageType.TAB_SUGGESTION, MessageType.IPH, MessageType.PRICE_WELCOME,
MessageType.ALL})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface MessageType { public @interface MessageType {
int FOR_TESTING = 0; int FOR_TESTING = 0;
int TAB_SUGGESTION = 1; int TAB_SUGGESTION = 1;
int IPH = 2; int IPH = 2;
int ALL = 3; int PRICE_WELCOME = 3;
int ALL = 4;
} }
/** /**
......
...@@ -19,6 +19,8 @@ public class PriceTrackingUtilities { ...@@ -19,6 +19,8 @@ public class PriceTrackingUtilities {
@VisibleForTesting @VisibleForTesting
public static final String TRACK_PRICES_ON_TABS = public static final String TRACK_PRICES_ON_TABS =
ChromePreferenceKeys.PRICE_TRACKING_TRACK_PRICES_ON_TABS; ChromePreferenceKeys.PRICE_TRACKING_TRACK_PRICES_ON_TABS;
private static final String PRICE_WELCOME_MESSAGE_CARD =
ChromePreferenceKeys.PRICE_TRACKING_PRICE_WELCOME_MESSAGE_CARD;
@VisibleForTesting @VisibleForTesting
public static final SharedPreferencesManager SHARED_PREFERENCES_MANAGER = public static final SharedPreferencesManager SHARED_PREFERENCES_MANAGER =
...@@ -40,4 +42,18 @@ public class PriceTrackingUtilities { ...@@ -40,4 +42,18 @@ public class PriceTrackingUtilities {
return SHARED_PREFERENCES_MANAGER.readBoolean( return SHARED_PREFERENCES_MANAGER.readBoolean(
TRACK_PRICES_ON_TABS, TabUiFeatureUtilities.isPriceTrackingEnabled()); TRACK_PRICES_ON_TABS, TabUiFeatureUtilities.isPriceTrackingEnabled());
} }
/**
* Forbid showing the PriceWelcomeMessageCard any more.
*/
public static void disablePriceWelcomeMessageCard() {
SHARED_PREFERENCES_MANAGER.writeBoolean(PRICE_WELCOME_MESSAGE_CARD, false);
}
/**
* @return Whether the PriceWelcomeMessageCard is disabled by users.
*/
public static boolean isPriceWelcomeMessageCardDisabled() {
return !SHARED_PREFERENCES_MANAGER.readBoolean(PRICE_WELCOME_MESSAGE_CARD, true);
}
} }
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tasks.tab_management;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
import org.chromium.chrome.tab_ui.R;
import org.chromium.ui.widget.ButtonCompat;
import org.chromium.ui.widget.ChromeImageView;
import java.lang.ref.WeakReference;
class PriceWelcomeMessageCardView extends FrameLayout {
private static WeakReference<Bitmap> sCloseButtonBitmapWeakRef;
private PriceCardView mPriceInfoBox;
private TextView mTitle;
private TextView mContent;
private ButtonCompat mActionButton;
private ChromeImageView mCloseButton;
public PriceWelcomeMessageCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mPriceInfoBox = findViewById(R.id.price_info_box);
mTitle = findViewById(R.id.title);
mContent = findViewById(R.id.content);
mActionButton = findViewById(R.id.action_button);
mCloseButton = findViewById(R.id.close_button);
if (sCloseButtonBitmapWeakRef == null || sCloseButtonBitmapWeakRef.get() == null) {
int closeButtonSize =
(int) getResources().getDimension(R.dimen.tab_grid_close_button_size);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.btn_close);
sCloseButtonBitmapWeakRef = new WeakReference<>(
Bitmap.createScaledBitmap(bitmap, closeButtonSize, closeButtonSize, true));
}
mCloseButton.setImageBitmap(sCloseButtonBitmapWeakRef.get());
}
/**
* Set title text.
* @param titleText Text to be displayed.
*/
void setTitleText(String titleText) {
mTitle.setText(titleText);
}
/**
* Set content text.
* @param contentText Text to be displayed.
*/
void setContentText(String contentText) {
mContent.setText(contentText);
}
/**
* Set action text for the action button.
* @param actionText Text to be displayed.
*/
void setActionText(String actionText) {
mActionButton.setText(actionText);
}
/**
* Set click listener for the action button.
* @param listener {@link android.view.View.OnClickListener} for the action button.
*/
void setActionButtonOnClickListener(OnClickListener listener) {
mActionButton.setOnClickListener(listener);
}
/**
* Set content description for dismiss button.
* @param description The content description.
*/
void setDismissButtonContentDescription(String description) {
mCloseButton.setContentDescription(description);
}
/**
* Set {@link android.view.View.OnClickListener} for dismiss button.
* @param listener {@link android.view.View.OnClickListener} to set.
*/
void setDismissButtonOnClickListener(OnClickListener listener) {
mCloseButton.setOnClickListener(listener);
}
/**
* Set price strings for the price info box.
*/
void setPriceInfoBoxStrings(ShoppingPersistedTabData.PriceDrop priceDrop) {
mPriceInfoBox.setPriceStrings(priceDrop.price, priceDrop.previousPrice);
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_ALPHA;
import android.view.ViewGroup;
import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
/**
* ViewBinder for PriceWelcomeMessageCardItem.
*/
class PriceWelcomeMessageCardViewBinder {
public static void bind(PropertyModel model, ViewGroup view, PropertyKey propertyKey) {
assert view instanceof PriceWelcomeMessageCardView;
PriceWelcomeMessageCardView itemView = (PriceWelcomeMessageCardView) view;
if (MessageCardViewProperties.ACTION_TEXT == propertyKey) {
itemView.setActionText(model.get(MessageCardViewProperties.ACTION_TEXT));
itemView.setActionButtonOnClickListener(v -> {
MessageCardView.ReviewActionProvider uiProvider =
model.get(MessageCardViewProperties.UI_ACTION_PROVIDER);
if (uiProvider != null) uiProvider.review();
MessageCardView.ReviewActionProvider serviceProvider =
model.get(MessageCardViewProperties.MESSAGE_SERVICE_ACTION_PROVIDER);
if (serviceProvider != null) serviceProvider.review();
MessageCardView.DismissActionProvider uiDismissProvider =
model.get(MessageCardViewProperties.UI_DISMISS_ACTION_PROVIDER);
if (uiDismissProvider != null
&& !model.get(MessageCardViewProperties.SHOULD_KEEP_AFTER_REVIEW)) {
uiDismissProvider.dismiss(model.get(MessageCardViewProperties.MESSAGE_TYPE));
}
});
} else if (MessageCardViewProperties.TITLE_TEXT == propertyKey) {
itemView.setTitleText(model.get(MessageCardViewProperties.TITLE_TEXT));
} else if (MessageCardViewProperties.DESCRIPTION_TEXT == propertyKey) {
itemView.setContentText(model.get(MessageCardViewProperties.DESCRIPTION_TEXT));
} else if (MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION == propertyKey) {
itemView.setDismissButtonContentDescription(
model.get(MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION));
itemView.setDismissButtonOnClickListener(v -> {
int type = model.get(MessageCardViewProperties.MESSAGE_TYPE);
MessageCardView.DismissActionProvider uiProvider =
model.get(MessageCardViewProperties.UI_DISMISS_ACTION_PROVIDER);
if (uiProvider != null) uiProvider.dismiss(type);
MessageCardView.DismissActionProvider serviceProvider = model.get(
MessageCardViewProperties.MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER);
if (serviceProvider != null) serviceProvider.dismiss(type);
});
} else if (MessageCardViewProperties.PRICE_DROP == propertyKey) {
ShoppingPersistedTabData.PriceDrop priceDrop =
model.get(MessageCardViewProperties.PRICE_DROP);
if (priceDrop != null) itemView.setPriceInfoBoxStrings(priceDrop);
} else if (CARD_ALPHA == propertyKey) {
itemView.setAlpha(model.get(CARD_ALPHA));
}
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_ALPHA;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_TYPE;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.ModelType.MESSAGE;
import android.content.Context;
import org.chromium.chrome.tab_ui.R;
import org.chromium.ui.modelutil.PropertyModel;
/**
* This is a util class for creating the property model of the {@link PriceWelcomeMessageCardView}.
*/
public class PriceWelcomeMessageCardViewModel {
/**
* Create a {@link PropertyModel} for {@link PriceWelcomeMessageCardView}.
* @param context The {@link Context} to use.
* @param uiDismissActionProvider The {@link MessageCardView.DismissActionProvider} to set.
* @param data The {@link PriceWelcomeMessageService.PriceWelcomeMessageData} to use.
* @return A {@link PropertyModel} for the given {@code data}.
*/
public static PropertyModel create(Context context,
MessageCardView.DismissActionProvider uiDismissActionProvider,
PriceWelcomeMessageService.PriceWelcomeMessageData data) {
String titleText = context.getString(R.string.price_drop_spotted_title);
String contentText = context.getString(R.string.price_drop_spotted_content);
String actionText = context.getString(R.string.price_drop_spotted_show_me);
String dismissButtonContextDescription =
context.getString(R.string.accessibility_tab_suggestion_dismiss_button);
return new PropertyModel.Builder(MessageCardViewProperties.ALL_KEYS)
.with(MessageCardViewProperties.MESSAGE_TYPE,
MessageService.MessageType.PRICE_WELCOME)
.with(MessageCardViewProperties.UI_DISMISS_ACTION_PROVIDER, uiDismissActionProvider)
.with(MessageCardViewProperties.MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER,
data.getDismissActionProvider())
.with(MessageCardViewProperties.MESSAGE_SERVICE_ACTION_PROVIDER,
data.getReviewActionProvider())
.with(MessageCardViewProperties.DESCRIPTION_TEXT, contentText)
.with(MessageCardViewProperties.DESCRIPTION_TEXT_TEMPLATE, null)
.with(MessageCardViewProperties.ACTION_TEXT, actionText)
.with(MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION,
dismissButtonContextDescription)
.with(MessageCardViewProperties.SHOULD_KEEP_AFTER_REVIEW, false)
.with(MessageCardViewProperties.IS_ICON_VISIBLE, false)
.with(MessageCardViewProperties.IS_INCOGNITO, false)
.with(MessageCardViewProperties.TITLE_TEXT, titleText)
.with(MessageCardViewProperties.PRICE_DROP, data.getPriceDrop())
.with(CARD_TYPE, MESSAGE)
.with(CARD_ALPHA, 1f)
.build();
}
}
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tasks.tab_management;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
/**
* One of the concrete {@link MessageService} that only serves {@link MessageType#PRICE_WELCOME}.
*/
public class PriceWelcomeMessageService extends MessageService {
/**
* Provides the binding tab ID and the price drop of the binding tab.
*/
static class PriceTabData {
public final int bindingTabId;
public final ShoppingPersistedTabData.PriceDrop priceDrop;
PriceTabData(int bindingTabId, ShoppingPersistedTabData.PriceDrop priceDrop) {
this.bindingTabId = bindingTabId;
this.priceDrop = priceDrop;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof PriceTabData)) return false;
PriceTabData priceTabData = (PriceTabData) object;
return this.bindingTabId == priceTabData.bindingTabId
&& this.priceDrop.equals(priceTabData.priceDrop);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + bindingTabId;
result = 31 * result + (priceDrop == null ? 0 : priceDrop.hashCode());
return result;
}
}
/**
* An interface to help build the PriceWelcomeMessage.
*/
public interface PriceWelcomeMessageProvider {
/**
* This method gets the information of the first tab showing price card.
*
* @return The PriceTabData including the tab ID and the price drop.
*/
PriceTabData getFirstTabShowingPriceCard();
/**
* This method gets the tab index from tab ID.
*
* @param tabId The tab ID to search for.
* @return the index within the {@link TabListModel}.
*/
int getTabIndexFromTabId(int tabId);
}
/**
* An interface to handle the review action of PriceWelcomeMessage.
*/
public interface PriceWelcomeMessageReviewActionProvider {
/**
* This method scrolls to the binding tab of the PriceWelcomeMessage.
*
* @param tabIndex The index of the {@link Tab} that is binding to PriceWelcomeMessage.
*/
void scrollToBindingTab(int tabIndex);
}
/**
* This is the data type that this MessageService is serving to its Observer.
*/
class PriceWelcomeMessageData implements MessageData {
private final ShoppingPersistedTabData.PriceDrop mPriceDrop;
private final MessageCardView.ReviewActionProvider mReviewActionProvider;
private final MessageCardView.DismissActionProvider mDismissActionProvider;
PriceWelcomeMessageData(ShoppingPersistedTabData.PriceDrop priceDrop,
MessageCardView.ReviewActionProvider reviewActionProvider,
MessageCardView.DismissActionProvider dismissActionProvider) {
mPriceDrop = priceDrop;
mReviewActionProvider = reviewActionProvider;
mDismissActionProvider = dismissActionProvider;
}
/**
* @return The {@link MessageCardViewProperties#PRICE_DROP} for the associated
* PRICE_WELCOME.
*/
ShoppingPersistedTabData.PriceDrop getPriceDrop() {
return mPriceDrop;
}
/**
* @return The {@link MessageCardView.ReviewActionProvider} for the associated
* PRICE_WELCOME.
*/
MessageCardView.ReviewActionProvider getReviewActionProvider() {
return mReviewActionProvider;
}
/**
* @return The {@link MessageCardView.DismissActionProvider} for the associated
* PRICE_WELCOME.
*/
MessageCardView.DismissActionProvider getDismissActionProvider() {
return mDismissActionProvider;
}
}
private final PriceWelcomeMessageProvider mPriceWelcomeMessageProvider;
private final PriceWelcomeMessageReviewActionProvider mPriceWelcomeMessageReviewActionProvider;
private PriceTabData mPriceTabData;
PriceWelcomeMessageService(PriceWelcomeMessageProvider priceWelcomeMessageProvider,
PriceWelcomeMessageReviewActionProvider priceWelcomeMessageReviewActionProvider) {
super(MessageType.PRICE_WELCOME);
mPriceTabData = null;
mPriceWelcomeMessageProvider = priceWelcomeMessageProvider;
mPriceWelcomeMessageReviewActionProvider = priceWelcomeMessageReviewActionProvider;
}
void preparePriceMessage() {
if (PriceTrackingUtilities.isPriceWelcomeMessageCardDisabled()) return;
PriceTabData priceTabData = mPriceWelcomeMessageProvider.getFirstTabShowingPriceCard();
if (priceTabData == null) {
mPriceTabData = null;
sendInvalidNotification();
} else if (!priceTabData.equals(mPriceTabData)) {
mPriceTabData = priceTabData;
sendInvalidNotification();
sendAvailabilityNotification(new PriceWelcomeMessageData(
mPriceTabData.priceDrop, this::review, (int messageType) -> dismiss()));
}
}
private void review() {
assert mPriceTabData != null;
mPriceWelcomeMessageReviewActionProvider.scrollToBindingTab(
mPriceWelcomeMessageProvider.getTabIndexFromTabId(mPriceTabData.bindingTabId));
PriceTrackingUtilities.disablePriceWelcomeMessageCard();
}
private void dismiss() {
PriceTrackingUtilities.disablePriceWelcomeMessageCard();
}
}
...@@ -101,6 +101,7 @@ public class TabGridItemTouchHelperCallback extends ItemTouchHelper.SimpleCallba ...@@ -101,6 +101,7 @@ public class TabGridItemTouchHelperCallback extends ItemTouchHelper.SimpleCallba
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final int dragFlags = viewHolder.getItemViewType() == TabProperties.UiType.MESSAGE final int dragFlags = viewHolder.getItemViewType() == TabProperties.UiType.MESSAGE
|| viewHolder.getItemViewType() == TabProperties.UiType.NEW_TAB_TILE || viewHolder.getItemViewType() == TabProperties.UiType.NEW_TAB_TILE
|| viewHolder.getItemViewType() == TabProperties.UiType.PRICE_WELCOME
? 0 ? 0
: mDragFlags; : mDragFlags;
final int swipeFlags = viewHolder.getItemViewType() == TabProperties.UiType.NEW_TAB_TILE final int swipeFlags = viewHolder.getItemViewType() == TabProperties.UiType.NEW_TAB_TILE
...@@ -114,7 +115,8 @@ public class TabGridItemTouchHelperCallback extends ItemTouchHelper.SimpleCallba ...@@ -114,7 +115,8 @@ public class TabGridItemTouchHelperCallback extends ItemTouchHelper.SimpleCallba
public boolean canDropOver(@NonNull RecyclerView recyclerView, public boolean canDropOver(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder current, @NonNull RecyclerView.ViewHolder target) { @NonNull RecyclerView.ViewHolder current, @NonNull RecyclerView.ViewHolder target) {
if (target.getItemViewType() == TabProperties.UiType.MESSAGE if (target.getItemViewType() == TabProperties.UiType.MESSAGE
|| target.getItemViewType() == TabProperties.UiType.NEW_TAB_TILE) { || target.getItemViewType() == TabProperties.UiType.NEW_TAB_TILE
|| target.getItemViewType() == TabProperties.UiType.PRICE_WELCOME) {
return false; return false;
} }
return super.canDropOver(recyclerView, current, target); return super.canDropOver(recyclerView, current, target);
......
...@@ -225,15 +225,19 @@ class TabGridViewBinder { ...@@ -225,15 +225,19 @@ class TabGridViewBinder {
.fetch((shoppingPersistedTabData) -> { .fetch((shoppingPersistedTabData) -> {
if (shoppingPersistedTabData.getPriceDrop() == null) { if (shoppingPersistedTabData.getPriceDrop() == null) {
priceCardView.setVisibility(View.GONE); priceCardView.setVisibility(View.GONE);
model.set(TabProperties.PRICE_DROP, null);
} else { } else {
priceCardView.setPriceStrings( priceCardView.setPriceStrings(
shoppingPersistedTabData.getPriceDrop().price, shoppingPersistedTabData.getPriceDrop().price,
shoppingPersistedTabData.getPriceDrop().previousPrice); shoppingPersistedTabData.getPriceDrop().previousPrice);
priceCardView.setVisibility(View.VISIBLE); priceCardView.setVisibility(View.VISIBLE);
model.set(TabProperties.PRICE_DROP,
shoppingPersistedTabData.getPriceDrop());
} }
}); });
} else { } else {
priceCardView.setVisibility(View.GONE); priceCardView.setVisibility(View.GONE);
model.set(TabProperties.PRICE_DROP, null);
} }
} else if (TabProperties.PAGE_INFO_LISTENER == propertyKey) { } else if (TabProperties.PAGE_INFO_LISTENER == propertyKey) {
TabListMediator.TabActionListener listener = TabListMediator.TabActionListener listener =
......
...@@ -30,6 +30,7 @@ import org.chromium.chrome.browser.profiles.Profile; ...@@ -30,6 +30,7 @@ import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab; import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
import org.chromium.chrome.browser.tasks.tab_management.PriceWelcomeMessageService.PriceTabData;
import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType; import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration; import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
import org.chromium.chrome.tab_ui.R; import org.chromium.chrome.tab_ui.R;
...@@ -48,7 +49,8 @@ import java.util.List; ...@@ -48,7 +49,8 @@ import java.util.List;
/** /**
* Coordinator for showing UI for a list of tabs. Can be used in GRID or STRIP modes. * Coordinator for showing UI for a list of tabs. Can be used in GRID or STRIP modes.
*/ */
public class TabListCoordinator implements Destroyable { public class TabListCoordinator
implements PriceWelcomeMessageService.PriceWelcomeMessageProvider, Destroyable {
/** /**
* Modes of showing the list of tabs. * Modes of showing the list of tabs.
* *
...@@ -338,6 +340,13 @@ public class TabListCoordinator implements Destroyable { ...@@ -338,6 +340,13 @@ public class TabListCoordinator implements Destroyable {
return tabListRect.top; return tabListRect.top;
} }
/**
* @return The index of the model list where the PriceWelcomeMessage should be inserted.
*/
int getPriceWelcomeMessageIndex() {
return mModel.size();
}
/** /**
* @return The container {@link androidx.recyclerview.widget.RecyclerView} that is showing the * @return The container {@link androidx.recyclerview.widget.RecyclerView} that is showing the
* tab list UI. * tab list UI.
...@@ -444,4 +453,15 @@ public class TabListCoordinator implements Destroyable { ...@@ -444,4 +453,15 @@ public class TabListCoordinator implements Destroyable {
void removeSpecialListItem(@UiType int uiType, int itemIdentifier) { void removeSpecialListItem(@UiType int uiType, int itemIdentifier) {
mMediator.removeSpecialItemFromModel(uiType, itemIdentifier); mMediator.removeSpecialItemFromModel(uiType, itemIdentifier);
} }
// PriceWelcomeMessageService.PriceWelcomeMessageProvider implementation.
@Override
public PriceTabData getFirstTabShowingPriceCard() {
return mModel.getFirstTabShowingPriceCard();
}
@Override
public int getTabIndexFromTabId(int tabId) {
return mModel.indexFromId(tabId);
}
} }
...@@ -1138,7 +1138,10 @@ class TabListMediator { ...@@ -1138,7 +1138,10 @@ class TabListMediator {
public int getSpanSize(int position) { public int getSpanSize(int position) {
int itemType = mModel.get(position).type; int itemType = mModel.get(position).type;
if (itemType == TabProperties.UiType.MESSAGE) return manager.getSpanCount(); if (itemType == TabProperties.UiType.MESSAGE
|| itemType == TabProperties.UiType.PRICE_WELCOME) {
return manager.getSpanCount();
}
return 1; return 1;
} }
}); });
...@@ -1286,6 +1289,7 @@ class TabListMediator { ...@@ -1286,6 +1289,7 @@ class TabListMediator {
.with(TabProperties.TABSTRIP_FAVICON_BACKGROUND_COLOR_ID, .with(TabProperties.TABSTRIP_FAVICON_BACKGROUND_COLOR_ID,
tabstripFaviconBackgroundDrawableId) tabstripFaviconBackgroundDrawableId)
.with(TabProperties.ACCESSIBILITY_DELEGATE, mAccessibilityDelegate) .with(TabProperties.ACCESSIBILITY_DELEGATE, mAccessibilityDelegate)
.with(TabProperties.PRICE_DROP, null)
.with(CARD_TYPE, TAB) .with(CARD_TYPE, TAB)
.build(); .build();
...@@ -1562,7 +1566,7 @@ class TabListMediator { ...@@ -1562,7 +1566,7 @@ class TabListMediator {
*/ */
void removeSpecialItemFromModel(@UiType int uiType, int itemIdentifier) { void removeSpecialItemFromModel(@UiType int uiType, int itemIdentifier) {
int index = TabModel.INVALID_TAB_INDEX; int index = TabModel.INVALID_TAB_INDEX;
if (uiType == UiType.MESSAGE) { if (uiType == UiType.MESSAGE || uiType == UiType.PRICE_WELCOME) {
if (itemIdentifier == MessageService.MessageType.ALL) { if (itemIdentifier == MessageService.MessageType.ALL) {
while (mModel.lastIndexForMessageItem() != TabModel.INVALID_TAB_INDEX) { while (mModel.lastIndexForMessageItem() != TabModel.INVALID_TAB_INDEX) {
index = mModel.lastIndexForMessageItem(); index = mModel.lastIndexForMessageItem();
...@@ -1582,7 +1586,7 @@ class TabListMediator { ...@@ -1582,7 +1586,7 @@ class TabListMediator {
} }
private boolean validateItemAt(int index, @UiType int uiType, int itemIdentifier) { private boolean validateItemAt(int index, @UiType int uiType, int itemIdentifier) {
if (uiType == UiType.MESSAGE) { if (uiType == UiType.MESSAGE || uiType == UiType.PRICE_WELCOME) {
return mModel.get(index).type == uiType return mModel.get(index).type == uiType
&& mModel.get(index).model.get(MESSAGE_TYPE) == itemIdentifier; && mModel.get(index).model.get(MESSAGE_TYPE) == itemIdentifier;
} else if (uiType == UiType.NEW_TAB_TILE) { } else if (uiType == UiType.NEW_TAB_TILE) {
......
...@@ -19,6 +19,7 @@ import androidx.annotation.IntDef; ...@@ -19,6 +19,7 @@ import androidx.annotation.IntDef;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tasks.tab_management.PriceWelcomeMessageService.PriceTabData;
import org.chromium.ui.modelutil.MVCListAdapter; import org.chromium.ui.modelutil.MVCListAdapter;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList; import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
import org.chromium.ui.modelutil.PropertyListModel; import org.chromium.ui.modelutil.PropertyListModel;
...@@ -203,4 +204,18 @@ class TabListModel extends ModelList { ...@@ -203,4 +204,18 @@ class TabListModel extends ModelList {
get(index).model.set(TabProperties.CARD_ANIMATION_STATUS, status); get(index).model.set(TabProperties.CARD_ANIMATION_STATUS, status);
} }
/**
* Get the first tab showing price card.
*/
public PriceTabData getFirstTabShowingPriceCard() {
for (int i = 0; i < size(); i++) {
PropertyModel model = get(i).model;
if ((model.get(CARD_TYPE) == TAB) && (model.get(TabProperties.PRICE_DROP) != null)) {
return new PriceTabData(
model.get(TabProperties.TAB_ID), model.get(TabProperties.PRICE_DROP));
}
}
return null;
}
} }
...@@ -13,6 +13,7 @@ import android.view.View.AccessibilityDelegate; ...@@ -13,6 +13,7 @@ import android.view.View.AccessibilityDelegate;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate; import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
...@@ -28,7 +29,7 @@ import java.lang.annotation.RetentionPolicy; ...@@ -28,7 +29,7 @@ import java.lang.annotation.RetentionPolicy;
public class TabProperties { public class TabProperties {
/** IDs for possible types of UI in the tab list. */ /** IDs for possible types of UI in the tab list. */
@IntDef({UiType.SELECTABLE, UiType.CLOSABLE, UiType.STRIP, UiType.MESSAGE, UiType.DIVIDER, @IntDef({UiType.SELECTABLE, UiType.CLOSABLE, UiType.STRIP, UiType.MESSAGE, UiType.DIVIDER,
UiType.NEW_TAB_TILE}) UiType.NEW_TAB_TILE, UiType.PRICE_WELCOME})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface UiType { public @interface UiType {
int SELECTABLE = 0; int SELECTABLE = 0;
...@@ -37,6 +38,7 @@ public class TabProperties { ...@@ -37,6 +38,7 @@ public class TabProperties {
int MESSAGE = 3; int MESSAGE = 3;
int DIVIDER = 4; int DIVIDER = 4;
int NEW_TAB_TILE = 5; int NEW_TAB_TILE = 5;
int PRICE_WELCOME = 6;
} }
public static final PropertyModel.WritableIntPropertyKey TAB_ID = public static final PropertyModel.WritableIntPropertyKey TAB_ID =
...@@ -118,6 +120,9 @@ public class TabProperties { ...@@ -118,6 +120,9 @@ public class TabProperties {
public static final WritableObjectPropertyKey<String> CLOSE_BUTTON_DESCRIPTION_STRING = public static final WritableObjectPropertyKey<String> CLOSE_BUTTON_DESCRIPTION_STRING =
new WritableObjectPropertyKey<>(); new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<ShoppingPersistedTabData.PriceDrop> PRICE_DROP =
new WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS_TAB_GRID = new PropertyKey[] {TAB_ID, public static final PropertyKey[] ALL_KEYS_TAB_GRID = new PropertyKey[] {TAB_ID,
TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON, THUMBNAIL_FETCHER, IPH_PROVIDER, TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON, THUMBNAIL_FETCHER, IPH_PROVIDER,
TITLE, IS_SELECTED, CHECKED_DRAWABLE_STATE_LIST, CREATE_GROUP_LISTENER, CARD_ALPHA, TITLE, IS_SELECTED, CHECKED_DRAWABLE_STATE_LIST, CREATE_GROUP_LISTENER, CARD_ALPHA,
...@@ -127,7 +132,7 @@ public class TabProperties { ...@@ -127,7 +132,7 @@ public class TabProperties {
SELECTABLE_TAB_ACTION_BUTTON_SELECTED_BACKGROUND, URL_DOMAIN, ACCESSIBILITY_DELEGATE, SELECTABLE_TAB_ACTION_BUTTON_SELECTED_BACKGROUND, URL_DOMAIN, ACCESSIBILITY_DELEGATE,
SEARCH_QUERY, PAGE_INFO_LISTENER, PAGE_INFO_ICON_DRAWABLE_ID, CARD_TYPE, SEARCH_QUERY, PAGE_INFO_LISTENER, PAGE_INFO_ICON_DRAWABLE_ID, CARD_TYPE,
CONTENT_DESCRIPTION_STRING, CLOSE_BUTTON_DESCRIPTION_STRING, CONTENT_DESCRIPTION_STRING, CLOSE_BUTTON_DESCRIPTION_STRING,
SHOPPING_PERSISTED_TAB_DATA_FETCHER}; SHOPPING_PERSISTED_TAB_DATA_FETCHER, PRICE_DROP};
public static final PropertyKey[] ALL_KEYS_TAB_STRIP = public static final PropertyKey[] ALL_KEYS_TAB_STRIP =
new PropertyKey[] {TAB_ID, TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON, new PropertyKey[] {TAB_ID, TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON,
......
...@@ -109,6 +109,7 @@ public class TabSwitcherCoordinator ...@@ -109,6 +109,7 @@ public class TabSwitcherCoordinator
private ViewGroup mContainer; private ViewGroup mContainer;
private TabCreatorManager mTabCreatorManager; private TabCreatorManager mTabCreatorManager;
private boolean mIsInitialized; private boolean mIsInitialized;
private PriceWelcomeMessageService mPriceWelcomeMessageService;
private final ViewGroup mRootView; private final ViewGroup mRootView;
private final MenuOrKeyboardActionController private final MenuOrKeyboardActionController
...@@ -211,9 +212,14 @@ public class TabSwitcherCoordinator ...@@ -211,9 +212,14 @@ public class TabSwitcherCoordinator
mMessageCardProviderCoordinator = new MessageCardProviderCoordinator( mMessageCardProviderCoordinator = new MessageCardProviderCoordinator(
context, tabModelSelector::isIncognitoSelected, (identifier) -> { context, tabModelSelector::isIncognitoSelected, (identifier) -> {
mTabListCoordinator.removeSpecialListItem( if (identifier == MessageService.MessageType.PRICE_WELCOME) {
TabProperties.UiType.MESSAGE, identifier); mTabListCoordinator.removeSpecialListItem(
appendNextMessage(identifier); TabProperties.UiType.PRICE_WELCOME, identifier);
} else {
mTabListCoordinator.removeSpecialListItem(
TabProperties.UiType.MESSAGE, identifier);
appendNextMessage(identifier);
}
}); });
if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled()) { if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled()) {
...@@ -238,6 +244,12 @@ public class TabSwitcherCoordinator ...@@ -238,6 +244,12 @@ public class TabSwitcherCoordinator
new LayoutViewBuilder(R.layout.new_tab_tile_card_item), new LayoutViewBuilder(R.layout.new_tab_tile_card_item),
NewTabTileViewBinder::bind); NewTabTileViewBinder::bind);
} }
if (TabUiFeatureUtilities.isPriceTrackingEnabled()) {
mTabListCoordinator.registerItemType(TabProperties.UiType.PRICE_WELCOME,
new LayoutViewBuilder(R.layout.price_welcome_message_card_item),
PriceWelcomeMessageCardViewBinder::bind);
}
} }
if (CachedFeatureFlags.isEnabled(ChromeFeatureList.INSTANT_START) if (CachedFeatureFlags.isEnabled(ChromeFeatureList.INSTANT_START)
...@@ -308,6 +320,13 @@ public class TabSwitcherCoordinator ...@@ -308,6 +320,13 @@ public class TabSwitcherCoordinator
new IphMessageService(mTabGridIphDialogCoordinator); new IphMessageService(mTabGridIphDialogCoordinator);
mMessageCardProviderCoordinator.subscribeMessageService(iphMessageService); mMessageCardProviderCoordinator.subscribeMessageService(iphMessageService);
} }
if (TabUiFeatureUtilities.isPriceTrackingEnabled()) {
mPriceWelcomeMessageService =
new PriceWelcomeMessageService(mTabListCoordinator, mMediator);
mMessageCardProviderCoordinator.subscribeMessageService(
mPriceWelcomeMessageService);
}
} }
mIsInitialized = true; mIsInitialized = true;
} }
...@@ -435,10 +454,12 @@ public class TabSwitcherCoordinator ...@@ -435,10 +454,12 @@ public class TabSwitcherCoordinator
public boolean resetWithTabs( public boolean resetWithTabs(
@Nullable List<PseudoTab> tabs, boolean quickMode, boolean mruMode) { @Nullable List<PseudoTab> tabs, boolean quickMode, boolean mruMode) {
mMediator.registerFirstMeaningfulPaintRecorder(); mMediator.registerFirstMeaningfulPaintRecorder();
// Make sure that before resetWithListOfTabs, there are no messages in the middle of tabs in
// our TabListModel.
removeAllAppendedMessage();
boolean showQuickly = mTabListCoordinator.resetWithListOfTabs(tabs, quickMode, mruMode); boolean showQuickly = mTabListCoordinator.resetWithListOfTabs(tabs, quickMode, mruMode);
if (showQuickly) { if (showQuickly) {
mTabListCoordinator.removeSpecialListItem(TabProperties.UiType.NEW_TAB_TILE, 0); mTabListCoordinator.removeSpecialListItem(TabProperties.UiType.NEW_TAB_TILE, 0);
removeAllAppendedMessage();
} }
int cardsCount = tabs == null ? 0 : tabs.size(); int cardsCount = tabs == null ? 0 : tabs.size();
...@@ -447,7 +468,13 @@ public class TabSwitcherCoordinator ...@@ -447,7 +468,13 @@ public class TabSwitcherCoordinator
mNewTabTileCoordinator.getModel()); mNewTabTileCoordinator.getModel());
cardsCount += 1; cardsCount += 1;
} }
if (tabs != null && tabs.size() > 0) appendMessagesTo(cardsCount);
if (tabs != null && tabs.size() > 0) {
if (mPriceWelcomeMessageService != null) {
mPriceWelcomeMessageService.preparePriceMessage();
}
appendMessagesTo(cardsCount);
}
return showQuickly; return showQuickly;
} }
...@@ -466,8 +493,14 @@ public class TabSwitcherCoordinator ...@@ -466,8 +493,14 @@ public class TabSwitcherCoordinator
List<MessageCardProviderMediator.Message> messages = List<MessageCardProviderMediator.Message> messages =
mMessageCardProviderCoordinator.getMessageItems(); mMessageCardProviderCoordinator.getMessageItems();
for (int i = 0; i < messages.size(); i++) { for (int i = 0; i < messages.size(); i++) {
mTabListCoordinator.addSpecialListItemToEnd( if (messages.get(i).type == MessageService.MessageType.PRICE_WELCOME) {
TabProperties.UiType.MESSAGE, messages.get(i).model); mTabListCoordinator.addSpecialListItem(
mTabListCoordinator.getPriceWelcomeMessageIndex(),
TabProperties.UiType.PRICE_WELCOME, messages.get(i).model);
} else {
mTabListCoordinator.addSpecialListItemToEnd(
TabProperties.UiType.MESSAGE, messages.get(i).model);
}
} }
sAppendedMessagesForTesting = messages.size() > 0; sAppendedMessagesForTesting = messages.size() > 0;
} }
...@@ -478,8 +511,14 @@ public class TabSwitcherCoordinator ...@@ -478,8 +511,14 @@ public class TabSwitcherCoordinator
List<MessageCardProviderMediator.Message> messages = List<MessageCardProviderMediator.Message> messages =
mMessageCardProviderCoordinator.getMessageItems(); mMessageCardProviderCoordinator.getMessageItems();
for (int i = 0; i < messages.size(); i++) { for (int i = 0; i < messages.size(); i++) {
mTabListCoordinator.addSpecialListItem( if (messages.get(i).type == MessageService.MessageType.PRICE_WELCOME) {
index + i, TabProperties.UiType.MESSAGE, messages.get(i).model); mTabListCoordinator.addSpecialListItem(
mTabListCoordinator.getPriceWelcomeMessageIndex(),
TabProperties.UiType.PRICE_WELCOME, messages.get(i).model);
} else {
mTabListCoordinator.addSpecialListItem(
index + i, TabProperties.UiType.MESSAGE, messages.get(i).model);
}
} }
if (messages.size() > 0) sAppendedMessagesForTesting = true; if (messages.size() > 0) sAppendedMessagesForTesting = true;
} }
......
...@@ -65,8 +65,10 @@ import java.util.List; ...@@ -65,8 +65,10 @@ import java.util.List;
* The Mediator that is responsible for resetting the tab grid or carousel based on visibility and * The Mediator that is responsible for resetting the tab grid or carousel based on visibility and
* model changes. * model changes.
*/ */
class TabSwitcherMediator implements TabSwitcher.Controller, TabListRecyclerView.VisibilityListener, class TabSwitcherMediator
TabListMediator.GridCardOnClickListenerProvider { implements TabSwitcher.Controller, TabListRecyclerView.VisibilityListener,
TabListMediator.GridCardOnClickListenerProvider,
PriceWelcomeMessageService.PriceWelcomeMessageReviewActionProvider {
private static final String TAG = "TabSwitcherMediator"; private static final String TAG = "TabSwitcherMediator";
// This should be the same as TabListCoordinator.GRID_LAYOUT_SPAN_COUNT for the selected tab // This should be the same as TabListCoordinator.GRID_LAYOUT_SPAN_COUNT for the selected tab
...@@ -778,6 +780,11 @@ class TabSwitcherMediator implements TabSwitcher.Controller, TabListRecyclerView ...@@ -778,6 +780,11 @@ class TabSwitcherMediator implements TabSwitcher.Controller, TabListRecyclerView
} }
} }
@Override
public void scrollToBindingTab(int tabIndex) {
mContainerViewModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, tabIndex);
}
private boolean ableToOpenDialog(Tab tab) { private boolean ableToOpenDialog(Tab tab) {
return TabUiFeatureUtilities.isTabGroupsAndroidEnabled() return TabUiFeatureUtilities.isTabGroupsAndroidEnabled()
&& mTabModelSelector.isIncognitoSelected() == tab.isIncognito() && mTabModelSelector.isIncognitoSelected() == tab.isIncognito()
......
...@@ -531,6 +531,11 @@ public final class ChromePreferenceKeys { ...@@ -531,6 +531,11 @@ public final class ChromePreferenceKeys {
public static final String PREFETCH_NOTIFICATION_TIME = "prefetch_notification_shown_time"; public static final String PREFETCH_NOTIFICATION_TIME = "prefetch_notification_shown_time";
public static final String PREFETCH_OFFLINE_COUNTER = "prefetch_notification_offline_counter"; public static final String PREFETCH_OFFLINE_COUNTER = "prefetch_notification_offline_counter";
/**
* Whether users disable the PriceWelcomeMessageCard.
*/
public static final String PRICE_TRACKING_PRICE_WELCOME_MESSAGE_CARD =
"Chrome.PriceTracking.PriceWelcome";
/** /**
* Whether users turn on the feature track prices on tabs. * Whether users turn on the feature track prices on tabs.
*/ */
...@@ -837,6 +842,7 @@ public final class ChromePreferenceKeys { ...@@ -837,6 +842,7 @@ public final class ChromePreferenceKeys {
IMAGE_DESCRIPTIONS_JUST_ONCE_COUNT, IMAGE_DESCRIPTIONS_JUST_ONCE_COUNT,
IMAGE_DESCRIPTIONS_DONT_ASK_AGAIN, IMAGE_DESCRIPTIONS_DONT_ASK_AGAIN,
PERSISTENT_OFFLINE_CONTENT_AVAILABILITY_STATUS, PERSISTENT_OFFLINE_CONTENT_AVAILABILITY_STATUS,
PRICE_TRACKING_PRICE_WELCOME_MESSAGE_CARD,
PRICE_TRACKING_TRACK_PRICES_ON_TABS, PRICE_TRACKING_TRACK_PRICES_ON_TABS,
PROMO_IS_DISMISSED.pattern(), PROMO_IS_DISMISSED.pattern(),
PROMO_TIMES_SEEN.pattern(), PROMO_TIMES_SEEN.pattern(),
......
...@@ -90,6 +90,22 @@ public class ShoppingPersistedTabData extends PersistedTabData { ...@@ -90,6 +90,22 @@ public class ShoppingPersistedTabData extends PersistedTabData {
this.price = price; this.price = price;
this.previousPrice = previousPrice; this.previousPrice = previousPrice;
} }
@Override
public boolean equals(Object object) {
if (!(object instanceof PriceDrop)) return false;
PriceDrop priceDrop = (PriceDrop) object;
return this.price.equals(priceDrop.price)
&& this.previousPrice.equals(priceDrop.previousPrice);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (price == null ? 0 : price.hashCode());
result = 31 * result + (previousPrice == null ? 0 : previousPrice.hashCode());
return result;
}
} }
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment