public final class

CarNotificationManager

extends java.lang.Object

 java.lang.Object

↳androidx.car.app.notification.CarNotificationManager

Gradle dependencies

compile group: 'androidx.car.app', name: 'app', version: '1.2.0-rc01'

  • groupId: androidx.car.app
  • artifactId: app
  • version: 1.2.0-rc01

Artifact androidx.car.app:app:1.2.0-rc01 it located at Google repository (https://maven.google.com/)

Overview

A manager for car apps to send notifications. This class wraps a NotificationManagerCompat to manage the actual sending of the Notification.

Summary

Methods
public booleanareNotificationsEnabled()

Returns whether notifications from the calling package are not blocked.

public voidcancel(int id)

Cancels a previously shown notification.

public voidcancel(java.lang.String tag, int id)

Cancels a previously shown notification.

public voidcancelAll()

Cancels all previously shown notifications.

public voidcreateNotificationChannel(NotificationChannelCompat channel)

Creates a notification channel that notifications can be posted to.

public voidcreateNotificationChannelGroup(NotificationChannelGroupCompat group)

Creates a group container for objects.

public voidcreateNotificationChannelGroups(java.util.List<NotificationChannelGroupCompat> groups)

Creates multiple notification channel groups.

public voidcreateNotificationChannels(java.util.List<NotificationChannelCompat> channels)

Creates multiple notification channels that different notifications can be posted to.

public voiddeleteNotificationChannel(java.lang.String channelId)

Deletes the given notification channel.

public voiddeleteNotificationChannelGroup(java.lang.String groupId)

Deletes the given notification channel group, and all notification channels that belong to it.

public voiddeleteUnlistedNotificationChannels(java.util.Collection<java.lang.String> channelIds)

Deletes notification channels for which ids are NOT given.

public static CarNotificationManagerfrom(Context context)

Returns a CarNotificationManager instance for a provided context.

public static java.util.Set<java.lang.String>getEnabledListenerPackages(Context context)

Get the set of packages that have an enabled notification listener component within them.

public intgetImportance()

Returns the user specified importance for notifications from the calling package.

public NotificationChannelCompatgetNotificationChannel(java.lang.String channelId)

Returns the notification channel settings for a given channel id.

public NotificationChannelCompatgetNotificationChannel(java.lang.String channelId, java.lang.String conversationId)

Returns the notification channel settings for a given channel and .

public NotificationChannelGroupCompatgetNotificationChannelGroup(java.lang.String channelGroupId)

Returns the notification channel group settings for a given channel group id.

public java.util.List<NotificationChannelGroupCompat>getNotificationChannelGroups()

Returns all notification channel groups belonging to the calling app or an empty list on older SDKs which don't support Notification Channels.

public java.util.List<NotificationChannelCompat>getNotificationChannels()

Returns all notification channels belonging to the calling app or an empty list on older SDKs which don't support Notification Channels.

public voidnotify(int id, NotificationCompat.Builder notification)

Posts a notification.

public voidnotify(java.lang.String tag, int id, NotificationCompat.Builder notification)

Post a notification to be shown in the status bar, stream, etc.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Methods

public static CarNotificationManager from(Context context)

Returns a CarNotificationManager instance for a provided context.

public void cancel(int id)

Cancels a previously shown notification.

See also: NotificationManagerCompat.cancel(int)

public void cancel(java.lang.String tag, int id)

Cancels a previously shown notification.

See also: NotificationManagerCompat.cancel(String, int)

public void cancelAll()

Cancels all previously shown notifications.

See also: NotificationManagerCompat.cancelAll()

public void notify(int id, NotificationCompat.Builder notification)

Posts a notification.

Callers are expected to extend the notification with CarAppExtender to ensure the notification will show up on all car environments. This method will extend the notification if it is not already extended for the car using a CarAppExtender.

See also: NotificationManagerCompat.notify(int, Notification)

public void notify(java.lang.String tag, int id, NotificationCompat.Builder notification)

Post a notification to be shown in the status bar, stream, etc.

Callers are expected to extend the notification with CarAppExtender to ensure the notification will show up on all car environments. This method will extend the notification if it is not already extended for the car using a CarAppExtender.

Parameters:

tag: the string identifier for a notification. Can be null.
id: the ID of the notification. The pair (tag, id) must be unique within your app.
notification: the notification to post to the system

public boolean areNotificationsEnabled()

Returns whether notifications from the calling package are not blocked.

See also: NotificationManagerCompat.areNotificationsEnabled()

public int getImportance()

Returns the user specified importance for notifications from the calling package.

See also: NotificationManagerCompat.getImportance()

public void createNotificationChannel(NotificationChannelCompat channel)

Creates a notification channel that notifications can be posted to.

See also: NotificationManagerCompat

public void createNotificationChannelGroup(NotificationChannelGroupCompat group)

Creates a group container for objects.

See also: NotificationManagerCompat

public void createNotificationChannels(java.util.List<NotificationChannelCompat> channels)

Creates multiple notification channels that different notifications can be posted to. See CarNotificationManager.createNotificationChannel(NotificationChannelCompat).

See also: NotificationManagerCompat.createNotificationChannelsCompat(List)

public void createNotificationChannelGroups(java.util.List<NotificationChannelGroupCompat> groups)

Creates multiple notification channel groups. See CarNotificationManager.createNotificationChannelGroup(NotificationChannelGroupCompat).

See also: NotificationManagerCompat.createNotificationChannelGroupsCompat(List)

public void deleteNotificationChannel(java.lang.String channelId)

Deletes the given notification channel.

See also: NotificationManagerCompat.deleteNotificationChannel(String)

public void deleteNotificationChannelGroup(java.lang.String groupId)

Deletes the given notification channel group, and all notification channels that belong to it.

See also: NotificationManagerCompat.deleteNotificationChannelGroup(String)

public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String> channelIds)

