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

Commit 936849dd authored by Ned Burns's avatar Ned Burns Committed by Android (Google) Code Review
Browse files

Merge changes If9b31b3b,Iea400614,I9567aaf5,I6696029f,I3d4f434e, ...

* changes:
  Don't show new groups until their children inflate
  Cleanup PreparationCoordinator a bit
  Add onCleanup() method to Pluggables
  Introduce GroupEntryBuilder
  NotificationEntryBuilder can modify existing entries
  Move creationTime into ListEntry (from NotifEntry)
parents b8f900c4 c3969582
Loading
Loading
Loading
Loading
+4 −7
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.collection;
import android.annotation.NonNull;
import android.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;

import java.util.ArrayList;
@@ -39,9 +38,8 @@ public class GroupEntry extends ListEntry {
            Collections.unmodifiableList(mChildren);
    private int mUntruncatedChildCount;

    @VisibleForTesting
    public GroupEntry(String key) {
        super(key);
    GroupEntry(String key, long creationTime) {
        super(key, creationTime);
    }

    @Override
@@ -59,8 +57,7 @@ public class GroupEntry extends ListEntry {
        return mUnmodifiableChildren;
    }

    @VisibleForTesting
    public void setSummary(@Nullable NotificationEntry summary) {
    void setSummary(@Nullable NotificationEntry summary) {
        mSummary = summary;
    }

@@ -98,6 +95,6 @@ public class GroupEntry extends ListEntry {
        return mChildren;
    }

    public static final GroupEntry ROOT_ENTRY = new GroupEntry("<root>");
    public static final GroupEntry ROOT_ENTRY = new GroupEntry("<root>", 0);

}
+26 −1
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.notification.collection;


import android.annotation.UptimeMillisLong;

import androidx.annotation.Nullable;

import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
@@ -27,20 +29,35 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
 */
public abstract class ListEntry {
    private final String mKey;
    private final long mCreationTime;

    int mFirstAddedIteration = -1;

    private final ListAttachState mPreviousAttachState = ListAttachState.create();
    private final ListAttachState mAttachState = ListAttachState.create();

    ListEntry(String key) {
    ListEntry(String key, long creationTime) {
        mKey = key;
        mCreationTime = creationTime;
    }

    public String getKey() {
        return mKey;
    }

    /**
     * The SystemClock.uptimeMillis() when this object was created. In general, this means the
     * moment when NotificationManager notifies our listener about the existence of this entry.
     *
     * This value will not change if the notification is updated, although it will change if the
     * notification is removed and then re-posted. It is also wholly independent from
     * Notification#when.
     */
    @UptimeMillisLong
    public long getCreationTime() {
        return mCreationTime;
    }

    /**
     * Should return the "representative entry" for this ListEntry. For NotificationEntries, its
     * the entry itself. For groups, it should be the summary (but if a summary doesn't exist,
@@ -78,6 +95,14 @@ public abstract class ListEntry {
        return mPreviousAttachState;
    }

    /**
     * True if this entry has been attached to the shade at least once in its lifetime (it may not
     * currently be attached).
     */
    public boolean hasBeenAttachedBefore() {
        return mFirstAddedIteration != -1;
    }

    /**
     * Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
     * fresh attach state (all entries will be null/default-initialized).
+1 −19
Original line number Diff line number Diff line
@@ -35,7 +35,6 @@ import static com.android.systemui.statusbar.notification.stack.NotificationSect

import static java.util.Objects.requireNonNull;

import android.annotation.CurrentTimeMillisLong;
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
@@ -96,7 +95,6 @@ public final class NotificationEntry extends ListEntry {
    private final String mKey;
    private StatusBarNotification mSbn;
    private Ranking mRanking;
    private long mCreationTime;

    /*
     * Bookkeeping members
@@ -198,11 +196,10 @@ public final class NotificationEntry extends ListEntry {
            boolean allowFgsDismissal,
            long creationTime
    ) {
        super(requireNonNull(requireNonNull(sbn).getKey()));
        super(requireNonNull(requireNonNull(sbn).getKey()), creationTime);

        requireNonNull(ranking);

        mCreationTime = creationTime;
        mKey = sbn.getKey();
        setSbn(sbn);
        setRanking(ranking);
@@ -254,21 +251,6 @@ public final class NotificationEntry extends ListEntry {
        return mRanking;
    }

    /**
     * A timestamp of SystemClock.uptimeMillis() of when this entry was first created, regardless
     * of any changes to the data presented. It is set once on creation and will never change, and
     * allows us to know exactly how long this notification has been alive for in our listener
     * service. It is entirely unrelated to the information inside of the notification.
     *
     * This is different to Notification#when because it persists throughout updates, whereas
     * system server treats every single call to notify() as a new notification and we handle
     * updates to NotificationEntry locally.
     */
    @CurrentTimeMillisLong
    public long getCreationTime() {
        return mCreationTime;
    }

    /**
     * Should only be called by NotificationEntryManager and friends.
     * TODO: Make this package-private
+17 −1
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.util.Assert;
import com.android.systemui.util.time.SystemClock;
@@ -321,6 +322,7 @@ public class ShadeListBuilder implements Dumpable {
        mPipelineState.incrementTo(STATE_FINALIZING);
        logChanges();
        freeEmptyGroups();
        cleanupPluggables();

        // Step 8: Dispatch the new list, first to any listeners and then to the view layer
        dispatchOnBeforeRenderList(mReadOnlyNotifList);
@@ -421,7 +423,7 @@ public class ShadeListBuilder implements Dumpable {

                GroupEntry group = mGroups.get(topLevelKey);
                if (group == null) {
                    group = new GroupEntry(topLevelKey);
                    group = new GroupEntry(topLevelKey, mSystemClock.uptimeMillis());
                    group.mFirstAddedIteration = mIterationCount;
                    mGroups.put(topLevelKey, group);
                }
@@ -673,6 +675,20 @@ public class ShadeListBuilder implements Dumpable {
        }
    }

    private void cleanupPluggables() {
        callOnCleanup(mNotifPreGroupFilters);
        callOnCleanup(mNotifPromoters);
        callOnCleanup(mNotifFinalizeFilters);
        callOnCleanup(mNotifComparators);
        callOnCleanup(mNotifSections);
    }

    private void callOnCleanup(List<? extends Pluggable<?>> pluggables) {
        for (int i = 0; i < pluggables.size(); i++) {
            pluggables.get(i).onCleanup();
        }
    }

    private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> {

        int cmp = Integer.compare(o1.getSection(), o2.getSection());
+70 −15
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator;

import static com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;

import static java.util.Objects.requireNonNull;

import android.annotation.IntDef;
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
@@ -28,7 +30,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotifViewBarn;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -38,12 +39,12 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager.NotifInflationErrorListener;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.inject.Inject;
@@ -81,35 +82,48 @@ public class PreparationCoordinator implements Coordinator {
     */
    private final int mChildBindCutoff;

    /** How long we can delay a group while waiting for all children to inflate */
    private final long mMaxGroupInflationDelay;

    @Inject
    public PreparationCoordinator(
            PreparationCoordinatorLogger logger,
            NotifInflaterImpl notifInflater,
            NotifInflater notifInflater,
            NotifInflationErrorManager errorManager,
            NotifViewBarn viewBarn,
            IStatusBarService service) {
        this(logger, notifInflater, errorManager, viewBarn, service, CHILD_BIND_CUTOFF);
        this(
                logger,
                notifInflater,
                errorManager,
                viewBarn,
                service,
                CHILD_BIND_CUTOFF,
                MAX_GROUP_INFLATION_DELAY);
    }

    @VisibleForTesting
    PreparationCoordinator(
            PreparationCoordinatorLogger logger,
            NotifInflaterImpl notifInflater,
            NotifInflater notifInflater,
            NotifInflationErrorManager errorManager,
            NotifViewBarn viewBarn,
            IStatusBarService service,
            int childBindCutoff) {
            int childBindCutoff,
            long maxGroupInflationDelay) {
        mLogger = logger;
        mNotifInflater = notifInflater;
        mNotifErrorManager = errorManager;
        mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);
        mViewBarn = viewBarn;
        mStatusBarService = service;
        mChildBindCutoff = childBindCutoff;
        mMaxGroupInflationDelay = maxGroupInflationDelay;
    }

    @Override
    public void attach(NotifPipeline pipeline) {
        mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);

        pipeline.addCollectionListener(mNotifCollectionListener);
        // Inflate after grouping/sorting since that affects what views to inflate.
        pipeline.addOnBeforeFinalizeFilterListener(mOnBeforeFinalizeFilterListener);
@@ -127,7 +141,6 @@ public class PreparationCoordinator implements Coordinator {
        @Override
        public void onEntryUpdated(NotificationEntry entry) {
            abortInflation(entry, "entryUpdated");
            mInflatingNotifs.remove(entry);
            @InflationState int state = getInflationState(entry);
            if (state == STATE_INFLATED) {
                mInflationStates.put(entry, STATE_INFLATED_INVALID);
@@ -145,7 +158,6 @@ public class PreparationCoordinator implements Coordinator {
        @Override
        public void onEntryCleanUp(NotificationEntry entry) {
            mInflationStates.remove(entry);
            mInflatingNotifs.remove(entry);
            mViewBarn.removeViewForEntry(entry);
        }
    };
@@ -165,17 +177,32 @@ public class PreparationCoordinator implements Coordinator {
    };

    private final NotifFilter mNotifInflatingFilter = new NotifFilter(TAG + "Inflating") {
        private final Map<GroupEntry, Boolean> mIsDelayedGroupCache = new ArrayMap<>();

        /**
         * Filters out notifications that aren't inflated
         * Filters out notifications that either (a) aren't inflated or (b) are part of a group
         * that isn't completely inflated yet
         */
        @Override
        public boolean shouldFilterOut(NotificationEntry entry, long now) {
            return !isInflated(entry);
            final GroupEntry parent = requireNonNull(entry.getParent());
            Boolean isMemberOfDelayedGroup = mIsDelayedGroupCache.get(parent);
            if (isMemberOfDelayedGroup == null) {
                isMemberOfDelayedGroup = shouldWaitForGroupToInflate(parent, now);
                mIsDelayedGroupCache.put(parent, isMemberOfDelayedGroup);
            }

            return !isInflated(entry) || isMemberOfDelayedGroup;
        }

        @Override
        public void onCleanup() {
            mIsDelayedGroupCache.clear();
        }
    };

    private final NotifInflationErrorManager.NotifInflationErrorListener mInflationErrorListener =
            new NotifInflationErrorManager.NotifInflationErrorListener() {
    private final NotifInflationErrorListener mInflationErrorListener =
            new NotifInflationErrorListener() {
        @Override
        public void onNotifInflationError(NotificationEntry entry, Exception e) {
            mViewBarn.removeViewForEntry(entry);
@@ -191,8 +218,9 @@ public class PreparationCoordinator implements Coordinator {
                        sbn.getUid(),
                        sbn.getInitialPid(),
                        e.getMessage(),
                        sbn.getUserId());
                        sbn.getUser().getIdentifier());
            } catch (RemoteException ex) {
                // System server is dead, nothing to do about that
            }
            mNotifInflationErrorFilter.invalidateList();
        }
@@ -297,11 +325,36 @@ public class PreparationCoordinator implements Coordinator {

    private @InflationState int getInflationState(NotificationEntry entry) {
        Integer stateObj = mInflationStates.get(entry);
        Objects.requireNonNull(stateObj,
        requireNonNull(stateObj,
                "Asking state of a notification preparation coordinator doesn't know about");
        return stateObj;
    }

    private boolean shouldWaitForGroupToInflate(GroupEntry group, long now) {
        if (group == GroupEntry.ROOT_ENTRY || group.hasBeenAttachedBefore()) {
            return false;
        }
        if (isBeyondGroupInitializationWindow(group, now)) {
            mLogger.logGroupInflationTookTooLong(group.getKey());
            return false;
        }
        if (mInflatingNotifs.contains(group.getSummary())) {
            mLogger.logDelayingGroupRelease(group.getKey(), group.getSummary().getKey());
            return true;
        }
        for (NotificationEntry child : group.getChildren()) {
            if (mInflatingNotifs.contains(child) && !child.hasBeenAttachedBefore()) {
                mLogger.logDelayingGroupRelease(group.getKey(), child.getKey());
                return true;
            }
        }
        return false;
    }

    private boolean isBeyondGroupInitializationWindow(GroupEntry entry, long now) {
        return now - entry.getCreationTime() > mMaxGroupInflationDelay;
    }

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"STATE_"},
            value = {STATE_UNINFLATED, STATE_INFLATED_INVALID, STATE_INFLATED, STATE_ERROR})
@@ -328,6 +381,8 @@ public class PreparationCoordinator implements Coordinator {
     */
    private static final int EXTRA_VIEW_BUFFER_COUNT = 1;

    private static final long MAX_GROUP_INFLATION_DELAY = 500;

    private static final int CHILD_BIND_CUTOFF =
            NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED + EXTRA_VIEW_BUFFER_COUNT;
}
Loading