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

Commit 43018ba2 authored by Jeff DeCew's avatar Jeff DeCew
Browse files

New Pipeline: Extend the NotifPipeline to include a render stage with callbacks

* RenderStageManager implements the pipeline behavior for handoff and listeners.
* ViewRenderer interface (implemented by ShadeViewManager) defines view update API.
* Implement a StackCoordinator (and similar logic in NotificationViewHierarchyManager) which sets notification stats on the NotifStackController rather than letting this be calculated by the view.  This is an example conversion to unidirectional data flow.

Bug: 204674942
Test: atest NotifPipelineTest PreparationCoordinatorTest NodeSpecBuilderTest RenderStageManagerTest StackCoordinatorTest GroupCountCoordinatorTest

Change-Id: I7b2296504202a9cbe87d5d3ead9bd3afffa00aab
parent c273e395
Loading
Loading
Loading
Loading
+58 −3
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.statusbar;

import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;

import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
@@ -39,6 +41,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -97,6 +101,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
    private final Context mContext;

    private NotificationPresenter mPresenter;
    private NotifStackController mStackController;
    private NotificationListContainer mListContainer;

    // Used to help track down re-entrant calls to our update methods, which will cause bugs.
@@ -147,8 +152,10 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
    }

    public void setUpWithPresenter(NotificationPresenter presenter,
            NotifStackController stackController,
            NotificationListContainer listContainer) {
        mPresenter = presenter;
        mStackController = stackController;
        mListContainer = listContainer;
        if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
            mDynamicPrivacyController.addListener(this);
@@ -328,12 +335,62 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
        mTmpChildOrderMap.clear();

        updateRowStatesInternal();
        updateNotifStats();

        mListContainer.onNotificationViewUpdateFinished();

        endUpdate();
    }

    /**
     * In the spirit of unidirectional data flow, calculate this information when the notification
     * views are updated, and set it once, speeding up lookups later.
     * This is analogous to logic in the
     * {@link com.android.systemui.statusbar.notification.collection.coordinator.StackCoordinator}
     */
    private void updateNotifStats() {
        boolean hasNonClearableAlertingNotifs = false;
        boolean hasClearableAlertingNotifs = false;
        boolean hasNonClearableSilentNotifs = false;
        boolean hasClearableSilentNotifs = false;
        final int childCount = mListContainer.getContainerChildCount();
        int visibleTopLevelEntries = 0;
        for (int i = 0; i < childCount; i++) {
            View child = mListContainer.getContainerChildAt(i);
            if (child == null || child.getVisibility() == View.GONE) {
                continue;
            }
            if (!(child instanceof ExpandableNotificationRow)) {
                continue;
            }
            final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
            boolean isSilent = row.getEntry().getBucket() == BUCKET_SILENT;
            // NOTE: NotificationEntry.isClearable() will internally check group children to ensure
            //  the group itself definitively clearable.
            boolean isClearable = row.getEntry().isClearable();
            if (isSilent) {
                if (isClearable) {
                    hasClearableSilentNotifs = true;
                } else {  // !isClearable
                    hasNonClearableSilentNotifs = true;
                }
            } else {  // !isSilent
                if (isClearable) {
                    hasClearableAlertingNotifs = true;
                } else {  // !isClearable
                    hasNonClearableAlertingNotifs = true;
                }
            }
        }
        mStackController.setNotifStats(new NotifStats(
                visibleTopLevelEntries /* numActiveNotifs */,
                hasNonClearableAlertingNotifs /* hasNonClearableAlertingNotifs */,
                hasClearableAlertingNotifs /* hasClearableAlertingNotifs */,
                hasNonClearableSilentNotifs /* hasNonClearableSilentNotifs */,
                hasClearableSilentNotifs /* hasClearableSilentNotifs */
        ));
    }

    /**
     * Should a notification entry from the active list be suppressed and not show?
     */
