Commit 9ab2faeb authored by Xi Han's avatar Xi Han Committed by Commit Bot

Chrome sends notifications with Channel to WebAPKs which targets SDK 26 on Android O.

In Chrome:
- WebAPK's channel is set under "Sites" notification group and
  "readable origin" as the channel, the same channel as Webapps.
- If the device is Android o+, Chrome will set a channel to the
  notification.

In WebAPK:
- WebApkServiceImpl#notifyNotification() is deprecated. Chrome calls
  notifyNotificationWithChannel() instead.
- If on Android O+, WebAPK will ensure the notification channel is
  created if its notifications have a channel id.

To test this change, we need to make sure the following cases all work:
- New WebAPK, new Chrome;
- New WebAPK, old Chrome;
- Old WebAPK, new Chrome.

Bug: 700228
Change-Id: I176a8d45f021a33ea441837476b1ca09e973a48b
Reviewed-on: https://chromium-review.googlesource.com/786300Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarPeter Kotwicz <pkotwicz@chromium.org>
Reviewed-by: default avatarAnita Woodruff <awdf@chromium.org>
Commit-Queue: Xi Han <hanxi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521841}
parent a9783e12
...@@ -79,8 +79,8 @@ public class CustomNotificationBuilder extends NotificationBuilderBase { ...@@ -79,8 +79,8 @@ public class CustomNotificationBuilder extends NotificationBuilderBase {
private final Context mContext; private final Context mContext;
public CustomNotificationBuilder(Context context, String channelId) { public CustomNotificationBuilder(Context context) {
super(context.getResources(), channelId); super(context.getResources());
mContext = context; mContext = context;
} }
......
...@@ -106,11 +106,11 @@ public abstract class NotificationBuilderBase { ...@@ -106,11 +106,11 @@ public abstract class NotificationBuilderBase {
private final int mLargeIconWidthPx; private final int mLargeIconWidthPx;
private final int mLargeIconHeightPx; private final int mLargeIconHeightPx;
private final RoundedIconGenerator mIconGenerator; private final RoundedIconGenerator mIconGenerator;
protected final String mChannelId;
protected CharSequence mTitle; protected CharSequence mTitle;
protected CharSequence mBody; protected CharSequence mBody;
protected CharSequence mOrigin; protected CharSequence mOrigin;
protected String mChannelId;
protected CharSequence mTickerText; protected CharSequence mTickerText;
protected Bitmap mImage; protected Bitmap mImage;
protected int mSmallIconId; protected int mSmallIconId;
...@@ -126,13 +126,12 @@ public abstract class NotificationBuilderBase { ...@@ -126,13 +126,12 @@ public abstract class NotificationBuilderBase {
protected int mPriority; protected int mPriority;
private Bitmap mLargeIcon; private Bitmap mLargeIcon;
public NotificationBuilderBase(Resources resources, String channelId) { public NotificationBuilderBase(Resources resources) {
mLargeIconWidthPx = mLargeIconWidthPx =
resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_width); resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
mLargeIconHeightPx = mLargeIconHeightPx =
resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_height); resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
mIconGenerator = createIconGenerator(resources); mIconGenerator = createIconGenerator(resources);
mChannelId = channelId;
} }
/** /**
...@@ -235,6 +234,14 @@ public abstract class NotificationBuilderBase { ...@@ -235,6 +234,14 @@ public abstract class NotificationBuilderBase {
return this; return this;
} }
/**
* Sets the channel id of the notification.
*/
public NotificationBuilderBase setChannelId(String channelId) {
mChannelId = channelId;
return this;
}
/** /**
* Adds an action to the notification, displayed as a button adjacent to the notification * Adds an action to the notification, displayed as a button adjacent to the notification
* content. * content.
......
...@@ -7,8 +7,10 @@ package org.chromium.chrome.browser.notifications; ...@@ -7,8 +7,10 @@ package org.chromium.chrome.browser.notifications;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.text.TextUtils;
import org.chromium.chrome.browser.notifications.channels.ChannelsInitializer; import org.chromium.chrome.browser.notifications.channels.ChannelsInitializer;
import org.chromium.chrome.browser.webapps.WebApkServiceClient;
/** /**
* Builder to be used on Android O until we target O and these APIs are in the support library. * Builder to be used on Android O until we target O and these APIs are in the support library.
...@@ -26,7 +28,12 @@ public class NotificationBuilderForO extends NotificationBuilder { ...@@ -26,7 +28,12 @@ public class NotificationBuilderForO extends NotificationBuilder {
// does not target O or sets its own channels. E.g. Web apk notifications. // does not target O or sets its own channels. E.g. Web apk notifications.
return; return;
} }
channelsInitializer.ensureInitialized(channelId);
// If the channel ID matches {@link WebApkServiceClient#CHANNEL_ID_WEBAPKS}, we don't create
// the channel in Chrome. Instead, the channel will be created in WebAPKs.
if (!TextUtils.equals(channelId, WebApkServiceClient.CHANNEL_ID_WEBAPKS)) {
channelsInitializer.ensureInitialized(channelId);
}
mBuilder.setChannelId(channelId); mBuilder.setChannelId(channelId);
} }
} }
...@@ -583,7 +583,7 @@ public class NotificationPlatformBridge { ...@@ -583,7 +583,7 @@ public class NotificationPlatformBridge {
boolean hasImage = image != null; boolean hasImage = image != null;
boolean forWebApk = !webApkPackage.isEmpty(); boolean forWebApk = !webApkPackage.isEmpty();
NotificationBuilderBase notificationBuilder = NotificationBuilderBase notificationBuilder =
createNotificationBuilder(context, forWebApk, hasImage, origin) createNotificationBuilder(context, hasImage)
.setTitle(title) .setTitle(title)
.setBody(body) .setBody(body)
.setImage(image) .setImage(image)
...@@ -598,6 +598,16 @@ public class NotificationPlatformBridge { ...@@ -598,6 +598,16 @@ public class NotificationPlatformBridge {
.setOrigin(UrlFormatter.formatUrlForSecurityDisplay( .setOrigin(UrlFormatter.formatUrlForSecurityDisplay(
origin, false /* showScheme */)); origin, false /* showScheme */));
if (shouldSetChannelId(forWebApk)) {
// TODO(crbug.com/700377): Channel ID should be retrieved from cache in native and
// passed through to here with other notification parameters.
String channelId =
ChromeFeatureList.isEnabled(ChromeFeatureList.SITE_NOTIFICATION_CHANNELS)
? SiteChannelsManager.getInstance().getChannelIdForOrigin(origin)
: ChannelDefinitions.CHANNEL_ID_SITES;
notificationBuilder.setChannelId(channelId);
}
for (int actionIndex = 0; actionIndex < actions.length; actionIndex++) { for (int actionIndex = 0; actionIndex < actions.length; actionIndex++) {
PendingIntent intent = makePendingIntent(context, PendingIntent intent = makePendingIntent(context,
NotificationConstants.ACTION_CLICK_NOTIFICATION, notificationId, origin, NotificationConstants.ACTION_CLICK_NOTIFICATION, notificationId, origin,
...@@ -661,22 +671,16 @@ public class NotificationPlatformBridge { ...@@ -661,22 +671,16 @@ public class NotificationPlatformBridge {
} }
} }
private NotificationBuilderBase createNotificationBuilder( private NotificationBuilderBase createNotificationBuilder(Context context, boolean hasImage) {
Context context, boolean forWebApk, boolean hasImage, String origin) {
// Don't set a channelId for web apk notifications because the channel won't be
// initialized for the web apk and it will crash on notify - see crbug.com/727178.
// (It's okay to not set a channel on them because web apks don't target O yet.)
// TODO(crbug.com/700377): Channel ID should be retrieved from cache in native and passed
// through to here with other notification parameters.
String channelId = (forWebApk || Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
? null
: ChromeFeatureList.isEnabled(ChromeFeatureList.SITE_NOTIFICATION_CHANNELS)
? SiteChannelsManager.getInstance().getChannelIdForOrigin(origin)
: ChannelDefinitions.CHANNEL_ID_SITES;
if (useCustomLayouts(hasImage)) { if (useCustomLayouts(hasImage)) {
return new CustomNotificationBuilder(context, channelId); return new CustomNotificationBuilder(context);
} }
return new StandardNotificationBuilder(context, channelId); return new StandardNotificationBuilder(context);
}
/** Returns whether to set a channel id when building a notification. */
private boolean shouldSetChannelId(boolean forWebApk) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !forWebApk;
} }
/** /**
......
...@@ -14,8 +14,8 @@ import android.os.Build; ...@@ -14,8 +14,8 @@ import android.os.Build;
public class StandardNotificationBuilder extends NotificationBuilderBase { public class StandardNotificationBuilder extends NotificationBuilderBase {
private final Context mContext; private final Context mContext;
public StandardNotificationBuilder(Context context, String channelId) { public StandardNotificationBuilder(Context context) {
super(context.getResources(), channelId); super(context.getResources());
mContext = context; mContext = context;
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.webapps; package org.chromium.chrome.browser.webapps;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
...@@ -45,6 +46,12 @@ public class WebApkServiceClient { ...@@ -45,6 +46,12 @@ public class WebApkServiceClient {
} }
} }
/**
* Keeps the value consistent with {@link
* org.chromium.webapk.shell_apk.WebApkServiceImplWrapper#DEFAULT_NOTIFICATION_CHANNEL_ID}.
*/
public static final String CHANNEL_ID_WEBAPKS = "default_channel_id";
private static final String CATEGORY_WEBAPK_API = "android.intent.category.WEBAPK_API"; private static final String CATEGORY_WEBAPK_API = "android.intent.category.WEBAPK_API";
private static final String TAG = "cr_WebApk"; private static final String TAG = "cr_WebApk";
...@@ -93,7 +100,14 @@ public class WebApkServiceClient { ...@@ -93,7 +100,14 @@ public class WebApkServiceClient {
} }
boolean notificationPermissionEnabled = api.notificationPermissionEnabled(); boolean notificationPermissionEnabled = api.notificationPermissionEnabled();
if (notificationPermissionEnabled) { if (notificationPermissionEnabled) {
api.notifyNotification(platformTag, platformID, notificationBuilder.build()); String channelName = null;
if (webApkTargetsAtLeastO(webApkPackage)) {
notificationBuilder.setChannelId(CHANNEL_ID_WEBAPKS);
channelName = ContextUtils.getApplicationContext().getString(
org.chromium.chrome.R.string.webapk_notification_channel_name);
}
api.notifyNotificationWithChannel(
platformTag, platformID, notificationBuilder.build(), channelName);
} }
WebApkUma.recordNotificationPermissionStatus(notificationPermissionEnabled); WebApkUma.recordNotificationPermissionStatus(notificationPermissionEnabled);
} }
...@@ -134,4 +148,17 @@ public class WebApkServiceClient { ...@@ -134,4 +148,17 @@ public class WebApkServiceClient {
return null; return null;
} }
} }
/** Returns whether the WebAPK targets SDK 26+. */
private boolean webApkTargetsAtLeastO(String webApkPackage) {
try {
ApplicationInfo info =
ContextUtils.getApplicationContext().getPackageManager().getApplicationInfo(
webApkPackage, 0);
return info.targetSdkVersion >= Build.VERSION_CODES.O;
} catch (PackageManager.NameNotFoundException e) {
}
return false;
}
} }
...@@ -3025,6 +3025,9 @@ You must have Bluetooth and Location turned on in order to use the Physical Web. ...@@ -3025,6 +3025,9 @@ You must have Bluetooth and Location turned on in order to use the Physical Web.
<message name="IDS_WEBAPK_OFFLINE_DIALOG" desc="The message on the dialog shown when launching a WebAPK needs network connection."> <message name="IDS_WEBAPK_OFFLINE_DIALOG" desc="The message on the dialog shown when launching a WebAPK needs network connection.">
To use <ph name="APP_NAME">%1$s<ex>PWA List</ex></ph> for the first time, please connect to the internet. To use <ph name="APP_NAME">%1$s<ex>PWA List</ex></ph> for the first time, please connect to the internet.
</message> </message>
<message name="IDS_WEBAPK_NOTIFICATION_CHANNEL_NAME" desc="The visible name of the notification channel of WebAPKs on Android O+.">
General
</message>
<!-- Keyboard shortcuts in Android N--> <!-- Keyboard shortcuts in Android N-->
<message name="IDS_KEYBOARD_SHORTCUT_OPEN_NEW_TAB" desc="A text label that appears next to the keyboard shortcut to open a new tab in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]"> <message name="IDS_KEYBOARD_SHORTCUT_OPEN_NEW_TAB" desc="A text label that appears next to the keyboard shortcut to open a new tab in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
......
...@@ -77,25 +77,25 @@ public class CustomNotificationBuilderTest { ...@@ -77,25 +77,25 @@ public class CustomNotificationBuilderTest {
new int[] {Color.WHITE}, 1 /* width */, 1 /* height */, Bitmap.Config.ARGB_8888); new int[] {Color.WHITE}, 1 /* width */, 1 /* height */, Bitmap.Config.ARGB_8888);
actionIcon = actionIcon.copy(Bitmap.Config.ARGB_8888, true /* isMutable */); actionIcon = actionIcon.copy(Bitmap.Config.ARGB_8888, true /* isMutable */);
Notification notification = Notification notification = new CustomNotificationBuilder(context)
new CustomNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES) .setSmallIcon(R.drawable.ic_chrome)
.setSmallIcon(R.drawable.ic_chrome) .setLargeIcon(largeIcon)
.setLargeIcon(largeIcon) .setTitle("title")
.setTitle("title") .setBody("body")
.setBody("body") .setOrigin("origin")
.setOrigin("origin") .setChannelId(ChannelDefinitions.CHANNEL_ID_SITES)
.setTicker("ticker") .setTicker("ticker")
.setDefaults(Notification.DEFAULT_ALL) .setDefaults(Notification.DEFAULT_ALL)
.setVibrate(new long[] {100L}) .setVibrate(new long[] {100L})
.setContentIntent(contentIntent) .setContentIntent(contentIntent)
.setDeleteIntent(deleteIntent) .setDeleteIntent(deleteIntent)
.addButtonAction( .addButtonAction(actionIcon, "button",
actionIcon, "button", createIntent(context, "ActionButtonOne")) createIntent(context, "ActionButtonOne"))
.addButtonAction( .addButtonAction(actionIcon, "button",
actionIcon, "button", createIntent(context, "ActionButtonTwo")) createIntent(context, "ActionButtonTwo"))
.addSettingsAction( .addSettingsAction(0 /* iconId */, "settings",
0 /* iconId */, "settings", createIntent(context, "SettingsButton")) createIntent(context, "SettingsButton"))
.build(); .build();
assertSmallNotificationIconAsExpected(context, notification, smallIcon); assertSmallNotificationIconAsExpected(context, notification, smallIcon);
assertLargeNotificationIconAsExpected(context, notification, largeIcon); assertLargeNotificationIconAsExpected(context, notification, largeIcon);
...@@ -149,8 +149,9 @@ public class CustomNotificationBuilderTest { ...@@ -149,8 +149,9 @@ public class CustomNotificationBuilderTest {
@DisableIf.Build(sdk_is_greater_than = 23, message = "crbug.com/779228") @DisableIf.Build(sdk_is_greater_than = 23, message = "crbug.com/779228")
public void testZeroActionButtons() { public void testZeroActionButtons() {
Context context = InstrumentationRegistry.getTargetContext(); Context context = InstrumentationRegistry.getTargetContext();
Notification notification = Notification notification = new CustomNotificationBuilder(context)
new CustomNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES).build(); .setChannelId(ChannelDefinitions.CHANNEL_ID_SITES)
.build();
View bigView = notification.bigContentView.apply(context, new LinearLayout(context)); View bigView = notification.bigContentView.apply(context, new LinearLayout(context));
ArrayList<View> buttons = new ArrayList<>(); ArrayList<View> buttons = new ArrayList<>();
bigView.findViewsWithText(buttons, "button", View.FIND_VIEWS_WITH_TEXT); bigView.findViewsWithText(buttons, "button", View.FIND_VIEWS_WITH_TEXT);
...@@ -167,12 +168,12 @@ public class CustomNotificationBuilderTest { ...@@ -167,12 +168,12 @@ public class CustomNotificationBuilderTest {
@DisableIf.Build(sdk_is_greater_than = 23, message = "crbug.com/779228") @DisableIf.Build(sdk_is_greater_than = 23, message = "crbug.com/779228")
public void testMaxActionButtons() { public void testMaxActionButtons() {
Context context = InstrumentationRegistry.getTargetContext(); Context context = InstrumentationRegistry.getTargetContext();
NotificationBuilderBase builder = NotificationBuilderBase builder = new CustomNotificationBuilder(context)
new CustomNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES) .setChannelId(ChannelDefinitions.CHANNEL_ID_SITES)
.addButtonAction(null /* iconBitmap */, "button", .addButtonAction(null /* iconBitmap */, "button",
createIntent(context, "ActionButtonOne")) createIntent(context, "ActionButtonOne"))
.addButtonAction(null /* iconBitmap */, "button", .addButtonAction(null /* iconBitmap */, "button",
createIntent(context, "ActionButtonTwo")); createIntent(context, "ActionButtonTwo"));
try { try {
builder.addButtonAction( builder.addButtonAction(
null /* iconBitmap */, "button", createIntent(context, "ActionButtonThree")); null /* iconBitmap */, "button", createIntent(context, "ActionButtonThree"));
...@@ -208,13 +209,13 @@ public class CustomNotificationBuilderTest { ...@@ -208,13 +209,13 @@ public class CustomNotificationBuilderTest {
new int[] {Color.RED}, 1 /* width */, 1 /* height */, Bitmap.Config.ARGB_8888); new int[] {Color.RED}, 1 /* width */, 1 /* height */, Bitmap.Config.ARGB_8888);
actionIcon = actionIcon.copy(Bitmap.Config.ARGB_8888, true /* isMutable */); actionIcon = actionIcon.copy(Bitmap.Config.ARGB_8888, true /* isMutable */);
Notification notification = Notification notification = new CustomNotificationBuilder(context)
new CustomNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES) .setChannelId(ChannelDefinitions.CHANNEL_ID_SITES)
.setLargeIcon(largeIcon) .setLargeIcon(largeIcon)
.setSmallIcon(smallIcon) .setSmallIcon(smallIcon)
.addButtonAction( .addButtonAction(actionIcon, "button",
actionIcon, "button", createIntent(context, "ActionButton")) createIntent(context, "ActionButton"))
.build(); .build();
// The large icon should be unchanged. // The large icon should be unchanged.
assertLargeNotificationIconAsExpected(context, notification, largeIcon); assertLargeNotificationIconAsExpected(context, notification, largeIcon);
...@@ -240,10 +241,11 @@ public class CustomNotificationBuilderTest { ...@@ -240,10 +241,11 @@ public class CustomNotificationBuilderTest {
Context context = InstrumentationRegistry.getTargetContext(); Context context = InstrumentationRegistry.getTargetContext();
int maxLength = CustomNotificationBuilder.MAX_CHARSEQUENCE_LENGTH; int maxLength = CustomNotificationBuilder.MAX_CHARSEQUENCE_LENGTH;
Notification notification = Notification notification =
new CustomNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES) new CustomNotificationBuilder(context)
.setTitle(createString('a', maxLength + 1)) .setTitle(createString('a', maxLength + 1))
.setBody(createString('b', maxLength + 1)) .setBody(createString('b', maxLength + 1))
.setOrigin(createString('c', maxLength + 1)) .setOrigin(createString('c', maxLength + 1))
.setChannelId(ChannelDefinitions.CHANNEL_ID_SITES)
.setTicker(createString('d', maxLength + 1)) .setTicker(createString('d', maxLength + 1))
.addButtonAction(null /* iconBitmap */, createString('e', maxLength + 1), .addButtonAction(null /* iconBitmap */, createString('e', maxLength + 1),
createIntent(context, "ActionButtonOne")) createIntent(context, "ActionButtonOne"))
...@@ -299,8 +301,9 @@ public class CustomNotificationBuilderTest { ...@@ -299,8 +301,9 @@ public class CustomNotificationBuilderTest {
public void testGeneratesLargeIconFromOriginWhenNoLargeIconProvided() { public void testGeneratesLargeIconFromOriginWhenNoLargeIconProvided() {
Context context = InstrumentationRegistry.getTargetContext(); Context context = InstrumentationRegistry.getTargetContext();
NotificationBuilderBase notificationBuilder = NotificationBuilderBase notificationBuilder =
new CustomNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES) new CustomNotificationBuilder(context)
.setOrigin("https://www.google.com"); .setOrigin("https://www.google.com")
.setChannelId(ChannelDefinitions.CHANNEL_ID_SITES);
Notification notification = notificationBuilder.build(); Notification notification = notificationBuilder.build();
...@@ -317,8 +320,9 @@ public class CustomNotificationBuilderTest { ...@@ -317,8 +320,9 @@ public class CustomNotificationBuilderTest {
public void testGeneratesLargeIconFromOriginWhenLargeIconProvidedIsNull() { public void testGeneratesLargeIconFromOriginWhenLargeIconProvidedIsNull() {
Context context = InstrumentationRegistry.getTargetContext(); Context context = InstrumentationRegistry.getTargetContext();
NotificationBuilderBase notificationBuilder = NotificationBuilderBase notificationBuilder =
new CustomNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES) new CustomNotificationBuilder(context)
.setOrigin("https://www.chromium.org") .setOrigin("https://www.chromium.org")
.setChannelId(ChannelDefinitions.CHANNEL_ID_SITES)
.setLargeIcon(null); .setLargeIcon(null);
Notification notification = notificationBuilder.build(); Notification notification = notificationBuilder.build();
...@@ -344,7 +348,8 @@ public class CustomNotificationBuilderTest { ...@@ -344,7 +348,8 @@ public class CustomNotificationBuilderTest {
public void testAddTextActionSetsRemoteInput() { public void testAddTextActionSetsRemoteInput() {
Context context = InstrumentationRegistry.getTargetContext(); Context context = InstrumentationRegistry.getTargetContext();
NotificationBuilderBase notificationBuilder = NotificationBuilderBase notificationBuilder =
new CustomNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES) new CustomNotificationBuilder(context)
.setChannelId(ChannelDefinitions.CHANNEL_ID_SITES)
.addTextAction(null, "Action Title", null, "Placeholder"); .addTextAction(null, "Action Title", null, "Placeholder");
Notification notification = notificationBuilder.build(); Notification notification = notificationBuilder.build();
......
...@@ -66,13 +66,13 @@ public class NotificationBuilderBaseTest { ...@@ -66,13 +66,13 @@ public class NotificationBuilderBaseTest {
String origin = "https://example.com"; String origin = "https://example.com";
NotificationBuilderBase notificationBuilder = new NotificationBuilderBase(resources, NotificationBuilderBase notificationBuilder = new NotificationBuilderBase(resources) {
ChannelDefinitions.CHANNEL_ID_BROWSER) {
@Override @Override
public Notification build() { public Notification build() {
return null; return null;
} }
}; };
notificationBuilder.setChannelId(ChannelDefinitions.CHANNEL_ID_BROWSER);
Bitmap fromNullIcon = notificationBuilder.ensureNormalizedIcon(null, origin); Bitmap fromNullIcon = notificationBuilder.ensureNormalizedIcon(null, origin);
Assert.assertNotNull(fromNullIcon); Assert.assertNotNull(fromNullIcon);
Assert.assertEquals(largeIconWidthPx, fromNullIcon.getWidth()); Assert.assertEquals(largeIconWidthPx, fromNullIcon.getWidth());
......
...@@ -79,10 +79,11 @@ public class StandardNotificationBuilderTest { ...@@ -79,10 +79,11 @@ public class StandardNotificationBuilderTest {
new int[] {Color.GRAY}, 1 /* width */, 1 /* height */, Bitmap.Config.ARGB_8888); new int[] {Color.GRAY}, 1 /* width */, 1 /* height */, Bitmap.Config.ARGB_8888);
actionIcon = actionIcon.copy(Bitmap.Config.ARGB_8888, true /* isMutable */); actionIcon = actionIcon.copy(Bitmap.Config.ARGB_8888, true /* isMutable */);
return new StandardNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES) return new StandardNotificationBuilder(context)
.setTitle("title") .setTitle("title")
.setBody("body") .setBody("body")
.setOrigin("origin") .setOrigin("origin")
.setChannelId(ChannelDefinitions.CHANNEL_ID_SITES)
.setTicker(new SpannableStringBuilder("ticker")) .setTicker(new SpannableStringBuilder("ticker"))
.setImage(image) .setImage(image)
.setLargeIcon(largeIcon) .setLargeIcon(largeIcon)
...@@ -176,14 +177,14 @@ public class StandardNotificationBuilderTest { ...@@ -176,14 +177,14 @@ public class StandardNotificationBuilderTest {
@Feature({"Browser", "Notifications"}) @Feature({"Browser", "Notifications"})
public void testSetSmallIcon() { public void testSetSmallIcon() {
Context context = InstrumentationRegistry.getTargetContext(); Context context = InstrumentationRegistry.getTargetContext();
NotificationBuilderBase notificationBuilder = NotificationBuilderBase notificationBuilder = new StandardNotificationBuilder(context);
new StandardNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES);
Bitmap bitmap = Bitmap bitmap =
BitmapFactory.decodeResource(context.getResources(), R.drawable.chrome_sync_logo); BitmapFactory.decodeResource(context.getResources(), R.drawable.chrome_sync_logo);
notificationBuilder.setSmallIcon(R.drawable.ic_chrome); notificationBuilder.setSmallIcon(R.drawable.ic_chrome);
notificationBuilder.setSmallIcon(bitmap); // Should override on M+ notificationBuilder.setSmallIcon(bitmap); // Should override on M+
notificationBuilder.setChannelId(ChannelDefinitions.CHANNEL_ID_SITES);
Notification notification = notificationBuilder.build(); Notification notification = notificationBuilder.build();
...@@ -198,9 +199,8 @@ public class StandardNotificationBuilderTest { ...@@ -198,9 +199,8 @@ public class StandardNotificationBuilderTest {
Assert.assertTrue(expected.sameAs(result)); Assert.assertTrue(expected.sameAs(result));
// Check using the same bitmap on another builder gives the same result. // Check using the same bitmap on another builder gives the same result.
NotificationBuilderBase otherBuilder = NotificationBuilderBase otherBuilder = new StandardNotificationBuilder(context);
new StandardNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES); otherBuilder.setSmallIcon(bitmap).setChannelId(ChannelDefinitions.CHANNEL_ID_SITES);
otherBuilder.setSmallIcon(bitmap);
Notification otherNotification = otherBuilder.build(); Notification otherNotification = otherBuilder.build();
Assert.assertTrue(expected.sameAs( Assert.assertTrue(expected.sameAs(
NotificationTestUtil.getSmallIconFromNotification(context, otherNotification))); NotificationTestUtil.getSmallIconFromNotification(context, otherNotification)));
...@@ -219,7 +219,8 @@ public class StandardNotificationBuilderTest { ...@@ -219,7 +219,8 @@ public class StandardNotificationBuilderTest {
public void testAddTextActionSetsRemoteInput() { public void testAddTextActionSetsRemoteInput() {
Context context = InstrumentationRegistry.getTargetContext(); Context context = InstrumentationRegistry.getTargetContext();
NotificationBuilderBase notificationBuilder = NotificationBuilderBase notificationBuilder =
new StandardNotificationBuilder(context, ChannelDefinitions.CHANNEL_ID_SITES) new StandardNotificationBuilder(context)
.setChannelId(ChannelDefinitions.CHANNEL_ID_SITES)
.addTextAction(null, "Action Title", null, "Placeholder"); .addTextAction(null, "Action Title", null, "Placeholder");
Notification notification = notificationBuilder.build(); Notification notification = notificationBuilder.build();
......
...@@ -14,6 +14,7 @@ interface IWebApkApi { ...@@ -14,6 +14,7 @@ interface IWebApkApi {
int getSmallIconId(); int getSmallIconId();
// Display a notification. // Display a notification.
// DEPRECATED: Use notifyNotificationWithChannel.
void notifyNotification(String platformTag, int platformID, in Notification notification); void notifyNotification(String platformTag, int platformID, in Notification notification);
// Cancel a notification. // Cancel a notification.
...@@ -21,4 +22,8 @@ interface IWebApkApi { ...@@ -21,4 +22,8 @@ interface IWebApkApi {
// Get if notification permission is enabled. // Get if notification permission is enabled.
boolean notificationPermissionEnabled(); boolean notificationPermissionEnabled();
// Display a notification with a specified channel name.
void notifyNotificationWithChannel(String platformTag, int platformID,
in Notification notification, String channelName);
} }
...@@ -4,14 +4,18 @@ ...@@ -4,14 +4,18 @@
package org.chromium.webapk.lib.runtime_library; package org.chromium.webapk.lib.runtime_library;
import android.annotation.TargetApi;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.os.Binder; import android.os.Binder;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v4.app.NotificationManagerCompat; import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;
/** /**
* Implements services offered by the WebAPK to Chrome. * Implements services offered by the WebAPK to Chrome.
...@@ -21,7 +25,7 @@ public class WebApkServiceImpl extends IWebApkApi.Stub { ...@@ -21,7 +25,7 @@ public class WebApkServiceImpl extends IWebApkApi.Stub {
public static final String KEY_SMALL_ICON_ID = "small_icon_id"; public static final String KEY_SMALL_ICON_ID = "small_icon_id";
public static final String KEY_HOST_BROWSER_UID = "host_browser_uid"; public static final String KEY_HOST_BROWSER_UID = "host_browser_uid";
private static final String TAG = "WebApkServiceImpl"; private static final String TAG = "cr_WebApkServiceImpl";
private final Context mContext; private final Context mContext;
...@@ -65,7 +69,9 @@ public class WebApkServiceImpl extends IWebApkApi.Stub { ...@@ -65,7 +69,9 @@ public class WebApkServiceImpl extends IWebApkApi.Stub {
@Override @Override
public void notifyNotification(String platformTag, int platformID, Notification notification) { public void notifyNotification(String platformTag, int platformID, Notification notification) {
getNotificationManager().notify(platformTag, platformID, notification); Log.w(TAG,
"Should NOT reach WebApkServiceImpl#notifyNotification(String, int,"
+ " Notification).");
} }
@Override @Override
...@@ -78,7 +84,20 @@ public class WebApkServiceImpl extends IWebApkApi.Stub { ...@@ -78,7 +84,20 @@ public class WebApkServiceImpl extends IWebApkApi.Stub {
return NotificationManagerCompat.from(mContext).areNotificationsEnabled(); return NotificationManagerCompat.from(mContext).areNotificationsEnabled();
} }
@TargetApi(Build.VERSION_CODES.O)
@Override
public void notifyNotificationWithChannel(
String platformTag, int platformID, Notification notification, String channelName) {
NotificationManager notificationManager = getNotificationManager();
if (notification.getChannelId() != null) {
NotificationChannel channel = new NotificationChannel(notification.getChannelId(),
channelName, NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(platformTag, platformID, notification);
}
private NotificationManager getNotificationManager() { private NotificationManager getNotificationManager() {
return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
} }
} }
\ No newline at end of file
...@@ -5,4 +5,4 @@ ...@@ -5,4 +5,4 @@
# Must be incremented whenever the runtime library is updated. The WebAPK # Must be incremented whenever the runtime library is updated. The WebAPK
# re-extracts the runtime library from the Chrome APK when # re-extracts the runtime library from the Chrome APK when
# |runtime_library_version| is incremented. # |runtime_library_version| is incremented.
runtime_library_version = 3 runtime_library_version = 4
...@@ -93,6 +93,14 @@ public class WebApkServiceImplWrapper extends IWebApkApi.Stub { ...@@ -93,6 +93,14 @@ public class WebApkServiceImplWrapper extends IWebApkApi.Stub {
Log.w(TAG, "Should NOT reach WebApkServiceImplWrapper#cancelNotification(String, int)."); Log.w(TAG, "Should NOT reach WebApkServiceImplWrapper#cancelNotification(String, int).");
} }
@Override
public void notifyNotificationWithChannel(
String platformTag, int platformID, Notification notification, String channelName) {
Log.w(TAG,
"Should NOT reach WebApkServiceImplWrapper#notifyNotificationWithChannel("
+ "String, int, Notification, String)");
}
/** Creates a WebAPK notification channel on Android O+ if one does not exist. */ /** Creates a WebAPK notification channel on Android O+ if one does not exist. */
protected void ensureNotificationChannelExists() { protected void ensureNotificationChannelExists() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
......
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