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

Commit 9b2a480c authored by Evan Laird's avatar Evan Laird
Browse files

Create a shim for StatusBarManager click methods

`StatusBarManager#onNotificationClick` and
`StatusBarManager#onNotificationActionClick` are signals we send to
system server about notification clicks. This CL adds a shim so that we
can have an in-process callback about the exact same events.

This CL also adds NotificationInteractionTracker, which basically just
merges the NotificationClickNotifier callbacks with the notification
collection and will be able to answer the question "has the user
interacted with this notification"

Lastly, this modifies the logic in ForegroundServiceLifetimeExtender
which now checks the interaction flag for notifications. So if a user
tapped on a notification action (for instance) which _then_ triggers a
notification cancel, it will succeed. It _does not_ as of yet release
the notification from lifetime extension upon interaction. So if a
notification is canceled and then interacted with, it will still live
the full amount of time.

Test: atest SystemUITests
Bug: 144324894
Change-Id: I42201d6e7b7ffe9ad4f19c774b638a36a51750ef
parent 31db4f45
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -23,10 +23,13 @@ import android.os.Looper;
import android.util.ArraySet;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.NotificationInteractionTracker;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.util.time.SystemClock;

import javax.inject.Inject;

/**
 * Extends the lifetime of foreground notification services such that they show for at least
 * five seconds
@@ -41,9 +44,14 @@ public class ForegroundServiceLifetimeExtender implements NotificationLifetimeEx
    private ArraySet<NotificationEntry> mManagedEntries = new ArraySet<>();
    private Handler mHandler = new Handler(Looper.getMainLooper());
    private final SystemClock mSystemClock;
    private final NotificationInteractionTracker mInteractionTracker;

    public ForegroundServiceLifetimeExtender(SystemClock systemClock) {
    @Inject
    public ForegroundServiceLifetimeExtender(
            NotificationInteractionTracker interactionTracker,
            SystemClock systemClock) {
        mSystemClock = systemClock;
        mInteractionTracker = interactionTracker;
    }

    @Override
@@ -58,8 +66,9 @@ public class ForegroundServiceLifetimeExtender implements NotificationLifetimeEx
            return false;
        }

        long currentTime = mSystemClock.uptimeMillis();
        return currentTime - entry.getCreationTime() < MIN_FGS_TIME_MS;
        boolean hasInteracted = mInteractionTracker.hasUserInteractedWith(entry.getKey());
        long aliveTime = mSystemClock.uptimeMillis() - entry.getCreationTime();
        return aliveTime < MIN_FGS_TIME_MS && !hasInteracted;
    }

    @Override
+2 −2
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ public class ForegroundServiceNotificationListener {
            ForegroundServiceController foregroundServiceController,
            NotificationEntryManager notificationEntryManager,
            NotifPipeline notifPipeline,
            ForegroundServiceLifetimeExtender fgsLifetimeExtender,
            SystemClock systemClock) {
        mContext = context;
        mForegroundServiceController = foregroundServiceController;
@@ -78,8 +79,7 @@ public class ForegroundServiceNotificationListener {
                removeNotification(entry.getSbn());
            }
        });
        mEntryManager.addNotificationLifetimeExtender(
                new ForegroundServiceLifetimeExtender(systemClock));
        mEntryManager.addNotificationLifetimeExtender(fgsLifetimeExtender);

        notifPipeline.addCollectionListener(new NotifCollectionListener() {
            @Override
+87 −0
Original line number Diff line number Diff line
package com.android.systemui.statusbar

import android.app.Notification
import android.os.RemoteException
import com.android.internal.statusbar.IStatusBarService
import com.android.internal.statusbar.NotificationVisibility
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.Assert
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Class to shim calls to IStatusBarManager#onNotificationClick/#onNotificationActionClick that
 * allow an in-process notification to go out (e.g., for tracking interactions) as well as
 * sending the messages along to system server.
 *
 * NOTE: this class eats exceptions from system server, as no current client of these APIs cares
 * about errors
 */