Deletes notification channels for which ids are NOT given.

See also: NotificationManagerCompat.deleteUnlistedNotificationChannels(Collection)

public NotificationChannelCompat getNotificationChannel(java.lang.String channelId)

Returns the notification channel settings for a given channel id.

See also: NotificationManagerCompat.getNotificationChannelCompat(String)

public NotificationChannelCompat getNotificationChannel(java.lang.String channelId, java.lang.String conversationId)

Returns the notification channel settings for a given channel and .

See also: NotificationManagerCompat.getNotificationChannelCompat(String, String)

public NotificationChannelGroupCompat getNotificationChannelGroup(java.lang.String channelGroupId)

Returns the notification channel group settings for a given channel group id.

See also: NotificationManagerCompat.getNotificationChannelGroupCompat(String)

public java.util.List<NotificationChannelCompat> getNotificationChannels()

Returns all notification channels belonging to the calling app or an empty list on older SDKs which don't support Notification Channels.

See also: NotificationManagerCompat.getNotificationChannelsCompat()

public java.util.List<NotificationChannelGroupCompat> getNotificationChannelGroups()

Returns all notification channel groups belonging to the calling app or an empty list on older SDKs which don't support Notification Channels.

See also: NotificationManagerCompat.getNotificationChannelGroupsCompat()

public static java.util.Set<java.lang.String> getEnabledListenerPackages(Context context)

Get the set of packages that have an enabled notification listener component within them.

See also: NotificationManagerCompat.getEnabledListenerPackages(Context)

Source

/*
 * Copyright 2021 The Android Open Source Project
 *
 * 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 androidx.car.app.notification;

import static androidx.car.app.model.CarColor.TYPE_BLUE;
import static androidx.car.app.model.CarColor.TYPE_CUSTOM;
import static androidx.car.app.model.CarColor.TYPE_DEFAULT;
import static androidx.car.app.model.CarColor.TYPE_GREEN;
import static androidx.car.app.model.CarColor.TYPE_PRIMARY;
import static androidx.car.app.model.CarColor.TYPE_RED;
import static androidx.car.app.model.CarColor.TYPE_SECONDARY;
import static androidx.car.app.model.CarColor.TYPE_YELLOW;
import static androidx.car.app.utils.CommonUtils.isAutomotiveOS;

import static java.util.Objects.requireNonNull;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.os.Build;

import androidx.annotation.ColorInt;
import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StyleRes;
import androidx.annotation.VisibleForTesting;
import androidx.car.app.R;
import androidx.car.app.model.CarColor;
import androidx.core.app.NotificationChannelCompat;
import androidx.core.app.NotificationChannelGroupCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * A manager for car apps to send notifications.
 *
 * This class wraps a {@link NotificationManagerCompat} to manage the actual sending of the
 * {@link Notification}.
 */
