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

Commit 273bb0df authored by Kevin Han's avatar Kevin Han
Browse files

Refactor heads up view binding

As part of deprecating row.freeContentViewWhenSafe, this CL
replaces/removes all remaining calls. In addition, we create a
shared class called HeadsUpViewBinder that binds/unbinds the heads
up view and is used by both the old and new pipeline.

In the new pipeline, HeadsUpCoordinator uses this class to
bind/unbind heads up views in response to notification collection
events and heads up ending. This also fully puts heads up in the control
of the HeadsUpCoordinator instead of partially intruding on
PreparationCoordinator

In the old pipeline, HeadsUpViewController effectively acts as the
HeadsUpCoordinator equivalent in response to NotificationEntryManager
events. Notably, it takes control of calling heads up view inflation
instead of NotificationRowBinderImpl which also decouples the
row binder from as much business logic.

Bug: 112656837
Test: atest SystemUITests
Test: heads up notification smoke test
Test: heap dump heads up notification after and see view is freed
Change-Id: Ifb3a9ff753bc0670601763cb18d6ca54fbff52c5
parent 5300f144
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -67,7 +67,6 @@ import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;

import java.util.ArrayList;
@@ -583,10 +582,6 @@ public final class NotificationEntry extends ListEntry {
        if (row != null) row.resetUserExpansion();
    }

    public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
        if (row != null) row.freeContentViewWhenSafe(inflationFlag);
    }

    public boolean rowExists() {
        return row != null;
    }
+60 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;

import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
import static com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager.alertAgain;

import android.annotation.Nullable;

@@ -28,6 +29,8 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.statusbar.notification.headsup.HeadsUpViewBinder;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;

@@ -55,6 +58,8 @@ public class HeadsUpCoordinator implements Coordinator {
    private static final String TAG = "HeadsUpCoordinator";

    private final HeadsUpManager mHeadsUpManager;
    private final HeadsUpViewBinder mHeadsUpViewBinder;
    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
    private final NotificationRemoteInputManager mRemoteInputManager;

    // tracks the current HeadUpNotification reported by HeadsUpManager
@@ -66,8 +71,12 @@ public class HeadsUpCoordinator implements Coordinator {
    @Inject
    public HeadsUpCoordinator(
            HeadsUpManager headsUpManager,
            HeadsUpViewBinder headsUpViewBinder,
            NotificationInterruptStateProvider notificationInterruptStateProvider,
            NotificationRemoteInputManager remoteInputManager) {
        mHeadsUpManager = headsUpManager;
        mHeadsUpViewBinder = headsUpViewBinder;
        mNotificationInterruptStateProvider = notificationInterruptStateProvider;
        mRemoteInputManager = remoteInputManager;
    }

@@ -84,7 +93,50 @@ public class HeadsUpCoordinator implements Coordinator {
        return mNotifSection;
    }

    private void onHeadsUpViewBound(NotificationEntry entry) {
        mHeadsUpManager.showNotification(entry);
    }

    private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
        /**
         * Notification was just added and if it should heads up, bind the view and then show it.
         */
        @Override
        public void onEntryAdded(NotificationEntry entry) {
            if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
                mHeadsUpViewBinder.bindHeadsUpView(
                        entry,
                        HeadsUpCoordinator.this::onHeadsUpViewBound);
            }
        }

        /**
         * Notification could've updated to be heads up or not heads up. Even if it did update to
         * heads up, if the notification specified that it only wants to alert once, don't heads
         * up again.
         */
        @Override
        public void onEntryUpdated(NotificationEntry entry) {
            boolean hunAgain = alertAgain(entry, entry.getSbn().getNotification());
            // includes check for whether this notification should be filtered:
            boolean shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry);
            final boolean wasHeadsUp = mHeadsUpManager.isAlerting(entry.getKey());
            if (wasHeadsUp) {
                if (shouldHeadsUp) {
                    mHeadsUpManager.updateNotification(entry.getKey(), hunAgain);
                } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.getKey())) {
                    // We don't want this to be interrupting anymore, let's remove it
                    mHeadsUpManager.removeNotification(
                            entry.getKey(), false /* removeImmediately */);
                }
            } else if (shouldHeadsUp && hunAgain) {
                // This notification was updated to be heads up, show it!
                mHeadsUpViewBinder.bindHeadsUpView(
                        entry,
                        HeadsUpCoordinator.this::onHeadsUpViewBound);
            }
        }

        /**
         * Stop alerting HUNs that are removed from the notification collection
         */
@@ -98,6 +150,11 @@ public class HeadsUpCoordinator implements Coordinator {
                mHeadsUpManager.removeNotification(entry.getKey(), removeImmediatelyForRemoteInput);
            }
        }

        @Override
        public void onEntryCleanUp(NotificationEntry entry) {
            mHeadsUpViewBinder.abortBindCallback(entry);
        }
    };

    private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
@@ -153,6 +210,9 @@ public class HeadsUpCoordinator implements Coordinator {
                mNotifPromoter.invalidateList();
                mNotifSection.invalidateList();
            }
            if (!isHeadsUp) {
                mHeadsUpViewBinder.unbindHeadsUpView(entry);
            }
        }
    };