@Singleton
public class NotificationClickNotifier @Inject constructor(
    val barService: IStatusBarService,
    @Main val mainExecutor: Executor
) {
    val listeners = mutableListOf<NotificationInteractionListener>()

    fun addNotificationInteractionListener(listener: NotificationInteractionListener) {
        Assert.isMainThread()
        listeners.add(listener)
    }

    fun removeNotificationInteractionListener(listener: NotificationInteractionListener) {
        Assert.isMainThread()
        listeners.remove(listener)
    }

    private fun notifyListenersAboutInteraction(key: String) {
        for (l in listeners) {
            l.onNotificationInteraction(key)
        }
    }

    fun onNotificationActionClick(
        key: String,
        actionIndex: Int,
        action: Notification.Action,
        visibility: NotificationVisibility,
        generatedByAssistant: Boolean
    ) {
        try {
            barService.onNotificationActionClick(
                    key, actionIndex, action, visibility, generatedByAssistant)
        } catch (e: RemoteException) {
            // nothing
        }

        mainExecutor.execute {
            notifyListenersAboutInteraction(key)
        }
    }

    fun onNotificationClick(
        key: String,
        visibility: NotificationVisibility
    ) {
        try {
            barService.onNotificationClick(key, visibility)
        } catch (e: RemoteException) {
            // nothing
        }

        mainExecutor.execute {
            notifyListenersAboutInteraction(key)
        }
    }
}

/**
 * Interface for listeners to get notified when a notification is interacted with via a click or
 * interaction with remote input or actions
 */
interface NotificationInteractionListener {
    fun onNotificationInteraction(key: String)
}

private const val TAG = "NotificationClickNotifier"
+40 −0
Original line number Diff line number Diff line
package com.android.systemui.statusbar

import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Class to track user interaction with notifications. It's a glorified map of key : bool that can
 * merge multiple "user interacted with notification" signals into a single place.
 */
@Singleton
class NotificationInteractionTracker @Inject constructor(
    private val clicker: NotificationClickNotifier,
    private val entryManager: NotificationEntryManager
) : NotifCollectionListener, NotificationInteractionListener {
    private val interactions = mutableMapOf<String, Boolean>()

    init {
        clicker.addNotificationInteractionListener(this)
        entryManager.addCollectionListener(this)
    }

    fun hasUserInteractedWith(key: String): Boolean = key in interactions

    override fun onEntryAdded(entry: NotificationEntry) {
        interactions[entry.key] = false
    }

    override fun onEntryCleanUp(entry: NotificationEntry) {
        interactions.remove(entry.key)
    }

    override fun onNotificationInteraction(key: String) {
        interactions[key] = true
    }
}

private const val TAG = "NotificationInteractionTracker"
+4 −10
Original line number Diff line number Diff line
@@ -35,7 +35,6 @@ import android.content.IntentSender;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -43,7 +42,6 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;

import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -92,9 +90,9 @@ public class NotificationLockscreenUserManagerImpl implements
    private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
    private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
    private final UserManager mUserManager;
    private final IStatusBarService mBarService;
    private final List<UserChangedListener> mListeners = new ArrayList<>();
    private final BroadcastDispatcher mBroadcastDispatcher;
    private final NotificationClickNotifier mClickNotifier;

    private boolean mShowLockscreenNotifications;
    private boolean mAllowLockscreenRemoteInput;
@@ -170,11 +168,7 @@ public class NotificationLockscreenUserManagerImpl implements
                        final NotificationVisibility nv = NotificationVisibility.obtain(
                                notificationKey,
                                rank, count, true, location);
                        try {
                            mBarService.onNotificationClick(notificationKey, nv);
                        } catch (RemoteException exception) {
                            /* ignore */
                        }
                        mClickNotifier.onNotificationClick(notificationKey, nv);
                    }
                    break;
            }
@@ -203,7 +197,7 @@ public class NotificationLockscreenUserManagerImpl implements
            BroadcastDispatcher broadcastDispatcher,
            DevicePolicyManager devicePolicyManager,
            UserManager userManager,
            IStatusBarService iStatusBarService,
            NotificationClickNotifier clickNotifier,
            KeyguardManager keyguardManager,
            StatusBarStateController statusBarStateController,
            @Main Handler mainHandler,
@@ -214,7 +208,7 @@ public class NotificationLockscreenUserManagerImpl implements
        mDevicePolicyManager = devicePolicyManager;
        mUserManager = userManager;
        mCurrentUserId = ActivityManager.getCurrentUser();
        mBarService = iStatusBarService;
        mClickNotifier = clickNotifier;
        statusBarStateController.addCallback(this);
        mLockPatternUtils = new LockPatternUtils(context);
        mKeyguardManager = keyguardManager;
Loading