public final class CarNotificationManager {
    @NonNull
    private final Context mContext;
    @NonNull
    private final NotificationManagerCompat mNotificationManagerCompat;
    @ColorInt
    @Nullable
    private final Integer mPrimaryColor;
    @ColorInt
    @Nullable
    private final Integer mPrimaryColorDark;
    @ColorInt
    @Nullable
    private final Integer mSecondaryColor;
    @ColorInt
    @Nullable
    private final Integer mSecondaryColorDark;

    /**
     * Returns a {@link CarNotificationManager} instance for a provided context.
     *
     * @throws NullPointerException if {@code context} is {@code null}
     */
    @NonNull
    public static CarNotificationManager from(@NonNull Context context) {
        return new CarNotificationManager(requireNonNull(context));
    }

    /**
     * Cancels a previously shown notification.
     *
     * @see NotificationManagerCompat#cancel(int)
     */
    public void cancel(int id) {
        mNotificationManagerCompat.cancel(id);
    }

    /**
     * Cancels a previously shown notification.
     *
     * @see NotificationManagerCompat#cancel(String, int)
     */
    public void cancel(@Nullable String tag, int id) {
        mNotificationManagerCompat.cancel(tag, id);
    }

    /**
     * Cancels all previously shown notifications.
     *
     * @see NotificationManagerCompat#cancelAll()
     */
    public void cancelAll() {
        mNotificationManagerCompat.cancelAll();
    }

    /**
     * Posts a notification.
     *
     * <p>Callers are expected to extend the notification with CarAppExtender to ensure the
     * notification will show up on all car environments. This method will extend the
     * notification if it is not already extended for the car using a CarAppExtender.
     *
     * @throws NullPointerException if {@code notification} is {@code null}
     *
     * @see NotificationManagerCompat#notify(int, Notification)
     */
    public void notify(int id, @NonNull NotificationCompat.Builder notification) {
        mNotificationManagerCompat.notify(id, updateForCar(requireNonNull(notification)));
    }

    /**
     * Post a notification to be shown in the status bar, stream, etc.
     *
     * <p>Callers are expected to extend the notification with CarAppExtender to ensure the
     * notification will show up on all car environments. This method will extend the
     * notification if it is not already extended for the car using a CarAppExtender.
     *
     * @param tag          the string identifier for a notification. Can be {@code null}.
     * @param id           the ID of the notification. The pair (tag, id) must be unique within
     *                     your app.
     * @param notification the notification to post to the system
     *
     * @throws NullPointerException if notification is {@code null}
     */
    public void notify(@Nullable String tag, int id,
            @NonNull NotificationCompat.Builder notification) {
        mNotificationManagerCompat.notify(tag, id, updateForCar(requireNonNull(notification)));
    }

    /**
     * Returns whether notifications from the calling package are not blocked.
     *
     * @see NotificationManagerCompat#areNotificationsEnabled()
     */
    public boolean areNotificationsEnabled() {
        return mNotificationManagerCompat.areNotificationsEnabled();
    }

    /**
     * Returns the user specified importance for notifications from the calling package.
     *
     * @see NotificationManagerCompat#getImportance()
     */
    public int getImportance() {
        return mNotificationManagerCompat.getImportance();
    }

    /**
     * Creates a notification channel that notifications can be posted to.
     *
     * @throws NullPointerException if {@code channel} is {@code null}
     *
     * @see NotificationManagerCompat#createNotificationChannel(NotificationChannelCompat)
     */
    public void createNotificationChannel(@NonNull NotificationChannelCompat channel) {
        mNotificationManagerCompat.createNotificationChannel(requireNonNull(channel));
    }

