Merge "Wraps NotificationCompatBuilder legacy API calls" into androidx-main
diff --git a/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java b/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
index 15b05ac..7dbdfda 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
@@ -25,14 +25,21 @@
 import static androidx.core.app.NotificationCompat.GROUP_ALERT_SUMMARY;
 
 import android.app.Notification;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.LocusId;
+import android.graphics.drawable.Icon;
+import android.media.AudioAttributes;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.SparseArray;
 import android.widget.RemoteViews;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.collection.ArraySet;
 import androidx.core.graphics.drawable.IconCompat;
@@ -69,7 +76,7 @@
         mBuilderCompat = b;
         mContext = b.mContext;
         if (Build.VERSION.SDK_INT >= 26) {
-            mBuilder = new Notification.Builder(b.mContext, b.mChannelId);
+            mBuilder = Api26Impl.createBuilder(b.mContext, b.mChannelId);
         } else {
             mBuilder = new Notification.Builder(b.mContext);
         }
@@ -98,9 +105,9 @@
             mBuilder.setSound(n.sound, n.audioStreamType);
         }
         if (Build.VERSION.SDK_INT >= 16) {
-            mBuilder.setSubText(b.mSubText)
-                    .setUsesChronometer(b.mUseChronometer)
-                    .setPriority(b.mPriority);
+            Api16Impl.setPriority(
+                    Api16Impl.setUsesChronometer(Api16Impl.setSubText(mBuilder, b.mSubText),
+                            b.mUseChronometer), b.mPriority);
             for (NotificationCompat.Action action : b.mActions) {
                 addAction(action);
             }
@@ -130,7 +137,7 @@
             mBigContentView = b.mBigContentView;
         }
         if (Build.VERSION.SDK_INT >= 17) {
-            mBuilder.setShowWhen(b.mShowWhen);
+            Api17Impl.setShowWhen(mBuilder, b.mShowWhen);
         }
         if (Build.VERSION.SDK_INT >= 19) {
             if (Build.VERSION.SDK_INT < 21) {
@@ -142,20 +149,18 @@
             }
         }
         if (Build.VERSION.SDK_INT >= 20) {
-            mBuilder.setLocalOnly(b.mLocalOnly)
-                    .setGroup(b.mGroupKey)
-                    .setGroupSummary(b.mGroupSummary)
-                    .setSortKey(b.mSortKey);
-
+            Api20Impl.setLocalOnly(mBuilder, b.mLocalOnly);
+            Api20Impl.setGroup(mBuilder, b.mGroupKey);
+            Api20Impl.setSortKey(mBuilder, b.mSortKey);
+            Api20Impl.setGroupSummary(mBuilder, b.mGroupSummary);
             mGroupAlertBehavior = b.mGroupAlertBehavior;
         }
         if (Build.VERSION.SDK_INT >= 21) {
-            mBuilder.setCategory(b.mCategory)
-                    .setColor(b.mColor)
-                    .setVisibility(b.mVisibility)
-                    .setPublicVersion(b.mPublicVersion)
-                    .setSound(n.sound, n.audioAttributes);
-
+            Api21Impl.setCategory(mBuilder, b.mCategory);
+            Api21Impl.setColor(mBuilder, b.mColor);
+            Api21Impl.setVisibility(mBuilder, b.mVisibility);
+            Api21Impl.setPublicVersion(mBuilder, b.mPublicVersion);
+            Api21Impl.setSound(mBuilder, n.sound, n.audioAttributes);
 
             final List<String> people;
             if (Build.VERSION.SDK_INT < 28) {
@@ -165,7 +170,7 @@
             }
             if (people != null && !people.isEmpty()) {
                 for (String person : people) {
-                    mBuilder.addPerson(person);
+                    Api21Impl.addPerson(mBuilder, person);
                 }
             }
 
@@ -199,30 +204,30 @@
         }
         if (Build.VERSION.SDK_INT >= 23) {
             if (b.mSmallIcon != null) {
-                mBuilder.setSmallIcon(b.mSmallIcon);
+                Api23Impl.setSmallIcon(mBuilder, b.mSmallIcon);
             }
         }
         if (Build.VERSION.SDK_INT >= 24) {
-            mBuilder.setExtras(b.mExtras)
-                    .setRemoteInputHistory(b.mRemoteInputHistory);
+            Api19Impl.setExtras(mBuilder, b.mExtras);
+            Api24Impl.setRemoteInputHistory(mBuilder, b.mRemoteInputHistory);
             if (b.mContentView != null) {
-                mBuilder.setCustomContentView(b.mContentView);
+                Api24Impl.setCustomContentView(mBuilder, b.mContentView);
             }
             if (b.mBigContentView != null) {
-                mBuilder.setCustomBigContentView(b.mBigContentView);
+                Api24Impl.setCustomBigContentView(mBuilder, b.mBigContentView);
             }
             if (b.mHeadsUpContentView != null) {
-                mBuilder.setCustomHeadsUpContentView(b.mHeadsUpContentView);
+                Api24Impl.setCustomHeadsUpContentView(mBuilder, b.mHeadsUpContentView);
             }
         }
         if (Build.VERSION.SDK_INT >= 26) {
-            mBuilder.setBadgeIconType(b.mBadgeIcon)
-                    .setSettingsText(b.mSettingsText)
-                    .setShortcutId(b.mShortcutId)
-                    .setTimeoutAfter(b.mTimeout)
-                    .setGroupAlertBehavior(b.mGroupAlertBehavior);
+            Api26Impl.setBadgeIconType(mBuilder, b.mBadgeIcon);
+            Api26Impl.setSettingsText(mBuilder, b.mSettingsText);
+            Api26Impl.setShortcutId(mBuilder, b.mShortcutId);
+            Api26Impl.setTimeoutAfter(mBuilder, b.mTimeout);
+            Api26Impl.setGroupAlertBehavior(mBuilder, b.mGroupAlertBehavior);
             if (b.mColorizedSet) {
-                mBuilder.setColorized(b.mColorized);
+                Api26Impl.setColorized(mBuilder, b.mColorized);
             }
 
             if (!TextUtils.isEmpty(b.mChannelId)) {
@@ -234,22 +239,22 @@
         }
         if (Build.VERSION.SDK_INT >= 28) {
             for (Person p : b.mPersonList) {
-                mBuilder.addPerson(p.toAndroidPerson());
+                Api28Impl.addPerson(mBuilder, p.toAndroidPerson());
             }
         }
         if (Build.VERSION.SDK_INT >= 29) {
-            mBuilder.setAllowSystemGeneratedContextualActions(
+            Api29Impl.setAllowSystemGeneratedContextualActions(mBuilder,
                     b.mAllowSystemGeneratedContextualActions);
             // TODO: Consider roundtripping NotificationCompat.BubbleMetadata on pre-Q platforms.
-            mBuilder.setBubbleMetadata(
+            Api29Impl.setBubbleMetadata(mBuilder,
                     NotificationCompat.BubbleMetadata.toPlatform(b.mBubbleMetadata));
             if (b.mLocusId != null) {
-                mBuilder.setLocusId(b.mLocusId.toLocusId());
+                Api29Impl.setLocusId(mBuilder, b.mLocusId.toLocusId());
             }
         }
         if (Build.VERSION.SDK_INT >= 31) {
             if (b.mFgsDeferBehavior != NotificationCompat.FOREGROUND_SERVICE_DEFAULT) {
-                mBuilder.setForegroundServiceBehavior(b.mFgsDeferBehavior);
+                Api31Impl.setForegroundServiceBehavior(mBuilder, b.mFgsDeferBehavior);
             }
         }
 
@@ -268,9 +273,9 @@
 
             if (Build.VERSION.SDK_INT >= 26) {
                 if (TextUtils.isEmpty(mBuilderCompat.mGroupKey)) {
-                    mBuilder.setGroup(NotificationCompat.GROUP_KEY_SILENT);
+                    Api20Impl.setGroup(mBuilder, NotificationCompat.GROUP_KEY_SILENT);
                 }
-                mBuilder.setGroupAlertBehavior(mGroupAlertBehavior);
+                Api26Impl.setGroupAlertBehavior(mBuilder, mGroupAlertBehavior);
             }
         }
     }
@@ -355,20 +360,18 @@
             Notification.Action.Builder actionBuilder;
             IconCompat iconCompat = action.getIconCompat();
             if (Build.VERSION.SDK_INT >= 23) {
-                actionBuilder = new Notification.Action.Builder(
-                        iconCompat != null ? iconCompat.toIcon() : null,
-                        action.getTitle(),
+                actionBuilder = Api23Impl.createBuilder(
+                        iconCompat != null ? iconCompat.toIcon() : null, action.getTitle(),
                         action.getActionIntent());
             } else {
-                actionBuilder = new Notification.Action.Builder(
-                        iconCompat != null ? iconCompat.getResId() : 0,
-                        action.getTitle(),
+                actionBuilder = Api20Impl.createBuilder(
+                        iconCompat != null ? iconCompat.getResId() : 0, action.getTitle(),
                         action.getActionIntent());
             }
             if (action.getRemoteInputs() != null) {
                 for (android.app.RemoteInput remoteInput : RemoteInput.fromCompat(
                         action.getRemoteInputs())) {
-                    actionBuilder.addRemoteInput(remoteInput);
+                    Api20Impl.addRemoteInput(actionBuilder, remoteInput);
                 }
             }
             Bundle actionExtras;
@@ -380,27 +383,29 @@
             actionExtras.putBoolean(NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES,
                     action.getAllowGeneratedReplies());
             if (Build.VERSION.SDK_INT >= 24) {
-                actionBuilder.setAllowGeneratedReplies(action.getAllowGeneratedReplies());
+                Api24Impl.setAllowGeneratedReplies(actionBuilder,
+                        action.getAllowGeneratedReplies());
             }
 
             actionExtras.putInt(NotificationCompat.Action.EXTRA_SEMANTIC_ACTION,
                     action.getSemanticAction());
             if (Build.VERSION.SDK_INT >= 28) {
-                actionBuilder.setSemanticAction(action.getSemanticAction());
+                Api28Impl.setSemanticAction(actionBuilder, action.getSemanticAction());
             }
 
             if (Build.VERSION.SDK_INT >= 29) {
-                actionBuilder.setContextual(action.isContextual());
+                Api29Impl.setContextual(actionBuilder, action.isContextual());
             }
 
             if (Build.VERSION.SDK_INT >= 31) {
-                actionBuilder.setAuthenticationRequired(action.isAuthenticationRequired());
+                Api31Impl.setAuthenticationRequired(actionBuilder,
+                        action.isAuthenticationRequired());
             }
 
             actionExtras.putBoolean(NotificationCompat.Action.EXTRA_SHOWS_USER_INTERFACE,
                     action.getShowsUserInterface());
-            actionBuilder.addExtras(actionExtras);
-            mBuilder.addAction(actionBuilder.build());
+            Api20Impl.addExtras(actionBuilder, actionExtras);
+            Api20Impl.addAction(mBuilder, Api20Impl.build(actionBuilder));
         } else if (Build.VERSION.SDK_INT >= 16) {
             mActionExtrasList.add(
                     NotificationCompatJellybean.writeActionAndGetExtras(mBuilder, action));
@@ -410,19 +415,19 @@
     @SuppressWarnings("deprecation")
     protected Notification buildInternal() {
         if (Build.VERSION.SDK_INT >= 26) {
-            return mBuilder.build();
+            return Api16Impl.build(mBuilder);
         } else if (Build.VERSION.SDK_INT >= 24) {
-            Notification notification =  mBuilder.build();
+            Notification notification =  Api16Impl.build(mBuilder);
 
             if (mGroupAlertBehavior != GROUP_ALERT_ALL) {
                 // if is summary and only children should alert
-                if (notification.getGroup() != null
+                if (Api20Impl.getGroup(notification) != null
                         && (notification.flags & FLAG_GROUP_SUMMARY) != 0
                         && mGroupAlertBehavior == GROUP_ALERT_CHILDREN) {
                     removeSoundAndVibration(notification);
                 }
                 // if is group child and only summary should alert
-                if (notification.getGroup() != null
+                if (Api20Impl.getGroup(notification) != null
                         && (notification.flags & FLAG_GROUP_SUMMARY) == 0
                         && mGroupAlertBehavior == GROUP_ALERT_SUMMARY) {
                     removeSoundAndVibration(notification);
@@ -431,8 +436,8 @@
 
             return notification;
         } else if (Build.VERSION.SDK_INT >= 21) {
-            mBuilder.setExtras(mExtras);
-            Notification notification = mBuilder.build();
+            Api19Impl.setExtras(mBuilder, mExtras);
+            Notification notification = Api16Impl.build(mBuilder);
             if (mContentView != null) {
                 notification.contentView = mContentView;
             }
@@ -445,13 +450,13 @@
 
             if (mGroupAlertBehavior != GROUP_ALERT_ALL) {
                 // if is summary and only children should alert
-                if (notification.getGroup() != null
+                if (Api20Impl.getGroup(notification) != null
                         && (notification.flags & FLAG_GROUP_SUMMARY) != 0
                         && mGroupAlertBehavior == GROUP_ALERT_CHILDREN) {
                     removeSoundAndVibration(notification);
                 }
                 // if is group child and only summary should alert
-                if (notification.getGroup() != null
+                if (Api20Impl.getGroup(notification) != null
                         && (notification.flags & FLAG_GROUP_SUMMARY) == 0
                         && mGroupAlertBehavior == GROUP_ALERT_SUMMARY) {
                     removeSoundAndVibration(notification);
@@ -459,8 +464,8 @@
             }
             return notification;
         } else if (Build.VERSION.SDK_INT >= 20) {
-            mBuilder.setExtras(mExtras);
-            Notification notification = mBuilder.build();
+            Api19Impl.setExtras(mBuilder, mExtras);
+            Notification notification = Api16Impl.build(mBuilder);
             if (mContentView != null) {
                 notification.contentView = mContentView;
             }
@@ -470,13 +475,13 @@
 
             if (mGroupAlertBehavior != GROUP_ALERT_ALL) {
                 // if is summary and only children should alert
-                if (notification.getGroup() != null
+                if (Api20Impl.getGroup(notification) != null
                         && (notification.flags & FLAG_GROUP_SUMMARY) != 0
                         && mGroupAlertBehavior == GROUP_ALERT_CHILDREN) {
                     removeSoundAndVibration(notification);
                 }
                 // if is group child and only summary should alert
-                if (notification.getGroup() != null
+                if (Api20Impl.getGroup(notification) != null
                         && (notification.flags & FLAG_GROUP_SUMMARY) == 0
                         && mGroupAlertBehavior == GROUP_ALERT_SUMMARY) {
                     removeSoundAndVibration(notification);
@@ -492,8 +497,8 @@
                 mExtras.putSparseParcelableArray(
                         NotificationCompatExtras.EXTRA_ACTION_EXTRAS, actionExtrasMap);
             }
-            mBuilder.setExtras(mExtras);
-            Notification notification = mBuilder.build();
+            Api19Impl.setExtras(mBuilder, mExtras);
+            Notification notification = Api16Impl.build(mBuilder);
             if (mContentView != null) {
                 notification.contentView = mContentView;
             }
@@ -502,7 +507,7 @@
             }
             return notification;
         } else if (Build.VERSION.SDK_INT >= 16) {
-            Notification notification = mBuilder.build();
+            Notification notification = Api16Impl.build(mBuilder);
             // Merge in developer provided extras, but let the values already set
             // for keys take precedence.
             Bundle extras = NotificationCompat.getExtras(notification);
@@ -538,4 +543,360 @@
         notification.defaults &= ~DEFAULT_SOUND;
         notification.defaults &= ~DEFAULT_VIBRATE;
     }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 16; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(16)
+    static class Api16Impl {
+        private Api16Impl() { }
+
+        @DoNotInline
+        static Notification.Builder setSubText(Notification.Builder builder, CharSequence text) {
+            return builder.setSubText(text);
+        }
+
+        @DoNotInline
+        static Notification.Builder setUsesChronometer(Notification.Builder builder, boolean b) {
+            return builder.setUsesChronometer(b);
+        }
+
+        @DoNotInline
+        static Notification.Builder setPriority(Notification.Builder builder, int pri) {
+            return builder.setPriority(pri);
+        }
+
+        @DoNotInline
+        static Notification build(Notification.Builder builder) {
+            return builder.build();
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 17; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(17)
+    static class Api17Impl {
+        private Api17Impl() {
+        }
+
+        @DoNotInline
+        static Notification.Builder setShowWhen(Notification.Builder builder, boolean show) {
+            return builder.setShowWhen(show);
+        }
+
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 19; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(19)
+    static class Api19Impl {
+        private Api19Impl() { }
+
+        @DoNotInline
+        static Notification.Builder setExtras(Notification.Builder builder, Bundle extras) {
+            return builder.setExtras(extras);
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 20; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(20)
+    static class Api20Impl {
+        private Api20Impl() { }
+
+        @DoNotInline
+        static Notification.Action.Builder createBuilder(int icon, CharSequence title,
+                PendingIntent intent) {
+            return new Notification.Action.Builder(icon, title, intent);
+        }
+
+        @DoNotInline
+        static Notification.Action.Builder addRemoteInput(Notification.Action.Builder builder,
+                android.app.RemoteInput remoteInput) {
+            return builder.addRemoteInput(remoteInput);
+        }
+
+        @DoNotInline
+        static Notification.Action.Builder addExtras(Notification.Action.Builder builder,
+                Bundle extras) {
+            return builder.addExtras(extras);
+        }
+
+
+        @DoNotInline
+        static Notification.Builder addAction(Notification.Builder builder,
+                Notification.Action action) {
+            return builder.addAction(action);
+        }
+
+        @DoNotInline
+        static Notification.Action build(Notification.Action.Builder builder) {
+            return builder.build();
+        }
+
+        @DoNotInline
+        static String getGroup(Notification notification) {
+            return notification.getGroup();
+        }
+
+        @DoNotInline
+        static Notification.Builder setGroup(Notification.Builder builder, String groupKey) {
+            return builder.setGroup(groupKey);
+        }
+
+        @DoNotInline
+        static Notification.Builder setGroupSummary(Notification.Builder builder,
+                boolean isGroupSummary) {
+            return builder.setGroupSummary(isGroupSummary);
+        }
+
+        @DoNotInline
+        static Notification.Builder setLocalOnly(Notification.Builder builder, boolean localOnly) {
+            return builder.setLocalOnly(localOnly);
+        }
+
+        @DoNotInline
+        static Notification.Builder setSortKey(Notification.Builder builder, String sortKey) {
+            return builder.setSortKey(sortKey);
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 21; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(21)
+    static class Api21Impl {
+        private Api21Impl() { }
+
+        @DoNotInline
+        static Notification.Builder addPerson(Notification.Builder builder, String uri) {
+            return builder.addPerson(uri);
+        }
+
+        @DoNotInline
+        static Notification.Builder setCategory(Notification.Builder builder, String category) {
+            return builder.setCategory(category);
+        }
+
+        @DoNotInline
+        static Notification.Builder setColor(Notification.Builder builder, int argb) {
+            return builder.setColor(argb);
+        }
+
+        @DoNotInline
+        static Notification.Builder setVisibility(Notification.Builder builder, int visibility) {
+            return builder.setVisibility(visibility);
+        }
+
+        @DoNotInline
+        static Notification.Builder setPublicVersion(Notification.Builder builder, Notification n) {
+            return builder.setPublicVersion(n);
+        }
+
+        @DoNotInline
+        static Notification.Builder setSound(Notification.Builder builder, Uri sound,
+                AudioAttributes audioAttributes) {
+            return builder.setSound(sound, audioAttributes);
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 23; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(23)
+    static class Api23Impl {
+        private Api23Impl() { }
+
+        @DoNotInline
+        static Notification.Action.Builder createBuilder(Icon icon, CharSequence title,
+                PendingIntent intent) {
+            return new Notification.Action.Builder(icon, title, intent);
+        }
+
+        @DoNotInline
+        static Notification.Builder setSmallIcon(Notification.Builder builder, Icon icon) {
+            return builder.setSmallIcon(icon);
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 24; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(24)
+    static class Api24Impl {
+        private Api24Impl() { }
+
+        @DoNotInline
+        static Notification.Action.Builder setAllowGeneratedReplies(
+                Notification.Action.Builder builder, boolean allowGeneratedReplies) {
+            return builder.setAllowGeneratedReplies(allowGeneratedReplies);
+        }
+
+        @DoNotInline
+        static Notification.Builder setRemoteInputHistory(Notification.Builder builder,
+                CharSequence[] text) {
+            return builder.setRemoteInputHistory(text);
+        }
+
+        @DoNotInline
+        static Notification.Builder setCustomContentView(Notification.Builder builder,
+                RemoteViews contentView) {
+            return builder.setCustomContentView(contentView);
+        }
+
+        @DoNotInline
+        static Notification.Builder setCustomBigContentView(Notification.Builder builder,
+                RemoteViews contentView) {
+            return builder.setCustomBigContentView(contentView);
+        }
+
+        @DoNotInline
+        static Notification.Builder setCustomHeadsUpContentView(Notification.Builder builder,
+                RemoteViews contentView) {
+            return builder.setCustomHeadsUpContentView(contentView);
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 26; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(26)
+    static class Api26Impl {
+        private Api26Impl() { }
+
+        @DoNotInline
+        static Notification.Builder createBuilder(Context context, String channelId) {
+            return new Notification.Builder(context, channelId);
+        }
+
+        @DoNotInline
+        static Notification.Builder setGroupAlertBehavior(Notification.Builder builder,
+                int groupAlertBehavior) {
+            return builder.setGroupAlertBehavior(groupAlertBehavior);
+        }
+
+        @DoNotInline
+        static Notification.Builder setColorized(Notification.Builder builder, boolean colorize) {
+            return builder.setColorized(colorize);
+        }
+
+        @DoNotInline
+        static Notification.Builder setBadgeIconType(Notification.Builder builder, int icon) {
+            return builder.setBadgeIconType(icon);
+        }
+
+        @DoNotInline
+        static Notification.Builder setSettingsText(Notification.Builder builder,
+                CharSequence text) {
+            return builder.setSettingsText(text);
+        }
+
+        @DoNotInline
+        static Notification.Builder setShortcutId(Notification.Builder builder, String shortcutId) {
+            return builder.setShortcutId(shortcutId);
+        }
+
+        @DoNotInline
+        static Notification.Builder setTimeoutAfter(Notification.Builder builder, long durationMs) {
+            return builder.setTimeoutAfter(durationMs);
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 28; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(28)
+    static class Api28Impl {
+        private Api28Impl() {
+        }
+
+        @DoNotInline
+        static Notification.Action.Builder setSemanticAction(Notification.Action.Builder builder,
+                int semanticAction) {
+            return builder.setSemanticAction(semanticAction);
+        }
+
+        @DoNotInline
+        static Notification.Builder addPerson(Notification.Builder builder,
+                android.app.Person person) {
+            return builder.addPerson(person);
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 29; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(29)
+    static class Api29Impl {
+        private Api29Impl() { }
+
+        @DoNotInline
+        static Notification.Action.Builder setContextual(Notification.Action.Builder builder,
+                boolean isContextual) {
+            return builder.setContextual(isContextual);
+        }
+
+        @DoNotInline
+        static Notification.Builder setLocusId(Notification.Builder builder, LocusId locusId) {
+            return builder.setLocusId(locusId);
+        }
+
+        @DoNotInline
+        static Notification.Builder setBubbleMetadata(Notification.Builder builder,
+                Notification.BubbleMetadata data) {
+            return builder.setBubbleMetadata(data);
+        }
+
+        @DoNotInline
+        static Notification.Builder setAllowSystemGeneratedContextualActions(
+                Notification.Builder builder, boolean allowed) {
+            return builder.setAllowSystemGeneratedContextualActions(allowed);
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationCompatBuilder} methods which
+     * were added in API 31; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(31)
+    static class Api31Impl {
+        private Api31Impl() {
+        }
+
+        @DoNotInline
+        static Notification.Action.Builder setAuthenticationRequired(
+                Notification.Action.Builder builder, boolean authenticationRequired) {
+            return builder.setAuthenticationRequired(authenticationRequired);
+        }
+
+        @DoNotInline
+        static Notification.Builder setForegroundServiceBehavior(Notification.Builder builder,
+                int behavior) {
+            return builder.setForegroundServiceBehavior(behavior);
+        }
+    }
 }