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

Commit d65c2db9 authored by Gus Prevas's avatar Gus Prevas
Browse files

Moves fullscreen intent logic to NotificationActivityStarter.

This change generalizes the AlertTransferListener interface as described
in its javadoc in order to make it a general-purpose listener interface
for changes managed by NotificationEntryManager, then uses this
mechanism to move handling of fullscreen intents on incoming
notifications from NotificationEntryManager to
StatusBarNotificationActivityStarter.

Test: atest NotificationEntryManagerTest
Change-Id: Ie8cb510466b2750fa74ef093dcad0726cf17210d
parent ec9e1f09
Loading
Loading
Loading
Loading
+8 −7
Original line number Diff line number Diff line
@@ -16,26 +16,27 @@
package com.android.systemui.statusbar.notification;

/**
 * Listener interface for when NotificationEntryManager needs to tell
 * NotificationGroupAlertTransferHelper things. Will eventually grow to be a general-purpose
 * listening interface for the NotificationEntryManager.
 * Listener interface for changes sent by NotificationEntryManager.
 */
public interface AlertTransferListener {
public interface NotificationEntryListener {
    /**
     * Called when a new notification is posted. At this point, the notification is "pending": its
     * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
     */
    void onPendingEntryAdded(NotificationData.Entry entry);
    default void onPendingEntryAdded(NotificationData.Entry entry) {
    }

    /**
     * Called when an existing notification's views are reinflated (usually due to an update being
     * posted to that notification).
     */
    void onEntryReinflated(NotificationData.Entry entry);
    default void onEntryReinflated(NotificationData.Entry entry) {
    }

    /**
     * Called when a notification has been removed (either because the user swiped it away or
     * because the developer retracted it).
     */
    void onEntryRemoved(NotificationData.Entry entry);
    default void onEntryRemoved(NotificationData.Entry entry) {
    }
}
+14 −67
Original line number Diff line number Diff line
@@ -22,33 +22,25 @@ import static com.android.systemui.statusbar.notification.row.NotificationInflat

import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.EventLogTags;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AmbientPulseManager;
@@ -101,7 +93,6 @@ public class NotificationEntryManager implements
            Dependency.get(NotificationGroupManager.class);
    private final NotificationGutsManager mGutsManager =
            Dependency.get(NotificationGutsManager.class);
    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
    private final DeviceProvisionedController mDeviceProvisionedController =
            Dependency.get(DeviceProvisionedController.class);
    private final VisualStabilityManager mVisualStabilityManager =
@@ -124,7 +115,6 @@ public class NotificationEntryManager implements
    private final Handler mDeferredNotificationViewUpdateHandler;
    private Runnable mUpdateNotificationViewsCallback;

    protected IDreamManager mDreamManager;
    protected IStatusBarService mBarService;
    private NotificationPresenter mPresenter;
    private Callback mCallback;
@@ -136,7 +126,7 @@ public class NotificationEntryManager implements
    @VisibleForTesting
    final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
            = new ArrayList<>();
    @Nullable private AlertTransferListener mAlertTransferListener;
    private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();

    private final DeviceProvisionedController.DeviceProvisionedListener
            mDeviceProvisionedListener =
@@ -169,15 +159,14 @@ public class NotificationEntryManager implements
        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mBarService = IStatusBarService.Stub.asInterface(
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
        mDreamManager = IDreamManager.Stub.asInterface(
                ServiceManager.checkService(DreamService.DREAM_SERVICE));
        mBubbleController.setDismissListener(this /* bubbleEventListener */);
        mNotificationData = new NotificationData();
        mDeferredNotificationViewUpdateHandler = new Handler();
    }