@@ -528,9 +585,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle

    @Override
    public void onDynamicPrivacyChanged() {
        if (mNotifPipelineFlags.isNewPipelineEnabled()) {
            throw new IllegalStateException("Old pipeline code running w/ new pipeline enabled");
        }
        mNotifPipelineFlags.assertLegacyPipelineEnabled();
        if (mPerformingUpdate) {
            Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
        }
+10 −7
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ class NotifPipelineFlags @Inject constructor(
    val featureFlags: FeatureFlags
) {
    fun checkLegacyPipelineEnabled(): Boolean {
        if (!featureFlags.isEnabled(Flags.NEW_NOTIFICATION_PIPELINE_RENDERING)) {
        if (!isNewPipelineEnabled()) {
            return true
        }
        Log.d("NotifPipeline", "Old pipeline code running w/ new pipeline enabled", Exception())
@@ -36,10 +36,13 @@ class NotifPipelineFlags @Inject constructor(
        return false
    }

    fun isNewPipelineEnabled(): Boolean = featureFlags.isEnabled(
            Flags.NEW_NOTIFICATION_PIPELINE_RENDERING)
    fun assertLegacyPipelineEnabled(): Nothing =
        error("Old pipeline code running w/ new pipeline enabled")

    fun isNewPipelineEnabled(): Boolean =
        featureFlags.isEnabled(Flags.NEW_NOTIFICATION_PIPELINE_RENDERING)

    fun isSmartspaceDedupingEnabled(): Boolean =
            featureFlags.isEnabled(Flags.SMARTSPACE)
                    && featureFlags.isEnabled(Flags.SMARTSPACE_DEDUPING)
            featureFlags.isEnabled(Flags.SMARTSPACE) &&
                    featureFlags.isEnabled(Flags.SMARTSPACE_DEDUPING)
}
 No newline at end of file
+0 −20
Original line number Diff line number Diff line
@@ -19,8 +19,6 @@ package com.android.systemui.statusbar.notification.collection;
import android.annotation.NonNull;
import android.annotation.Nullable;

import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -61,24 +59,6 @@ public class GroupEntry extends ListEntry {
        mSummary = summary;
    }

    /**
     * @see #getUntruncatedChildCount()
     */
    public void setUntruncatedChildCount(int childCount) {
        mUntruncatedChildCount = childCount;
    }

    /**
     * Get the untruncated number of children from the data model, including those that will not
     * have views bound. This includes children that {@link PreparationCoordinator} will filter out
     * entirely when they are beyond the last visible child.
     *
     * TODO: This should move to some shared class between the model and view hierarchy
     */
    public int getUntruncatedChildCount() {
        return mUntruncatedChildCount;
    }

    void clearChildren() {
        mChildren.clear();
    }
+1 −1
Original line number Diff line number Diff line
@@ -93,7 +93,7 @@ public class NotifInflaterImpl implements NotifInflater {
            public void onAsyncInflationFinished(NotificationEntry entry) {
                mNotifErrorManager.clearInflationError(entry);
                if (callback != null) {
                    callback.onInflationFinished(entry);
                    callback.onInflationFinished(entry, entry.getRowController());
                }
            }
        };
+31 −1
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.notification.collection

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener
@@ -31,6 +34,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.In
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
import com.android.systemui.statusbar.notification.collection.render.RenderStageManager
import javax.inject.Inject

/**
@@ -65,11 +69,15 @@ import javax.inject.Inject
 *  9. Finalize filters are fired on each notification ([.addFinalizeFilter])
 *  10. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
 *  11. The list is handed off to the view layer to be rendered
 *  12. OnAfterRenderListListeners are fired ([.addOnAfterRenderListListener])
 *  13. OnAfterRenderGroupListeners are fired ([.addOnAfterRenderGroupListener])
 *  13. OnAfterRenderEntryListeners are fired ([.addOnAfterRenderEntryListener])
 */
@SysUISingleton
class NotifPipeline @Inject constructor(
    private val mNotifCollection: NotifCollection,
    private val mShadeListBuilder: ShadeListBuilder
    private val mShadeListBuilder: ShadeListBuilder,
    private val mRenderStageManager: RenderStageManager
) : CommonNotifCollection {
    /**
     * Returns the list of all known notifications, i.e. the notifications that are currently posted
@@ -205,6 +213,28 @@ class NotifPipeline @Inject constructor(
        mShadeListBuilder.addPreRenderInvalidator(invalidator)
    }

    /**
     * Called at the end of the pipeline after the notif list has been handed off to the view layer.
     */
    fun addOnAfterRenderListListener(listener: OnAfterRenderListListener) {
        mRenderStageManager.addOnAfterRenderListListener(listener)
    }

    /**
     * Called at the end of the pipeline after a group has been handed off to the view layer.
     */
    fun addOnAfterRenderGroupListener(listener: OnAfterRenderGroupListener) {
        mRenderStageManager.addOnAfterRenderGroupListener(listener)
    }

    /**
     * Called at the end of the pipeline after an entry has been handed off to the view layer.
     * This will be called for every top level entry, every group summary, and every group child.
     */
    fun addOnAfterRenderEntryListener(listener: OnAfterRenderEntryListener) {
        mRenderStageManager.addOnAfterRenderEntryListener(listener)
    }

    /**
     * Get an object which can be used to update a notification (internally to the pipeline)
     * in response to a user action.
Loading