Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cef27526 authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge changes I64db4cc6,I1c6f86a8,I81ba7561,I25e623c2

* changes:
  Media player attempts to dismiss notifications when players removed.
  Add TODO about cleared media player notifications.
  Add some logging to help diagnose a crash with duplicate section headers
  NotificationMediaManager populates media player when using new pipeline.
parents 1b0fefb2 725d67d8
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -636,13 +636,13 @@ class MediaDataManager(
        Assert.isMainThread()
        val removed = mediaEntries.remove(key)
        if (useMediaResumption && removed?.resumeAction != null &&
                !isBlockedFromResume(removed?.packageName)) {
                !isBlockedFromResume(removed.packageName)) {
            Log.d(TAG, "Not removing $key because resumable")
            // Move to resume key (aka package name) if that key doesn't already exist.
            val resumeAction = getResumeMediaAction(removed.resumeAction!!)
            val updated = removed.copy(token = null, actions = listOf(resumeAction),
                    actionsToShowInCompact = listOf(0), active = false, resumption = true)
            val pkg = removed?.packageName
            val pkg = removed.packageName
            val migrate = mediaEntries.put(pkg, updated) == null
            // Notify listeners of "new" controls when migrating or removed and update when not
            if (migrate) {
+229 −89
Original line number Diff line number Diff line
@@ -40,6 +40,9 @@ import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
import android.view.View;
@@ -52,13 +55,18 @@ import com.android.systemui.Dumpable;
import com.android.systemui.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaData;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
@@ -77,6 +85,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import dagger.Lazy;
@@ -106,6 +115,9 @@ public class NotificationMediaManager implements Dumpable {

    private final NotificationEntryManager mEntryManager;
    private final MediaDataManager mMediaDataManager;
    private final NotifPipeline mNotifPipeline;
    private final NotifCollection mNotifCollection;
    private final boolean mUsingNotifPipeline;

    @Nullable
    private Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
@@ -189,6 +201,9 @@ public class NotificationMediaManager implements Dumpable {
            NotificationEntryManager notificationEntryManager,
            MediaArtworkProcessor mediaArtworkProcessor,
            KeyguardBypassController keyguardBypassController,
            NotifPipeline notifPipeline,
            NotifCollection notifCollection,
            FeatureFlags featureFlags,
            @Main DelayableExecutor mainExecutor,
            DeviceConfigProxy deviceConfig,
            MediaDataManager mediaDataManager) {
@@ -206,17 +221,87 @@ public class NotificationMediaManager implements Dumpable {
        mEntryManager = notificationEntryManager;
        mMainExecutor = mainExecutor;
        mMediaDataManager = mediaDataManager;
        mNotifPipeline = notifPipeline;
        mNotifCollection = notifCollection;

        notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
        if (!featureFlags.isNewNotifPipelineRenderingEnabled()) {
            setupNEM();
            mUsingNotifPipeline = false;
        } else {
            setupNotifPipeline();
            mUsingNotifPipeline = true;
        }

        mShowCompactMediaSeekbar = "true".equals(
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                    SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED));

        deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
                mContext.getMainExecutor(),
                mPropertiesChangedListener);
    }

    private void setupNotifPipeline() {
        mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
            @Override
            public void onEntryAdded(@NonNull NotificationEntry entry) {
                mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
            }

            @Override
            public void onEntryUpdated(NotificationEntry entry) {
                mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
            }

            @Override
            public void onEntryBind(NotificationEntry entry, StatusBarNotification sbn) {
                findAndUpdateMediaNotifications();
            }

            @Override
            public void onEntryRemoved(@NonNull NotificationEntry entry, int reason) {
                removeEntry(entry);
            }

            @Override
            public void onEntryCleanUp(@NonNull NotificationEntry entry) {
                removeEntry(entry);
            }
        });

        mMediaDataManager.addListener(new MediaDataManager.Listener() {
            @Override
            public void onMediaDataLoaded(@NonNull String key,
                    @Nullable String oldKey, @NonNull MediaData data) {
            }

            @Override
            public void onMediaDataRemoved(@NonNull String key) {
                mNotifPipeline.getAllNotifs()
                        .stream()
                        .filter(entry -> Objects.equals(entry.getKey(), key))
                        .findAny()
                        .ifPresent(entry -> {
                            // TODO(b/160713608): "removing" this notification won't happen and
                            //  won't send the 'deleteIntent' if the notification is ongoing.
                            mNotifCollection.dismissNotification(entry,
                                    getDismissedByUserStats(entry));
                        });
            }
        });
    }

    private void setupNEM() {
        mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {

            @Override
            public void onPendingEntryAdded(NotificationEntry entry) {
                mediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
                mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
            }

            @Override
            public void onPreEntryUpdated(NotificationEntry entry) {
                mediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
                mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
            }

            @Override
@@ -231,8 +316,8 @@ public class NotificationMediaManager implements Dumpable {

            @Override
            public void onEntryRemoved(
                    NotificationEntry entry,
                    NotificationVisibility visibility,
                    @NonNull NotificationEntry entry,
                    @Nullable NotificationVisibility visibility,
                    boolean removedByUser,
                    int reason) {
                removeEntry(entry);
@@ -242,20 +327,49 @@ public class NotificationMediaManager implements Dumpable {
        // Pending entries are never inflated, and will never generate a call to onEntryRemoved().
        // This can happen when notifications are added and canceled before inflation. Add this
        // separate listener for cleanup, since media inflation occurs onPendingEntryAdded().
        notificationEntryManager.addCollectionListener(new NotifCollectionListener() {
        mEntryManager.addCollectionListener(new NotifCollectionListener() {
            @Override
            public void onEntryCleanUp(@NonNull NotificationEntry entry) {
                removeEntry(entry);
            }
        });

        mShowCompactMediaSeekbar = "true".equals(
                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                    SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED));
        mMediaDataManager.addListener(new MediaDataManager.Listener() {
            @Override
            public void onMediaDataLoaded(@NonNull String key,
                    @Nullable String oldKey, @NonNull MediaData data) {
            }

        deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
                mContext.getMainExecutor(),
                mPropertiesChangedListener);
            @Override
            public void onMediaDataRemoved(@NonNull String key) {
                NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key);
                if (entry != null) {
                    // TODO(b/160713608): "removing" this notification won't happen and
                    //  won't send the 'deleteIntent' if the notification is ongoing.
                    mEntryManager.performRemoveNotification(entry.getSbn(),
                            getDismissedByUserStats(entry),
                            NotificationListenerService.REASON_CANCEL);
                }
            }
        });
    }

    private DismissedByUserStats getDismissedByUserStats(NotificationEntry entry) {
        final int activeNotificationsCount;
        if (mUsingNotifPipeline) {
            activeNotificationsCount = mNotifPipeline.getShadeListCount();
        } else {
            activeNotificationsCount = mEntryManager.getActiveNotificationsCount();
        }
        return new DismissedByUserStats(
                NotificationStats.DISMISSAL_SHADE, // Add DISMISSAL_MEDIA?
                NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
                NotificationVisibility.obtain(
                        entry.getKey(),
                        entry.getRanking().getRank(),
                        activeNotificationsCount,
                        /* visible= */ true,
                        NotificationLogger.getNotificationLocation(entry)));
    }

    private void removeEntry(NotificationEntry entry) {
@@ -299,6 +413,15 @@ public class NotificationMediaManager implements Dumpable {
        if (mMediaNotificationKey == null) {
            return null;
        }
        if (mUsingNotifPipeline) {
            // TODO(b/169655596): Either add O(1) lookup, or cache this icon?
            return mNotifPipeline.getAllNotifs().stream()
                .filter(entry -> Objects.equals(entry.getKey(), mMediaNotificationKey))
                .findAny()
                .map(entry -> entry.getIcons().getShelfIcon())
                .map(StatusBarIconView::getSourceIcon)
                .orElse(null);
        } else {
            synchronized (mEntryManager) {
                NotificationEntry entry = mEntryManager
                    .getActiveNotificationUnfiltered(mMediaNotificationKey);
@@ -309,6 +432,7 @@ public class NotificationMediaManager implements Dumpable {
                return entry.getIcons().getShelfIcon().getSourceIcon();
            }
        }
    }

    public void addCallback(MediaListener callback) {
        mMediaListeners.add(callback);
@@ -321,11 +445,33 @@ public class NotificationMediaManager implements Dumpable {
    }

    public void findAndUpdateMediaNotifications() {
        boolean metaDataChanged = false;

        boolean metaDataChanged;
        if (mUsingNotifPipeline) {
            // TODO(b/169655907): get the semi-filtered notifications for current user
            Collection<NotificationEntry> allNotifications = mNotifPipeline.getAllNotifs();
            metaDataChanged = findPlayingMediaNotification(allNotifications);
        } else {
            synchronized (mEntryManager) {
                Collection<NotificationEntry> allNotifications = mEntryManager.getAllNotifs();
                metaDataChanged = findPlayingMediaNotification(allNotifications);
            }

            if (metaDataChanged) {
                mEntryManager.updateNotifications("NotificationMediaManager - metaDataChanged");
            }

        }
        dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
    }

    /**
     * Find a notification and media controller associated with the playing media session, and
     * update this manager's internal state.
     * @return whether the current MediaMetadata changed (and needs to be announced to listeners).
     */
    private boolean findPlayingMediaNotification(
            @NonNull Collection<NotificationEntry> allNotifications) {
        boolean metaDataChanged = false;
        // Promote the media notification with a controller in 'playing' state, if any.
        NotificationEntry mediaNotification = null;
        MediaController controller = null;
@@ -336,8 +482,8 @@ public class NotificationMediaManager implements Dumpable {
                                Notification.EXTRA_MEDIA_SESSION);
                if (token != null) {
                    MediaController aController = new MediaController(mContext, token);
                        if (PlaybackState.STATE_PLAYING ==
                                getMediaControllerPlaybackState(aController)) {
                    if (PlaybackState.STATE_PLAYING
                            == getMediaControllerPlaybackState(aController)) {
                        if (DEBUG_MEDIA) {
                            Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
                                    + entry.getSbn().getKey());
@@ -355,11 +501,10 @@ public class NotificationMediaManager implements Dumpable {
            // notifications.

            if (mMediaSessionManager != null) {
                    // TODO: Should this really be for all users?
                    final List<MediaController> sessions
                            = mMediaSessionManager.getActiveSessionsForUser(
                            null,
                            UserHandle.USER_ALL);
                // TODO: Should this really be for all users? It appears that inactive users
                //  can't have active sessions, which would mean it is fine.
                final List<MediaController> sessions =
                        mMediaSessionManager.getActiveSessionsForUser(null, UserHandle.USER_ALL);

                for (MediaController aController : sessions) {
                    // now to see if we have one like this
@@ -402,13 +547,8 @@ public class NotificationMediaManager implements Dumpable {
                        + mMediaNotificationKey);
            }
        }
        }

        if (metaDataChanged) {
            mEntryManager.updateNotifications("NotificationMediaManager - metaDataChanged");
        }

        dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
        return metaDataChanged;
    }

    public void clearCurrentMediaNotification() {
@@ -428,7 +568,7 @@ public class NotificationMediaManager implements Dumpable {
    }

    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
        pw.print("    mMediaSessionManager=");
        pw.println(mMediaSessionManager);
        pw.print("    mMediaNotificationKey=");
+9 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.systemui.media.MediaDataManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.MediaArtworkProcessor;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationListener;
@@ -44,6 +45,8 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -111,6 +114,9 @@ public interface StatusBarDependenciesModule {
            NotificationEntryManager notificationEntryManager,
            MediaArtworkProcessor mediaArtworkProcessor,
            KeyguardBypassController keyguardBypassController,
            NotifPipeline notifPipeline,
            NotifCollection notifCollection,
            FeatureFlags featureFlags,
            @Main DelayableExecutor mainExecutor,
            DeviceConfigProxy deviceConfigProxy,
            MediaDataManager mediaDataManager) {
@@ -121,6 +127,9 @@ public interface StatusBarDependenciesModule {
                notificationEntryManager,
                mediaArtworkProcessor,
                keyguardBypassController,
                notifPipeline,
                notifCollection,
                featureFlags,
                mainExecutor,
                deviceConfigProxy,
                mediaDataManager);
+1 −0
Original line number Diff line number Diff line
@@ -291,6 +291,7 @@ public class NotifCollection implements Dumpable {
        mLogger.logDismissAll(userId);

        try {
            // TODO(b/169585328): Do not clear media player notifications
            mStatusBarService.onClearAllNotifications(userId);
        } catch (RemoteException e) {
            // system process is dead if we're here.
+9 −2
Original line number Diff line number Diff line
@@ -172,14 +172,19 @@ class ShadeViewDiffer(
    private fun treeToMap(tree: NodeSpec): Map<NodeController, NodeSpec> {
        val map = mutableMapOf<NodeController, NodeSpec>()

        try {
            registerNodes(tree, map)
        } catch (ex: DuplicateNodeException) {
            logger.logDuplicateNodeInTree(tree, ex)
            throw ex
        }

        return map
    }

    private fun registerNodes(node: NodeSpec, map: MutableMap<NodeController, NodeSpec>) {
        if (map.containsKey(node.controller)) {
            throw RuntimeException("Node ${node.controller.nodeLabel} appears more than once")
            throw DuplicateNodeException("Node ${node.controller.nodeLabel} appears more than once")
        }
        map[node.controller] = node

@@ -191,6 +196,8 @@ class ShadeViewDiffer(
    }
}

private class DuplicateNodeException(message: String) : RuntimeException(message)

private class ShadeNode(
    val controller: NodeController
) {
Loading