+1 −14
Original line number Diff line number Diff line
@@ -33,9 +33,7 @@ import com.android.systemui.statusbar.notification.collection.inflation.NotifInf
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
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.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -63,8 +61,6 @@ public class PreparationCoordinator implements Coordinator {
    private final NotifViewBarn mViewBarn;
    private final Map<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
    private final IStatusBarService mStatusBarService;
    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
    private final HeadsUpManager mHeadsUpManager;

    @Inject
    public PreparationCoordinator(
@@ -72,9 +68,7 @@ public class PreparationCoordinator implements Coordinator {
            NotifInflaterImpl notifInflater,
            NotifInflationErrorManager errorManager,
            NotifViewBarn viewBarn,
            IStatusBarService service,
            NotificationInterruptStateProvider notificationInterruptStateProvider,
            HeadsUpManager headsUpManager
            IStatusBarService service
    ) {
        mLogger = logger;
        mNotifInflater = notifInflater;
@@ -83,8 +77,6 @@ public class PreparationCoordinator implements Coordinator {
        mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);
        mViewBarn = viewBarn;
        mStatusBarService = service;
        mNotificationInterruptStateProvider = notificationInterruptStateProvider;
        mHeadsUpManager = headsUpManager;
    }

    @Override
@@ -158,11 +150,6 @@ public class PreparationCoordinator implements Coordinator {
            mLogger.logNotifInflated(entry.getKey());
            mViewBarn.registerViewForEntry(entry, entry.getRow());
            mInflationStates.put(entry, STATE_INFLATED);

            // TODO: should eventually be moved to HeadsUpCoordinator
            if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
                mHeadsUpManager.showNotification(entry);
            }
            mNotifInflatingFilter.invalidateList();
        }
    };
+0 −9
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.systemui.statusbar.notification.collection.inflation;

import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;

import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -227,24 +225,17 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {

        final boolean useIncreasedCollapsedHeight =
                mMessagingUtil.isImportantMessaging(sbn, entry.getImportance());
        final boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
                && !mPresenter.isPresenterFullyCollapsed();
        final boolean isLowPriority = entry.isAmbient();

        RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
        params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
        params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
        params.setUseLowPriority(entry.isAmbient());

        if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
            params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
        }
        //TODO: Replace this API with RowContentBindParams directly
        row.setNeedsRedaction(mNotificationLockscreenUserManager.needsRedaction(entry));
        params.rebindAllContentViews();
        mRowContentBindStage.requestRebind(entry, en -> {
            row.setUsesIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
            row.setUsesIncreasedHeadsUpHeight(useIncreasedHeadsUp);
            row.setIsLowPriority(isLowPriority);
            mInflationCallback.onAsyncInflationFinished(en);
        });
+95 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.headsup;

import androidx.annotation.NonNull;

import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * Controller class for old pipeline heads up view binding. It listens to
 * {@link NotificationEntryManager} entry events and appropriately binds or unbinds the heads up
 * view.
 *
 * This has a subtle contract with {@link NotificationAlertingManager} where this controller handles
 * the heads up binding, but {@link NotificationAlertingManager} listens for general inflation
 * events to actually mark it heads up/update. In the new pipeline, we combine the classes.
 * See {@link HeadsUpCoordinator}.
 */
@Singleton
public class HeadsUpBindController {
    private final HeadsUpViewBinder mHeadsUpViewBinder;
    private final NotificationInterruptStateProvider mInterruptStateProvider;

    @Inject
    HeadsUpBindController(
            HeadsUpViewBinder headsUpViewBinder,
            NotificationInterruptStateProvider notificationInterruptStateProvider) {
        mInterruptStateProvider = notificationInterruptStateProvider;
        mHeadsUpViewBinder = headsUpViewBinder;
    }

    /**
     * Attach this controller and add its listeners.
     */
    public void attach(
            NotificationEntryManager entryManager,
            HeadsUpManager headsUpManager) {
        entryManager.addCollectionListener(mCollectionListener);
        headsUpManager.addListener(mOnHeadsUpChangedListener);
    }

    private NotifCollectionListener mCollectionListener = new NotifCollectionListener() {
        @Override
        public void onEntryAdded(NotificationEntry entry) {
            if (mInterruptStateProvider.shouldHeadsUp(entry)) {
                mHeadsUpViewBinder.bindHeadsUpView(entry, null);
            }
        }

        @Override
        public void onEntryUpdated(NotificationEntry entry) {
            if (mInterruptStateProvider.shouldHeadsUp(entry)) {
                mHeadsUpViewBinder.bindHeadsUpView(entry, null);
            }
        }

        @Override
        public void onEntryCleanUp(NotificationEntry entry) {
            mHeadsUpViewBinder.abortBindCallback(entry);
        }
    };

    private OnHeadsUpChangedListener mOnHeadsUpChangedListener  = new OnHeadsUpChangedListener() {
        @Override
        public void onHeadsUpStateChanged(@NonNull NotificationEntry entry, boolean isHeadsUp) {
            if (!isHeadsUp) {
                mHeadsUpViewBinder.unbindHeadsUpView(entry);
            }
        }
    };
}
Loading