blob: ac5b8fda62577befc1fc10173d4662749cdeba8d [file] [log] [blame]
// Copyright 2021 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.price_tracking;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.Callback;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
import org.chromium.chrome.browser.notifications.NotificationUmaTracker.ActionType;
import org.chromium.chrome.browser.notifications.NotificationUmaTracker.SystemNotificationType;
import org.chromium.chrome.browser.notifications.NotificationWrapperBuilderFactory;
import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions.ChannelId;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.components.browser_ui.notifications.NotificationManagerProxy;
import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
import org.chromium.components.browser_ui.notifications.NotificationMetadata;
import org.chromium.components.browser_ui.notifications.NotificationWrapper;
import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder;
import org.chromium.components.browser_ui.notifications.PendingIntentProvider;
import org.chromium.components.image_fetcher.ImageFetcher;
import org.chromium.components.image_fetcher.ImageFetcherConfig;
import org.chromium.components.image_fetcher.ImageFetcherFactory;
import java.util.List;
/**
* Used to display price drop notification with {@link NotificationWrapperBuilder} and {@link
* ImageFetcher} without cache.
*/
public class PriceDropNotifier {
private static final String NOTIFICATION_TAG = "price_drop";
private static final int NOTIFICATION_ID = 0;
static class NotificationData {
public NotificationData(CharSequence title, CharSequence text, String iconUrl,
String destinationUrl, String offerId, String productClusterId,
List<ActionData> actions) {
this.title = title;
this.text = text;
this.iconUrl = iconUrl;
this.destinationUrl = destinationUrl;
this.offerId = offerId;
this.productClusterId = productClusterId;
this.actions = actions;
}
/**
* Title of the notification. The displayable product name or title.
*/
public final CharSequence title;
/**
* Content text of the notification. Could be product description.
*/
public final CharSequence text;
/**
* The URL of a representative image for the product, hosted on Google server. Used as both
* large icon and the image in the expanded view.
*/
public final String iconUrl;
/**
* The URL that leads to the shopping item.
*/
public final String destinationUrl;
/**
* Associated offer ID.
*/
public final String offerId;
/**
* Associated cluster ID.
*/
public final String productClusterId;
/**
* A list of button actions.
*/
public final List<ActionData> actions;
}
static class ActionData {
ActionData(String actionId, String text) {
this.actionId = actionId;
this.text = text;
}
public final String actionId;
public final CharSequence text;
}
private final Context mContext;
private ImageFetcher mImageFetcher;
private final NotificationBuilderFactory mNotificationBuilderFactory;
private final NotificationManagerProxy mNotificationManagerProxy;
private final PriceDropNotificationManager mPriceDropNotificationManager;
/**
* Creates a {@link PriceDropNotifier} instance.
* @param context The Android context.
*/
public static PriceDropNotifier create(Context context) {
NotificationBuilderFactory notificationBuilderFactory = ()
-> NotificationWrapperBuilderFactory.createNotificationWrapperBuilder(
ChannelId.PRICE_DROP,
new NotificationMetadata(SystemNotificationType.PRICE_DROP_ALERTS,
NOTIFICATION_TAG, NOTIFICATION_ID));
return new PriceDropNotifier(
context, notificationBuilderFactory, new NotificationManagerProxyImpl(context));
}
/**
* Factory interface to create {@link NotificationWrapperBuilder}.
*/
interface NotificationBuilderFactory {
NotificationWrapperBuilder createNotificationBuilder();
}
@VisibleForTesting
PriceDropNotifier(Context context, NotificationBuilderFactory notificationBuilderFactory,
NotificationManagerProxy notificationManager) {
mContext = context;
mNotificationBuilderFactory = notificationBuilderFactory;
mNotificationManagerProxy = notificationManager;
mPriceDropNotificationManager =
new PriceDropNotificationManager(mContext, mNotificationManagerProxy);
}
/**
* Shows a price drop notification.
* @param notificationData Information about the notification contents.
*/
public void showNotification(final NotificationData notificationData) {
maybeFetchIcon(notificationData, bitmap -> { showWithIcon(notificationData, bitmap); });
}
@VisibleForTesting
protected ImageFetcher getImageFetcher() {
if (mImageFetcher == null) {
mImageFetcher = ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.NETWORK_ONLY,
Profile.getLastUsedRegularProfile().getProfileKey());
}
return mImageFetcher;
}
private void maybeFetchIcon(
final NotificationData notificationData, Callback<Bitmap> callback) {
if (notificationData.iconUrl == null) {
callback.onResult(null);
return;
}
ImageFetcher.Params params = ImageFetcher.Params.create(
notificationData.iconUrl, ImageFetcher.PRICE_DROP_NOTIFICATION);
getImageFetcher().fetchImage(params, bitmap -> { callback.onResult(bitmap); });
}
private void showWithIcon(NotificationData notificationData, @Nullable Bitmap icon) {
NotificationWrapperBuilder notificationBuilder =
mNotificationBuilderFactory.createNotificationBuilder();
if (icon != null) {
// Both the large icon and the expanded view use the bitmap fetched from icon URL.
notificationBuilder.setLargeIcon(icon);
notificationBuilder.setBigPictureStyle(icon, notificationData.text);
}
notificationBuilder.setContentTitle(notificationData.title);
notificationBuilder.setContentText(notificationData.text);
notificationBuilder.setContentIntent(createContentIntent(notificationData.destinationUrl));
notificationBuilder.setSmallIcon(R.drawable.ic_chrome);
notificationBuilder.setTimeoutAfter(
PriceTrackingNotificationConfig.getNotificationTimeoutMs());
if (notificationData.actions != null) {
for (ActionData action : notificationData.actions) {
PendingIntentProvider actionClickIntentProvider =
createClickIntent(action.actionId, notificationData.destinationUrl,
notificationData.offerId, notificationData.productClusterId);
notificationBuilder.addAction(0, action.text, actionClickIntentProvider,
actionIdToUmaActionType(action.actionId));
}
}
NotificationWrapper notificationWrapper = notificationBuilder.buildNotificationWrapper();
mNotificationManagerProxy.notify(notificationWrapper);
mPriceDropNotificationManager.onNotificationPosted(notificationWrapper.getNotification());
}
private static @NotificationUmaTracker.ActionType int actionIdToUmaActionType(String actionId) {
if (PriceDropNotificationManager.ACTION_ID_VISIT_SITE.equals(actionId)) {
return ActionType.PRICE_DROP_VISIT_SITE;
}
if (PriceDropNotificationManager.ACTION_ID_TURN_OFF_ALERT.equals(actionId)) {
return ActionType.PRICE_DROP_TURN_OFF_ALERT;
}
return ActionType.UNKNOWN;
}
private PendingIntentProvider createContentIntent(String destinationUrl) {
Intent intent = mPriceDropNotificationManager.getNotificationClickIntent(destinationUrl);
return PendingIntentProvider.getActivity(
mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
private PendingIntentProvider createClickIntent(
String actionId, String url, String offerId, String clusterId) {
Intent intent = mPriceDropNotificationManager.getNotificationActionClickIntent(
actionId, url, offerId, clusterId);
return PendingIntentProvider.getActivity(
mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}