    /**
     * Creates a group container for {@link NotificationChannel} objects.
     *
     * @throws NullPointerException if {@code group} is {@code null}
     *
     * @see NotificationManagerCompat#createNotificationChannelGroup(NotificationChannelGroupCompat)
     */
    public void createNotificationChannelGroup(@NonNull NotificationChannelGroupCompat group) {
        mNotificationManagerCompat.createNotificationChannelGroup(requireNonNull(group));
    }

    /**
     * Creates multiple notification channels that different notifications can be posted to. See
     * {@link #createNotificationChannel(NotificationChannelCompat)}.
     *
     * @throws NullPointerException if {@code channels} is {@code null}
     *
     * @see NotificationManagerCompat#createNotificationChannelsCompat(List)
     */
    public void createNotificationChannels(
            @NonNull List<NotificationChannelCompat> channels) {
        mNotificationManagerCompat.createNotificationChannelsCompat(requireNonNull(channels));
    }

    /**
     * Creates multiple notification channel groups. See
     * {@link #createNotificationChannelGroup(NotificationChannelGroupCompat)}.
     *
     * @throws NullPointerException if {@code groups} is {@code null}
     *
     * @see NotificationManagerCompat#createNotificationChannelGroupsCompat(List)
     */
    public void createNotificationChannelGroups(
            @NonNull List<NotificationChannelGroupCompat> groups) {
        mNotificationManagerCompat.createNotificationChannelGroupsCompat(requireNonNull(groups));
    }

    /**
     * Deletes the given notification channel.
     *
     * @throws NullPointerException if {@code channelId} is {@code null}
     *
     * @see NotificationManagerCompat#deleteNotificationChannel(String)
     */
    public void deleteNotificationChannel(@NonNull String channelId) {
        mNotificationManagerCompat.deleteNotificationChannel(requireNonNull(channelId));
    }

    /**
     * Deletes the given notification channel group, and all notification channels that
     * belong to it.
     *
     * @throws NullPointerException if {@code groupId} is {@code null}
     *
     * @see NotificationManagerCompat#deleteNotificationChannelGroup(String)
     */
    public void deleteNotificationChannelGroup(@NonNull String groupId) {
        mNotificationManagerCompat.deleteNotificationChannelGroup(requireNonNull(groupId));
    }

    /**
     * Deletes notification channels for which ids are NOT given.
     *
     * @throws NullPointerException if {@code channelIds} is {@code null}
     *
     * @see NotificationManagerCompat#deleteUnlistedNotificationChannels(Collection)
     */
    public void deleteUnlistedNotificationChannels(@NonNull Collection<String> channelIds) {
        mNotificationManagerCompat.deleteUnlistedNotificationChannels(requireNonNull(channelIds));
    }

    /**
     * Returns the notification channel settings for a given channel id.
     *
     * @throws NullPointerException if {@code channelId} is {@code null}
     *
     * @see NotificationManagerCompat#getNotificationChannelCompat(String)
     */
    @Nullable
    public NotificationChannelCompat getNotificationChannel(@NonNull String channelId) {
        return mNotificationManagerCompat.getNotificationChannelCompat(requireNonNull(channelId));
    }

    /**
     * Returns the notification channel settings for a given channel and
     * {@link ShortcutInfo#getId() conversation id}.
     *
     * @throws NullPointerException if either {@code channelId} of {@code conversationId} are {@code
     *                              null}
     *
     * @see NotificationManagerCompat#getNotificationChannelCompat(String, String)
     */
    @Nullable
    public NotificationChannelCompat getNotificationChannel(@NonNull String channelId,
            @NonNull String conversationId) {
        return mNotificationManagerCompat.getNotificationChannelCompat(requireNonNull(channelId),
                requireNonNull(conversationId));
    }

    /**
     * Returns the notification channel group settings for a given channel group id.
     *
     * @throws NullPointerException if {@code channelGroupId} is {@code null}
     *
     * @see NotificationManagerCompat#getNotificationChannelGroupCompat(String)
     */
    @Nullable
    public NotificationChannelGroupCompat getNotificationChannelGroup(
            @NonNull String channelGroupId) {
        return mNotificationManagerCompat.getNotificationChannelGroupCompat(
                requireNonNull(channelGroupId));
    }

