diff --git a/androidbrowserhelper/src/main/java/com/google/androidbrowserhelper/trusted/NotificationUtils.java b/androidbrowserhelper/src/main/java/com/google/androidbrowserhelper/trusted/NotificationUtils.java index abf3e525..cce47484 100644 --- a/androidbrowserhelper/src/main/java/com/google/androidbrowserhelper/trusted/NotificationUtils.java +++ b/androidbrowserhelper/src/main/java/com/google/androidbrowserhelper/trusted/NotificationUtils.java @@ -16,8 +16,13 @@ import android.app.NotificationChannel; import android.app.NotificationManager; +import android.app.Service; +import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; import android.os.Build; +import android.os.Bundle; import androidx.core.app.NotificationManagerCompat; import java.util.Locale; @@ -25,6 +30,12 @@ * Helper for interacting with the notification manager and channels. */ public class NotificationUtils { + /** + * Metadata key to specify whether notification channels should be created with high importance. + */ + public static final String METADATA_USE_HIGH_PRI_NOTIFICATIONS_ANDROIDX = + "androidx.browser.trusted.USE_HIGH_PRI_NOTIFICATIONS"; + private NotificationUtils() {} /** @@ -41,13 +52,74 @@ public static boolean areNotificationsEnabled(Context context, String channelNam } /** - * Creates a notification channel using the given channel name. + * Checks if high-priority notifications are configured in the manifest metadata. + */ + public static boolean shouldUseHighPriorityNotifications(Context context) { + if (!(context instanceof Service)) return false; + try { + Service service = (Service) context; + ServiceInfo serviceInfo = service.getPackageManager().getServiceInfo( + new ComponentName(service, service.getClass()), PackageManager.GET_META_DATA); + if (serviceInfo != null && serviceInfo.metaData != null + && isHighPriorityInBundle(serviceInfo.metaData)) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + // Service not found; fallback to default. + } + return false; + } + + /** + * Checks if the given metadata bundle contains the high-priority notification configuration + * and evaluates to true. + */ + private static boolean isHighPriorityInBundle(Bundle metaData) { + if (metaData.containsKey(METADATA_USE_HIGH_PRI_NOTIFICATIONS_ANDROIDX)) { + Object val = metaData.get(METADATA_USE_HIGH_PRI_NOTIFICATIONS_ANDROIDX); + if (parseBooleanValue(val)) return true; + } + return false; + } + + /** + * Parses an object value from metadata into a boolean, handling both Boolean and String representations. + */ + private static boolean parseBooleanValue(Object value) { + if (value instanceof Boolean) { + return (Boolean) value; + } + if (value instanceof String) { + return Boolean.parseBoolean((String) value); + } + return false; + } + + /** + * Returns the notification importance level resolved from the application manifest metadata. + */ + public static int getNotificationImportance(Context context) { + return shouldUseHighPriorityNotifications(context) + ? NotificationManager.IMPORTANCE_HIGH + : NotificationManager.IMPORTANCE_DEFAULT; + } + + /** + * Creates a notification channel using the given channel name and the importance level resolved + * from manifest metadata. */ public static void createNotificationChannel(Context context, String channelName) { + createNotificationChannel(context, channelName, getNotificationImportance(context)); + } + + /** + * Creates a notification channel using the given channel name and explicit importance level. + */ + public static void createNotificationChannel(Context context, String channelName, int importance) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return; NotificationChannel channel = new NotificationChannel(channelNameToId(channelName), - channelName, NotificationManager.IMPORTANCE_DEFAULT); + channelName, importance); NotificationManagerCompat.from(context).createNotificationChannel(channel); } @@ -55,7 +127,7 @@ public static void createNotificationChannel(Context context, String channelName * Generates a notification channel id from a channel name. * TODO: Remove this when we can use the method defined in AndroidX instead. */ - private static String channelNameToId(String name) { + public static String channelNameToId(String name) { return name.toLowerCase(Locale.ROOT).replace(' ', '_') + "_channel_id"; } } diff --git a/androidbrowserhelper/src/test/java/com/google/androidbrowserhelper/trusted/NotificationUtilsTest.java b/androidbrowserhelper/src/test/java/com/google/androidbrowserhelper/trusted/NotificationUtilsTest.java new file mode 100644 index 00000000..d99ddc32 --- /dev/null +++ b/androidbrowserhelper/src/test/java/com/google/androidbrowserhelper/trusted/NotificationUtilsTest.java @@ -0,0 +1,260 @@ +// Copyright 2026 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidbrowserhelper.trusted; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.robolectric.Shadows.shadowOf; + +import android.app.Activity; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; + +import androidx.annotation.Nullable; +import androidx.core.app.NotificationManagerCompat; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.ParameterizedRobolectricTestRunner; +import org.robolectric.ParameterizedRobolectricTestRunner.Parameter; +import org.robolectric.ParameterizedRobolectricTestRunner.Parameters; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.internal.DoNotInstrument; +import org.robolectric.shadows.ShadowNotificationManager; +import org.robolectric.shadows.ShadowPackageManager; + +import java.util.Arrays; +import java.util.Collection; + +/** + * Parameterized tests for {@link NotificationUtils}. + */ +@RunWith(ParameterizedRobolectricTestRunner.class) +@DoNotInstrument +@Config(sdk = {Build.VERSION_CODES.O_MR1}) +public class NotificationUtilsTest { + @Parameter(0) + public String mTestName; + + @Parameter(1) + public String mMetadataKey; + + @Parameter(2) + public Object mMetadataValue; + + @Parameter(3) + public int mExpectedImportance; + + @Parameter(4) + public boolean mExpectedShouldUseHighPriority; + + private Context mContext; + private PackageManager mPackageManager; + private ShadowPackageManager mShadowPackageManager; + private NotificationManager mNotificationManager; + private ShadowNotificationManager mShadowNotificationManager; + + private static final String CHANNEL_NAME = "General Notifications"; + private static final String EXPECTED_CHANNEL_ID = "general_notifications_channel_id"; + + @Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList(new Object[][] { + { + "default_unspecified", + null, + null, + NotificationManager.IMPORTANCE_DEFAULT, + false + }, + { + "androidx_boolean_true", + NotificationUtils.METADATA_USE_HIGH_PRI_NOTIFICATIONS_ANDROIDX, + true, + NotificationManager.IMPORTANCE_HIGH, + true + }, + { + "androidx_string_true", + NotificationUtils.METADATA_USE_HIGH_PRI_NOTIFICATIONS_ANDROIDX, + "true", + NotificationManager.IMPORTANCE_HIGH, + true + }, + { + "androidx_string_true_uppercase", + NotificationUtils.METADATA_USE_HIGH_PRI_NOTIFICATIONS_ANDROIDX, + "TRUE", + NotificationManager.IMPORTANCE_HIGH, + true + }, + { + "androidx_boolean_false", + NotificationUtils.METADATA_USE_HIGH_PRI_NOTIFICATIONS_ANDROIDX, + false, + NotificationManager.IMPORTANCE_DEFAULT, + false + }, + { + "androidx_string_false", + NotificationUtils.METADATA_USE_HIGH_PRI_NOTIFICATIONS_ANDROIDX, + "false", + NotificationManager.IMPORTANCE_DEFAULT, + false + }, + { + "androidx_string_invalid", + NotificationUtils.METADATA_USE_HIGH_PRI_NOTIFICATIONS_ANDROIDX, + "invalid_string", + NotificationManager.IMPORTANCE_DEFAULT, + false + }, + }); + } + + public static class TestService extends Service { + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + } + + @Before + public void setUp() { + mContext = Robolectric.setupService(TestService.class); + mPackageManager = mContext.getPackageManager(); + mShadowPackageManager = shadowOf(mPackageManager); + mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + mShadowNotificationManager = shadowOf(mNotificationManager); + + if (mMetadataKey != null && mMetadataValue != null) { + setServiceMetadata(mMetadataKey, mMetadataValue); + } + } + + @Test + public void channelNameToId_replacesSpacesAndAppendsSuffix() { + assertEquals("general_notifications_channel_id", + NotificationUtils.channelNameToId("General Notifications")); + assertEquals("chat_channel_id", + NotificationUtils.channelNameToId("Chat")); + assertEquals("test_channel_name_channel_id", + NotificationUtils.channelNameToId("Test Channel Name")); + } + + @Test + public void getNotificationImportance_matchesExpectedImportance() { + assertEquals(mExpectedImportance, NotificationUtils.getNotificationImportance(mContext)); + } + + @Test + public void shouldUseHighPriorityNotifications_matchesExpectedBoolean() { + assertEquals(mExpectedShouldUseHighPriority, + NotificationUtils.shouldUseHighPriorityNotifications(mContext)); + } + + @Test + public void createNotificationChannel_resolvesImportanceCorrectly() { + NotificationUtils.createNotificationChannel(mContext, CHANNEL_NAME); + + NotificationChannel channel = mNotificationManager.getNotificationChannel(EXPECTED_CHANNEL_ID); + assertNotNull(channel); + assertEquals(CHANNEL_NAME, channel.getName().toString()); + assertEquals(mExpectedImportance, channel.getImportance()); + } + + @Test + public void createNotificationChannel_explicitImportanceOverridesMetadata() { + NotificationUtils.createNotificationChannel(mContext, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); + + NotificationChannel channel = mNotificationManager.getNotificationChannel(EXPECTED_CHANNEL_ID); + assertNotNull(channel); + assertEquals(CHANNEL_NAME, channel.getName().toString()); + assertEquals(NotificationManager.IMPORTANCE_LOW, channel.getImportance()); + } + + @Test + public void areNotificationsEnabled_returnsTrueForNewAndEnabledChannels() { + assertTrue(NotificationUtils.areNotificationsEnabled(mContext, CHANNEL_NAME)); + + NotificationUtils.createNotificationChannel(mContext, CHANNEL_NAME); + assertTrue(NotificationUtils.areNotificationsEnabled(mContext, CHANNEL_NAME)); + } + + @Test + public void areNotificationsEnabled_returnsFalseForBlockedChannel() { + NotificationChannel channel = new NotificationChannel( + EXPECTED_CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE); + mNotificationManager.createNotificationChannel(channel); + + assertFalse(NotificationUtils.areNotificationsEnabled(mContext, CHANNEL_NAME)); + } + + @Test + public void areNotificationsEnabled_returnsFalseWhenNotificationsDisabledGlobally() { + mShadowNotificationManager.setNotificationsEnabled(false); + assertFalse(NotificationUtils.areNotificationsEnabled(mContext, CHANNEL_NAME)); + } + + @Test + public void getNotificationImportance_withNullContext() { + assertEquals(NotificationManager.IMPORTANCE_DEFAULT, + NotificationUtils.getNotificationImportance(null)); + assertFalse(NotificationUtils.shouldUseHighPriorityNotifications(null)); + } + + @Test + public void getNotificationImportance_withNonServiceContext() { + Context appContext = RuntimeEnvironment.application; + assertEquals(NotificationManager.IMPORTANCE_DEFAULT, + NotificationUtils.getNotificationImportance(appContext)); + assertFalse(NotificationUtils.shouldUseHighPriorityNotifications(appContext)); + } + + private void setServiceMetadata(String key, Object value) { + ComponentName componentName = new ComponentName(mContext, mContext.getClass()); + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.packageName = mContext.getPackageName(); + serviceInfo.name = componentName.getClassName(); + serviceInfo.metaData = new Bundle(); + if (value instanceof Boolean) { + serviceInfo.metaData.putBoolean(key, (Boolean) value); + } else if (value instanceof String) { + serviceInfo.metaData.putString(key, (String) value); + } else if (value instanceof Integer) { + serviceInfo.metaData.putInt(key, (Integer) value); + } + mShadowPackageManager.addOrUpdateService(serviceInfo); + } +} diff --git a/demos/twa-notification-high-priority/README.md b/demos/twa-notification-high-priority/README.md new file mode 100644 index 00000000..88b21a3b --- /dev/null +++ b/demos/twa-notification-high-priority/README.md @@ -0,0 +1,8 @@ +# Trusted Web Activity / High Priority Notification Delegation Demo + +This demo application shows how a developer can override notification channels and high-priority notification behaviors using native code and `androidx.browser.trusted.USE_HIGH_PRI_NOTIFICATIONS` metadata. + +The demo launches the Notification API sample page at: +`https://googlechrome.github.io/samples/pwa-testing/notificationsapi/` + +The relevant code lives inside `NotificationDelegationService`. diff --git a/demos/twa-notification-high-priority/build.gradle b/demos/twa-notification-high-priority/build.gradle new file mode 100644 index 00000000..afaca21d --- /dev/null +++ b/demos/twa-notification-high-priority/build.gradle @@ -0,0 +1,32 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'com.google.androidbrowserhelper.demos.twa_notification_high_priority' + + defaultConfig { + applicationId "com.google.androidbrowserhelper.demos.twa_notification_high_priority" + minSdkVersion 23 + compileSdk 36 + targetSdkVersion 31 + versionCode 1 + versionName "1.0" + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + buildTypes { + release { + minifyEnabled false + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation project(path: ':androidbrowserhelper') +} diff --git a/demos/twa-notification-high-priority/src/main/AndroidManifest.xml b/demos/twa-notification-high-priority/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b98e72a8 --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/AndroidManifest.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/twa-notification-high-priority/src/main/java/com/google/androidbrowserhelper/demos/twa_notification_high_priority/NotificationDelegationService.java b/demos/twa-notification-high-priority/src/main/java/com/google/androidbrowserhelper/demos/twa_notification_high_priority/NotificationDelegationService.java new file mode 100644 index 00000000..77c0b0b0 --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/java/com/google/androidbrowserhelper/demos/twa_notification_high_priority/NotificationDelegationService.java @@ -0,0 +1,104 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidbrowserhelper.demos.twa_notification_high_priority; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.media.AudioAttributes; +import android.net.Uri; +import android.os.Build; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.androidbrowserhelper.trusted.DelegationService; +import com.google.androidbrowserhelper.trusted.NotificationUtils; + +public class NotificationDelegationService extends DelegationService { + private static final String TAG = "NotificationDelegation"; + + @Override + public void onCreate() { + super.onCreate(); + Log.i(TAG, "NotificationDelegationService onCreate() triggered"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.i(TAG, "NotificationDelegationService onDestroy() triggered"); + } + + @Override + public boolean onNotifyNotificationWithChannel( + @NonNull String platformTag, + int platformId, + @NonNull Notification notification, + @NonNull String channelName) { + int importance = NotificationUtils.getNotificationImportance(this); + Log.i(TAG, "Notification triggered for channel: " + channelName + + " (ID: " + NotificationUtils.channelNameToId(channelName) + ")" + + " with importance: " + importance + + " (HIGH=" + (importance == NotificationManager.IMPORTANCE_HIGH) + ")"); + + NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + Uri airhornUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + this.getPackageName() + "/" + R.raw.airhorn); + + // Notification.Builder.recoverBuilder() was introduced in Nougat, so we prefer it when + // available. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Notification.Builder builder = + Notification.Builder.recoverBuilder(this, notification); + + // Ensure high priority is set on the builder for heads-up presentation + builder.setPriority(Notification.PRIORITY_HIGH); + + // From Android O and above, importance and sound are set on the Channel. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + String channelId = NotificationUtils.channelNameToId(channelName); + builder.setChannelId(channelId); + + // Creates or updates the notification channel with configured importance, sound, and vibration + NotificationChannel channel = new NotificationChannel( + channelId, channelName, importance); + AudioAttributes audioAttributes = new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_NOTIFICATION) + .build(); + channel.setSound(airhornUri, audioAttributes); + channel.enableVibration(true); + + mNotificationManager.createNotificationChannel(channel); + } + + builder.setSound(airhornUri); + notification = builder.build(); + } else { + notification.sound = airhornUri; + } + + mNotificationManager.notify(platformTag, platformId, notification); + return true; + } +} diff --git a/demos/twa-notification-high-priority/src/main/res/drawable-anydpi/ic_notification_icon.xml b/demos/twa-notification-high-priority/src/main/res/drawable-anydpi/ic_notification_icon.xml new file mode 100644 index 00000000..94b0a2ab --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/res/drawable-anydpi/ic_notification_icon.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/demos/twa-notification-high-priority/src/main/res/drawable-hdpi/ic_notification_icon.png b/demos/twa-notification-high-priority/src/main/res/drawable-hdpi/ic_notification_icon.png new file mode 100644 index 00000000..c44655ce Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/drawable-hdpi/ic_notification_icon.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/drawable-mdpi/ic_notification_icon.png b/demos/twa-notification-high-priority/src/main/res/drawable-mdpi/ic_notification_icon.png new file mode 100644 index 00000000..241bdb8e Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/drawable-mdpi/ic_notification_icon.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/drawable-v24/ic_launcher_foreground.xml b/demos/twa-notification-high-priority/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..c478e087 --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + diff --git a/demos/twa-notification-high-priority/src/main/res/drawable-xhdpi/ic_notification_icon.png b/demos/twa-notification-high-priority/src/main/res/drawable-xhdpi/ic_notification_icon.png new file mode 100644 index 00000000..ba11f018 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/drawable-xhdpi/ic_notification_icon.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/drawable-xhdpi/splash.png b/demos/twa-notification-high-priority/src/main/res/drawable-xhdpi/splash.png new file mode 100644 index 00000000..8de34211 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/drawable-xhdpi/splash.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/drawable-xxhdpi/ic_notification_icon.png b/demos/twa-notification-high-priority/src/main/res/drawable-xxhdpi/ic_notification_icon.png new file mode 100644 index 00000000..e749e2f5 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/drawable-xxhdpi/ic_notification_icon.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/drawable/ic_launcher_background.xml b/demos/twa-notification-high-priority/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..ab8bc42e --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/demos/twa-notification-high-priority/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..6b78462d --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/demos/twa-notification-high-priority/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..287f5053 --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-hdpi/ic_launcher.png b/demos/twa-notification-high-priority/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..a571e600 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-hdpi/ic_launcher_round.png b/demos/twa-notification-high-priority/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..61da551c Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-mdpi/ic_launcher.png b/demos/twa-notification-high-priority/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..c41dd285 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-mdpi/ic_launcher_round.png b/demos/twa-notification-high-priority/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..db5080a7 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-xhdpi/ic_launcher.png b/demos/twa-notification-high-priority/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..6dba46da Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/demos/twa-notification-high-priority/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..da31a871 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-xxhdpi/ic_launcher.png b/demos/twa-notification-high-priority/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..15ac6817 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/demos/twa-notification-high-priority/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..b216f2d3 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/demos/twa-notification-high-priority/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..f25a4197 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/demos/twa-notification-high-priority/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..e96783cc Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/demos/twa-notification-high-priority/src/main/res/raw/airhorn.mp3 b/demos/twa-notification-high-priority/src/main/res/raw/airhorn.mp3 new file mode 100644 index 00000000..af4d1866 Binary files /dev/null and b/demos/twa-notification-high-priority/src/main/res/raw/airhorn.mp3 differ diff --git a/demos/twa-notification-high-priority/src/main/res/values/colors.xml b/demos/twa-notification-high-priority/src/main/res/values/colors.xml new file mode 100644 index 00000000..77536b23 --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/res/values/colors.xml @@ -0,0 +1,21 @@ + + + + #303F9F + #303F9F + #303F9F + #303F9F + #303F9F + #303F9F + diff --git a/demos/twa-notification-high-priority/src/main/res/values/strings.xml b/demos/twa-notification-high-priority/src/main/res/values/strings.xml new file mode 100644 index 00000000..5e38e384 --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/res/values/strings.xml @@ -0,0 +1,24 @@ + + + twa-notification-high-priority + + [{ + \"relation\": [\"delegate_permission/common.handle_all_urls\"], + \"target\": { + \"namespace\": \"web\", + \"site\": \"https://dp-goog.github.io\"} + }] + + com.google.browser.examples.twa_notification_high_priority.fileprovider + diff --git a/demos/twa-notification-high-priority/src/main/res/xml/filepaths.xml b/demos/twa-notification-high-priority/src/main/res/xml/filepaths.xml new file mode 100644 index 00000000..828271b0 --- /dev/null +++ b/demos/twa-notification-high-priority/src/main/res/xml/filepaths.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/settings.gradle b/settings.gradle index 48798a7f..5299f0bb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -45,6 +45,7 @@ include ':demos:twa-firebase-analytics' include ':demos:twa-location-delegation' include ':demos:twa-multi-domain' include ':demos:twa-notification-delegation' +include ':demos:twa-notification-high-priority' include ':demos:twa-offline-first' include ':demos:twa-orientation' include ':demos:twa-play-billing'