chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfo.java

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.browser_ui.media;

import android.content.Intent;
import android.graphics.Bitmap;
import android.text.TextUtils;

import androidx.annotation.Nullable;

import org.chromium.services.media_session.MediaMetadata;
import org.chromium.services.media_session.MediaPosition;

import java.util.Set;

/** Exposes information about the current media notification to the external clients. */
public class MediaNotificationInfo {
    // Bits defining various user actions supported by the media notification.

    /** If set, play/pause controls are shown and handled via notification UI and MediaSession. */
    public static final int ACTION_PLAY_PAUSE = 1 << 0;

    /** If set, a stop button is shown and handled via the notification UI. */
    public static final int ACTION_STOP = 1 << 1;

    /**
     * If set, a user can swipe the notification away when it's paused.
     * If notification swipe is not supported, it will behave like {@link #ACTION_STOP}.
     */
    public static final int ACTION_SWIPEAWAY = 1 << 2;

    /** A value that represents an invalid ID. */
    public static final int INVALID_ID = -1;

    /** Use this class to construct an instance of {@link MediaNotificationInfo}. */
    public static final class Builder {
        private MediaMetadata mMetadata;
        private boolean mIsPaused;
        private String mOrigin = "";
        private int mInstanceId = INVALID_ID;
        private boolean mIsPrivate = true;
        private int mNotificationSmallIcon;
        private Bitmap mNotificationLargeIcon;
        private int mDefaultNotificationLargeIcon;
        private Bitmap mMediaSessionImage;
        private int mActions = ACTION_PLAY_PAUSE | ACTION_SWIPEAWAY | ACTION_STOP;
        private int mId = INVALID_ID;
        private Intent mContentIntent;
        private MediaNotificationListener mListener;
        private Set<Integer> mMediaSessionActions;
        private @Nullable MediaPosition mMediaPosition;

        /** Initializes the builder with the default values. */
        public Builder() {}

        public MediaNotificationInfo build() {
            assert mMetadata != null;
            assert mOrigin != null;
            assert mListener != null;

            return new MediaNotificationInfo(
                    mMetadata,
                    mIsPaused,
                    mOrigin,
                    mInstanceId,
                    mIsPrivate,
                    mNotificationSmallIcon,
                    mNotificationLargeIcon,
                    mDefaultNotificationLargeIcon,
                    mMediaSessionImage,
                    mActions,
                    mId,
                    mContentIntent,
                    mListener,
                    mMediaSessionActions,
                    mMediaPosition);
        }

        public Builder setMetadata(MediaMetadata metadata) {
            mMetadata = metadata;
            return this;
        }

        public Builder setPaused(boolean isPaused) {
            mIsPaused = isPaused;
            return this;
        }

        public Builder setOrigin(String origin) {
            mOrigin = origin;
            return this;
        }

        public Builder setInstanceId(int instanceId) {
            mInstanceId = instanceId;
            return this;
        }

        public Builder setPrivate(boolean isPrivate) {
            mIsPrivate = isPrivate;
            return this;
        }

        public Builder setNotificationSmallIcon(int icon) {
            mNotificationSmallIcon = icon;
            return this;
        }

        public Builder setNotificationLargeIcon(Bitmap icon) {
            mNotificationLargeIcon = icon;
            return this;
        }

        public Builder setDefaultNotificationLargeIcon(int icon) {
            mDefaultNotificationLargeIcon = icon;
            return this;
        }

        public Builder setMediaSessionImage(Bitmap image) {
            mMediaSessionImage = image;
            return this;
        }

        public Builder setActions(int actions) {
            mActions = actions;
            return this;
        }

        public Builder setId(int id) {
            mId = id;
            return this;
        }

        public Builder setContentIntent(Intent intent) {
            mContentIntent = intent;
            return this;
        }

        public Builder setListener(MediaNotificationListener listener) {
            mListener = listener;
            return this;
        }

        public Builder setMediaSessionActions(Set<Integer> actions) {
            mMediaSessionActions = actions;
            return this;
        }

        public Builder setMediaPosition(@Nullable MediaPosition position) {
            mMediaPosition = position;
            return this;
        }
    }

    /** The bitset defining user actions handled by the notification. */
    private final int mActions;

    /** The metadata associated with the media. */
    public final MediaMetadata metadata;

    /** The current state of the media, paused or not. */
    public final boolean isPaused;

    /** The origin of the tab containing the media. */
    public final String origin;

    /**
     * An identifier that helps distinguish different instances of the same type of media
     * notification. The {@link id} is shared by different MediaNotificationInfo instances for the
     * same media type, but this identifier provides an extra layer of differentiation. In Chrome,
     * for example, this corresponds to the source tab.
     */
    public final int instanceId;

    /** Whether the media notification should be considered as private. */
    public final boolean isPrivate;

    /** The id of the notification small icon from R.drawable. */
    public final int notificationSmallIcon;

    /** The Bitmap resource used as the notification large icon. */
    public final Bitmap notificationLargeIcon;

    /** The id of the default notification large icon from R.drawable. */
    public final int defaultNotificationLargeIcon;

    /**
     * The Bitmap resource used for Android MediaSession image, which will be used on lock screen
     * and wearable devices.
     */
    public final Bitmap mediaSessionImage;

    /** The id to use for the Android Notification. */
    public final int id;

    /** The intent to send when the notification is selected. */
    public final Intent contentIntent;