    public void setAlertTransferListener(AlertTransferListener listener) {
        mAlertTransferListener = listener;
    /** Adds a {@link NotificationEntryListener}. */
    public void addNotificationEntryListener(NotificationEntryListener listener) {
        mNotificationEntryListeners.add(listener);
    }

    /**
@@ -261,14 +250,6 @@ public class NotificationEntryManager implements
        updateNotifications();
    }

    private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
        if (mPresenter.isDeviceInVrMode()) {
            return true;
        }

        return entry.shouldSuppressFullScreenIntent();
    }

    public void performRemoveNotification(StatusBarNotification n) {
        final int rank = mNotificationData.getRank(n.getKey());
        final int count = mNotificationData.getActiveNotifications().size();
@@ -413,8 +394,8 @@ public class NotificationEntryManager implements
                    mVisualStabilityManager.onLowPriorityUpdated(entry);
                    mPresenter.updateNotificationViews();
                }
                if (mAlertTransferListener != null) {
                    mAlertTransferListener.onEntryReinflated(entry);
                for (NotificationEntryListener listener : mNotificationEntryListeners) {
                    listener.onEntryReinflated(entry);
                }
            }
        }
@@ -431,8 +412,10 @@ public class NotificationEntryManager implements
        final NotificationData.Entry entry = mNotificationData.get(key);

        abortExistingInflation(key);
        if (mAlertTransferListener != null && entry != null) {
            mAlertTransferListener.onEntryRemoved(entry);
        if (entry != null) {
            for (NotificationEntryListener listener : mNotificationEntryListeners) {
                listener.onEntryRemoved(entry);
            }
        }

        // Attempt to remove notifications from their alert managers (heads up, ambient pulse).
@@ -589,51 +572,15 @@ public class NotificationEntryManager implements
        mNotificationData.updateRanking(rankingMap);
        NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
        rankingMap.getRanking(key, ranking);
        NotificationData.Entry shadeEntry = createNotificationEntry(notification, ranking);
        boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(shadeEntry);
        if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
            if (shouldSuppressFullScreenIntent(shadeEntry)) {
                if (DEBUG) {
                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
                }
            } else if (mNotificationData.getImportance(key)
                    < NotificationManager.IMPORTANCE_HIGH) {
                if (DEBUG) {
                    Log.d(TAG, "No Fullscreen intent: not important enough: "
                            + key);
                }
            } else {
                // Stop screensaver if the notification has a fullscreen intent.
                // (like an incoming phone call)
                Dependency.get(UiOffloadThread.class).submit(() -> {
                    try {
                        mDreamManager.awaken();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                });

                // not immersive & a fullscreen alert should be shown
                if (DEBUG)
                    Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
                try {
                    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
                            key);
                    notification.getNotification().fullScreenIntent.send();
                    shadeEntry.notifyFullScreenIntentLaunched();
                    mMetricsLogger.count("note_fullscreen", 1);
                } catch (PendingIntent.CanceledException e) {
                }
            }
        }
        NotificationData.Entry entry = createNotificationEntry(notification, ranking);
        abortExistingInflation(key);

        mForegroundServiceController.addNotification(notification,
                mNotificationData.getImportance(key));

        mPendingNotifications.put(key, shadeEntry);
        if (mAlertTransferListener != null) {
            mAlertTransferListener.onPendingEntryAdded(shadeEntry);
        mPendingNotifications.put(key, entry);
        for (NotificationEntryListener listener : mNotificationEntryListeners) {
            listener.onPendingEntryAdded(entry);
        }
    }

+4 −3
Original line number Diff line number Diff line
@@ -29,8 +29,8 @@ import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListen
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.AlertTransferListener;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
@@ -95,7 +95,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
        // not being up to date.
        mEntryManager = entryManager;

        mEntryManager.setAlertTransferListener(mAlertTransferListener);
        mEntryManager.addNotificationEntryListener(mNotificationEntryListener);
        groupManager.addOnGroupChangeListener(mOnGroupChangeListener);
    }

@@ -186,7 +186,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
        }
    }

    private final AlertTransferListener mAlertTransferListener = new AlertTransferListener() {
    private final NotificationEntryListener mNotificationEntryListener =
            new NotificationEntryListener() {
        // Called when a new notification has been posted but is not inflated yet. We use this to
        // see as early as we can if we need to abort a transfer.
        @Override
+70 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
@@ -33,15 +34,21 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.view.RemoteAnimationAdapter;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.CommandQueue;
@@ -54,7 +61,9 @@ import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -66,6 +75,7 @@ import com.android.systemui.statusbar.policy.PreviewInflater;
public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {

    private static final String TAG = "NotificationClickHandler";
    protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
    private final NotificationGroupManager mGroupManager =
@@ -84,6 +94,9 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
            Dependency.get(NotificationEntryManager.class);
    private final StatusBarStateController mStatusBarStateController =
            Dependency.get(StatusBarStateController.class);
    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
            Dependency.get(NotificationInterruptionStateProvider.class);
    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);

    private final Context mContext;
    private final NotificationPanelView mNotificationPanel;
@@ -94,6 +107,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
    private final ActivityLaunchAnimator mActivityLaunchAnimator;
    private final IStatusBarService mBarService;
    private final CommandQueue mCommandQueue;
    private final IDreamManager mDreamManager;

    private boolean mIsCollapsingToShowActivityOverLockscreen;

@@ -112,6 +126,15 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
        mBarService = IStatusBarService.Stub.asInterface(
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
        mCommandQueue = getComponent(context, CommandQueue.class);
        mDreamManager = IDreamManager.Stub.asInterface(
                ServiceManager.checkService(DreamService.DREAM_SERVICE));

        mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
            @Override
            public void onPendingEntryAdded(NotificationData.Entry entry) {
                handleFullScreenIntent(entry);
            }
        });
    }

    /**
@@ -322,6 +345,45 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
        }, null, false /* afterKeyguardGone */);
    }

    private void handleFullScreenIntent(NotificationData.Entry entry) {
        boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
        if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
            if (shouldSuppressFullScreenIntent(entry)) {
                if (DEBUG) {
                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key);
                }
            } else if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
                if (DEBUG) {
                    Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.key);
                }
            } else {
                // Stop screensaver if the notification has a fullscreen intent.
                // (like an incoming phone call)
                Dependency.get(UiOffloadThread.class).submit(() -> {
                    try {
                        mDreamManager.awaken();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                });

                // not immersive & a fullscreen alert should be shown
                if (DEBUG) {
                    Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
                }
                try {
                    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
                            entry.key);
                    entry.notification.getNotification().fullScreenIntent.send();
                    entry.notifyFullScreenIntentLaunched();
                    mMetricsLogger.count("note_fullscreen", 1);
                } catch (PendingIntent.CanceledException e) {
                    // ignore
                }
            }
        }
    }

    @Override
    public boolean isCollapsingToShowActivityOverLockscreen() {
        return mIsCollapsingToShowActivityOverLockscreen;
@@ -351,6 +413,14 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
                || !mActivityLaunchAnimator.isAnimationPending();
    }

    private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
        if (mPresenter.isDeviceInVrMode()) {
            return true;
        }

        return entry.shouldSuppressFullScreenIntent();
    }

    private void removeNotification(StatusBarNotification notification) {
        // We have to post it to the UI thread for synchronization
        Dependency.get(MAIN_HANDLER).post(() -> {
+1 −1
Original line number Diff line number Diff line
@@ -126,7 +126,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
        mPowerManager = new PowerManager(mContext, powerManagerService,
                Handler.createAsync(Looper.myLooper()));

        mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager,
        mEntryManager = new TestableNotificationEntryManager(mPowerManager,
                mContext);
        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
        Dependency.get(InitController.class).executePostInitTasks();
Loading