    /**
     * Returns all notification channels belonging to the calling app
     * or an empty list on older SDKs which don't support Notification Channels.
     *
     * @see NotificationManagerCompat#getNotificationChannelsCompat()
     */
    @NonNull
    public List<NotificationChannelCompat> getNotificationChannels() {
        return mNotificationManagerCompat.getNotificationChannelsCompat();
    }

    /**
     * Returns all notification channel groups belonging to the calling app
     * or an empty list on older SDKs which don't support Notification Channels.
     *
     * @see NotificationManagerCompat#getNotificationChannelGroupsCompat()
     */
    @NonNull
    public List<NotificationChannelGroupCompat> getNotificationChannelGroups() {
        return mNotificationManagerCompat.getNotificationChannelGroupsCompat();
    }

    /**
     * Get the set of packages that have an enabled notification listener component within them.
     *
     * @throws NullPointerException if {@code context} is {@code null}
     *
     * @see NotificationManagerCompat#getEnabledListenerPackages(Context)
     */
    @NonNull
    public static Set<String> getEnabledListenerPackages(@NonNull Context context) {
        return NotificationManagerCompat.getEnabledListenerPackages(requireNonNull(context));
    }

    @VisibleForTesting
    @NonNull
    Notification updateForCar(@NonNull NotificationCompat.Builder notification) {
        if (isAutomotiveOS(mContext)) {
            return updateForAutomotive(notification);
        } else if (!CarAppExtender.isExtended(notification.build())) {
            notification.extend(new CarAppExtender.Builder().build());
        }
        return notification.build();
    }

    private Notification updateForAutomotive(@NonNull NotificationCompat.Builder notification) {
        if (Build.VERSION.SDK_INT < 29) {
            throw new UnsupportedOperationException(
                    "Not supported for Automotive OS before API 29.");
        }

        CarAppExtender carAppExtender = new CarAppExtender(notification.build());

        // CarAppExtender only supports adding Icon to Notification.Action via resource.
        // To convert the Notification.Action in the extender back to NotificationCompat.Action,
        // we need to rely on the Icon.getResId() API which is added in API 28. The other ctors
        // for NotificationCompat.Action would throw an exception because the Icon was created
        // without a res package. See CarAppExtender.Builder.addAction(...) for reference.
        Api29Impl.convertActionsToCompatActions(notification, carAppExtender.getActions());

        CarColor color = carAppExtender.getColor();
        if (color != null) {
            @ColorInt Integer colorInt = getColorInt(color);
            if (colorInt != null) {
                notification.setColorized(true);
                notification.setColor(colorInt);
            }
        }

        PendingIntent contentIntent = carAppExtender.getContentIntent();
        if (contentIntent != null) {
            notification.setContentIntent(contentIntent);
        }

        CharSequence contentTitle = carAppExtender.getContentTitle();
        if (contentTitle != null) {
            notification.setContentTitle(contentTitle);
        }

        CharSequence contentText = carAppExtender.getContentText();
        if (contentText != null) {
            notification.setContentText(contentText);
        }

        PendingIntent deleteIntent = carAppExtender.getDeleteIntent();
        if (deleteIntent != null) {
            notification.setDeleteIntent(deleteIntent);
        }

        String channelId = carAppExtender.getChannelId();
        if (channelId != null) {
            notification.setChannelId(channelId);
        }

        Bitmap largeIcon = carAppExtender.getLargeIcon();
        if (largeIcon != null) {
            notification.setLargeIcon(largeIcon);
        }

        int smallIconRes = carAppExtender.getSmallIcon();
        if (smallIconRes != Resources.ID_NULL) {
            notification.setSmallIcon(smallIconRes);
        }

        return notification.build();
    }