    /** The listener for the control events. */
    public final MediaNotificationListener listener;

    /**
     * The actions enabled in MediaSession. If null, the notification is not associated with a web
     * Media Session.
     */
    public final @Nullable Set<Integer> mediaSessionActions;

    /** The current position of the media session. */
    public final @Nullable MediaPosition mediaPosition;

    /** @return if play/pause actions are supported by this notification. */
    public boolean supportsPlayPause() {
        return (mActions & ACTION_PLAY_PAUSE) != 0;
    }

    /** @return if stop action is supported by this notification. */
    public boolean supportsStop() {
        return (mActions & ACTION_STOP) != 0;
    }

    /** @return if notification should be dismissable by swiping it away when paused. */
    public boolean supportsSwipeAway() {
        return (mActions & ACTION_SWIPEAWAY) != 0;
    }

    /**
     * Create a new MediaNotificationInfo.
     * @param metadata The metadata associated with the media.
     * @param isPaused The current state of the media, paused or not.
     * @param origin The origin of the tab containing the media.
     * @param instanceId The id of the tab containing the media.
     * @param isPrivate Whether the media notification should be considered as private.
     * @param notificationSmallIcon The small icon used in the notification.
     * @param notificationLargeIcon The large icon used in the notification.
     * @param defaultNotificationLargeIcon The fallback large icon when |notificationLargeIcon| is
     *        improper to use.
     * @param mediaSessionImage The artwork image to be used in Android MediaSession.
     * @param actions The actions supported in this notification.
     * @param id The id of this notification, which is used for distinguishing media playback, cast
     *        and media remote.
     * @param contentIntent The intent to send when the notification is selected.
     * @param listener The listener for the control events.
     * @param mediaSessionActions The actions supported by the page.
     * @param mediaPosition The current position of the media.
     */
    private MediaNotificationInfo(
            MediaMetadata metadata,
            boolean isPaused,
            String origin,
            int instanceId,
            boolean isPrivate,
            int notificationSmallIcon,
            Bitmap notificationLargeIcon,
            int defaultNotificationLargeIcon,
            Bitmap mediaSessionImage,
            int actions,
            int id,
            Intent contentIntent,
            MediaNotificationListener listener,
            Set<Integer> mediaSessionActions,
            @Nullable MediaPosition mediaPosition) {
        this.metadata = metadata;
        this.isPaused = isPaused;
        this.origin = origin;
        assert instanceId != INVALID_ID;
        this.instanceId = instanceId;
        this.isPrivate = isPrivate;
        this.notificationSmallIcon = notificationSmallIcon;
        this.notificationLargeIcon = notificationLargeIcon;
        this.defaultNotificationLargeIcon = defaultNotificationLargeIcon;
        this.mediaSessionImage = mediaSessionImage;
        this.mActions = actions;
        assert id != INVALID_ID;
        this.id = id;
        this.contentIntent = contentIntent;
        this.listener = listener;
        this.mediaSessionActions = mediaSessionActions;
        this.mediaPosition = mediaPosition;
    }

    @Override
    @SuppressWarnings("ReferenceEquality")
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof MediaNotificationInfo)) return false;

        MediaNotificationInfo other = (MediaNotificationInfo) obj;
        return isPaused == other.isPaused
                && isPrivate == other.isPrivate
                && instanceId == other.instanceId
                && notificationSmallIcon == other.notificationSmallIcon
                && (notificationLargeIcon == other.notificationLargeIcon
                        || (notificationLargeIcon != null
                                && notificationLargeIcon.sameAs(other.notificationLargeIcon)))
                && defaultNotificationLargeIcon == other.defaultNotificationLargeIcon
                && mediaSessionImage == other.mediaSessionImage
                && mActions == other.mActions
                && id == other.id
                && (metadata == other.metadata
                        || (metadata != null && metadata.equals(other.metadata)))
                && TextUtils.equals(origin, other.origin)
                && (contentIntent == other.contentIntent
                        || (contentIntent != null && contentIntent.equals(other.contentIntent)))
                && (listener == other.listener
                        || (listener != null && listener.equals(other.listener)))
                && (mediaSessionActions == other.mediaSessionActions
                        || (mediaSessionActions != null
                                && mediaSessionActions.equals(other.mediaSessionActions)))
                && mediaPosition == other.mediaPosition;
    }

    @Override
    public int hashCode() {
        int result = isPaused ? 1 : 0;
        result = 31 * result + (isPrivate ? 1 : 0);
        result = 31 * result + (metadata == null ? 0 : metadata.hashCode());
        result = 31 * result + (origin == null ? 0 : origin.hashCode());
        result = 31 * result + (contentIntent == null ? 0 : contentIntent.hashCode());
        result = 31 * result + instanceId;
        result = 31 * result + notificationSmallIcon;
        result =
                31 * result
                        + (notificationLargeIcon == null ? 0 : notificationLargeIcon.hashCode());
        result = 31 * result + defaultNotificationLargeIcon;
        result = 31 * result + (mediaSessionImage == null ? 0 : mediaSessionImage.hashCode());
        result = 31 * result + mActions;
        result = 31 * result + id;
        result = 31 * result + listener.hashCode();
        result = 31 * result + (mediaSessionActions == null ? 0 : mediaSessionActions.hashCode());
        result = 31 * result + (mediaPosition == null ? 0 : mediaPosition.hashCode());
        return result;
    }
}