    @VisibleForTesting
    @ColorInt
    @Nullable
    Integer getColorInt(CarColor carColor) {
        boolean isDarkMode =
                (mContext.getResources().getConfiguration().uiMode
                        & Configuration.UI_MODE_NIGHT_MASK)
                        == Configuration.UI_MODE_NIGHT_YES;

        switch (carColor.getType()) {
            case TYPE_CUSTOM:
                return isDarkMode ? carColor.getColorDark() : carColor.getColor();
            case TYPE_PRIMARY:
                return isDarkMode ? mPrimaryColorDark : mPrimaryColor;
            case TYPE_SECONDARY:
                return isDarkMode ? mSecondaryColorDark : mSecondaryColor;
            case TYPE_RED:
                return mContext.getColor(R.color.carColorRed);
            case TYPE_GREEN:
                return mContext.getColor(R.color.carColorGreen);
            case TYPE_BLUE:
                return mContext.getColor(R.color.carColorBlue);
            case TYPE_YELLOW:
                return mContext.getColor(R.color.carColorYellow);
            case TYPE_DEFAULT:
            default:
                return null;
        }
    }

    @StyleRes
    @SuppressWarnings("deprecation")
    private static int loadThemeId(Context context) {
        int theme = Resources.ID_NULL;
        ApplicationInfo applicationInfo;
        try {
            applicationInfo = context.getPackageManager()
                    .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
        } catch (PackageManager.NameNotFoundException e) {
            return theme;
        }

        if (applicationInfo.metaData != null) {
            theme = applicationInfo.metaData.getInt("androidx.car.app.theme");
        }

        return theme;
    }

    @ColorInt
    @Nullable
    private static Integer getColor(int resId, Resources.Theme appTheme) {
        @ColorInt Integer color = null;
        if (resId != Resources.ID_NULL) {
            int[] attr = {resId};
            TypedArray ta = appTheme.obtainStyledAttributes(attr);
            color = ta.getColor(0, 0);
            ta.recycle();
        }
        return color;
    }

    private CarNotificationManager(@NonNull Context context) {
        mContext = requireNonNull(context);
        mNotificationManagerCompat = NotificationManagerCompat.from(context);

        Context themeableContext = mContext.createConfigurationContext(
                context.getResources().getConfiguration());

        @StyleRes int themeId = loadThemeId(context);
        if (themeId != Resources.ID_NULL) {
            themeableContext.setTheme(themeId);
        }

        Resources.Theme theme = themeableContext.getTheme();
        Resources themedResources = theme.getResources();

        int carColorPrimary = themedResources.getIdentifier("carColorPrimary", "attr",
                context.getPackageName());
        mPrimaryColor = getColor(carColorPrimary, theme);

        int carColorPrimaryDark = themedResources.getIdentifier("carColorPrimaryDark", "attr",
                context.getPackageName());
        mPrimaryColorDark = getColor(carColorPrimaryDark, theme);

        int carColorSecondary = themedResources.getIdentifier("carColorSecondary", "attr",
                context.getPackageName());
        mSecondaryColor = getColor(carColorSecondary, theme);

        int carColorSecondaryDark = themedResources.getIdentifier("carColorSecondaryDark", "attr",
                context.getPackageName());
        mSecondaryColorDark = getColor(carColorSecondaryDark, theme);
    }

    @RequiresApi(28)
    private static final class Api29Impl {
        /**
         * Convert the list of {@link Notification.Action} to {@link NotificationCompat.Action} and
         * add them to the input {@code notification}.
         */
        @DoNotInline
        static void convertActionsToCompatActions(@NonNull NotificationCompat.Builder notification,
                @NonNull List<Notification.Action> actions) {
            if (actions.isEmpty()) {
                return;
            }

            notification.clearActions();
            for (Notification.Action action : actions) {
                notification.addAction(fromAndroidAction(action));
            }
        }

        /**
         * Creates an {@link NotificationCompat.Action} from the {@code action} provided.
         *
         * <p>This is copied from {@link NotificationCompat.Action}'s fromAndroidAction method which
         * is not visible outside the package.
         */
        private static NotificationCompat.Action fromAndroidAction(
                @NonNull Notification.Action action) {
            return new NotificationCompat.Action(action.getIcon() == null ? 0 :
                    action.getIcon().getResId(),
                    action.title,
                    action.actionIntent);
        }

        private Api29Impl() {
        }
    }
}