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

Unverified Commit 8dc4e3e8 authored by Danny Baumann's avatar Danny Baumann Committed by Michael Bestas
Browse files

Support enforcing a minimum delay between notification sounds of an app.



Useful e.g. for messenger apps.

Co-authored-by: default avatarLuK1337 <priv.luk@gmail.com>
Change-Id: If8e8cc9e2f02d70537c1f9dc14f22bbd0ec1e9a6
parent 35174dd7
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -82,6 +82,8 @@ interface INotificationManager
    boolean areNotificationsEnabled(String pkg);
    int getPackageImportance(String pkg);
    boolean isImportanceLocked(String pkg, int uid);
    void setNotificationSoundTimeout(String pkg, int uid, long timeout);
    long getNotificationSoundTimeout(String pkg, int uid);

    List<String> getAllowedAssistantAdjustments(String pkg);

+36 −1
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -59,9 +60,11 @@ import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;

@@ -156,6 +159,7 @@ public final class NotificationAttentionHelper {
    private final AudioAttributes mInCallNotificationAudioAttributes;
    private final float mInCallNotificationVolume;
    private Binder mCallNotificationToken = null;
    private final ArrayMap<String, Long> mLastSoundTimestamps = new ArrayMap<>();

    // Settings flags
    private boolean mNotificationCooldownEnabled;
@@ -423,7 +427,8 @@ public final class NotificationAttentionHelper {
                boolean vibrateOnly =
                        hasValidVibrate && mNotificationCooldownVibrateUnlocked && mUserPresent;
                boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
                if (hasAudibleAlert && !shouldMuteNotificationLocked(record, signals)) {
                if (hasAudibleAlert && !shouldMuteNotificationLocked(record, signals)
                        && !isInSoundTimeoutPeriod(record)) {
                    if (!sentAccessibilityEvent) {
                        sendAccessibilityEvent(record);
                        sentAccessibilityEvent = true;
@@ -519,6 +524,10 @@ public final class NotificationAttentionHelper {
                }
            }
        }
        if (buzz || beep) {
            mLastSoundTimestamps.put(generateLastSoundTimeoutKey(record),
                    SystemClock.elapsedRealtime());
        }
        final int buzzBeepBlinkLoggingCode =
                (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0) | getPoliteBit(record);
        if (buzzBeepBlinkLoggingCode > 0) {
@@ -543,6 +552,24 @@ public final class NotificationAttentionHelper {
        return buzzBeepBlinkLoggingCode;
    }

    private boolean isInSoundTimeoutPeriod(NotificationRecord record) {
        long timeoutMillis = mNMP.getNotificationSoundTimeout(
                record.getSbn().getPackageName(), record.getSbn().getUid());
        if (timeoutMillis == 0) {
            return false;
        }

        Long value = mLastSoundTimestamps.get(generateLastSoundTimeoutKey(record));
        if (value == null) {
            return false;
        }
        return SystemClock.elapsedRealtime() - value < timeoutMillis;
    }

    private String generateLastSoundTimeoutKey(NotificationRecord record) {
        return record.getSbn().getPackageName() + "|" + record.getSbn().getUid();
    }

    private int getPoliteBit(final NotificationRecord record) {
        switch (getPolitenessState(record)) {
            case PolitenessStrategy.POLITE_STATE_POLITE:
@@ -1078,6 +1105,14 @@ public final class NotificationAttentionHelper {
        pw.print(prefix);
        pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);

        long now = SystemClock.elapsedRealtime();
        pw.println("\n  Last notification sound timestamps:");
        for (Map.Entry<String, Long> entry : mLastSoundTimestamps.entrySet()) {
            pw.print("    " + entry.getKey() + " -> ");
            TimeUtils.formatDuration(entry.getValue(), now, pw);
            pw.println(" ago");
        }

        int N = mLights.size();
        if (N > 0) {
            pw.print(prefix);
+1 −0
Original line number Diff line number Diff line
@@ -27,4 +27,5 @@ interface NotificationManagerPrivate {
    NotificationRecord getNotificationByKey(String key);

    void timeoutNotification(String key);
    long getNotificationSoundTimeout(String pkg, int uid);
}
+18 −0
Original line number Diff line number Diff line
@@ -1809,6 +1809,11 @@ public class NotificationManagerService extends SystemService {
                }
            }
        }
        @Override
        public long getNotificationSoundTimeout(String pkg, int uid) {
            return mPreferencesHelper.getNotificationSoundTimeout(pkg, uid);
        }
    };
    @VisibleForTesting
@@ -3839,6 +3844,19 @@ public class NotificationManagerService extends SystemService {
            // callback from AppOpsManager.
        }
        @Override
        public void setNotificationSoundTimeout(String pkg, int uid, long timeout) {
            checkCallerIsSystem();
            mPreferencesHelper.setNotificationSoundTimeout(pkg, uid, timeout);
            handleSavePolicyFile();
        }
        @Override
        public long getNotificationSoundTimeout(String pkg, int uid) {
            checkCallerIsSystem();
            return mPreferencesHelper.getNotificationSoundTimeout(pkg, uid);
        }
        /**
         * Updates the enabled state for notifications for the given package (and uid).
         * Additionally, this method marks the app importance as locked by the user, which
+28 −0
Original line number Diff line number Diff line
@@ -157,6 +157,7 @@ public class PreferencesHelper implements RankingConfig {
    private static final String ATT_SENT_VALID_BUBBLE = "sent_valid_bubble";

    private static final String ATT_CREATION_TIME = "creation_time";
    private static final String ATT_SOUND_TIMEOUT = "sound-timeout";

    private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
    private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
@@ -180,6 +181,8 @@ public class PreferencesHelper implements RankingConfig {
     */
    private static final int DEFAULT_LOCKED_APP_FIELDS = 0;

    private static final int DEFAULT_SOUND_TIMEOUT = 0;

    /**
     * All user-lockable fields for a given application.
     */
@@ -344,6 +347,8 @@ public class PreferencesHelper implements RankingConfig {
            r.userDemotedMsgApp = parser.getAttributeBoolean(
                    null, ATT_USER_DEMOTED_INVALID_MSG_APP, false);
            r.hasSentValidBubble = parser.getAttributeBoolean(null, ATT_SENT_VALID_BUBBLE, false);
            r.soundTimeout = parser.getAttributeLong(
                    null, ATT_SOUND_TIMEOUT, DEFAULT_SOUND_TIMEOUT);

            final int innerDepth = parser.getDepth();
            int type;
@@ -675,6 +680,9 @@ public class PreferencesHelper implements RankingConfig {
        if (r.visibility != DEFAULT_VISIBILITY) {
            out.attributeInt(null, ATT_VISIBILITY, r.visibility);
        }
        if (r.soundTimeout != DEFAULT_SOUND_TIMEOUT) {
            out.attributeLong(null, ATT_SOUND_TIMEOUT, r.soundTimeout);
        }
        if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
            out.attributeInt(null, ATT_ALLOW_BUBBLE, r.bubblePreference);
        }
@@ -1958,6 +1966,21 @@ public class PreferencesHelper implements RankingConfig {
        updateConfig();
    }

    /**
     * @hide
     */
    public long getNotificationSoundTimeout(String packageName, int uid) {
        return getOrCreatePackagePreferencesLocked(packageName, uid).soundTimeout;
    }

    /**
     * @hide
     */
    public void setNotificationSoundTimeout(String packageName, int uid, long timeout) {
        getOrCreatePackagePreferencesLocked(packageName, uid).soundTimeout = timeout;
        updateConfig();
    }

    /**
     * Returns the delegate for a given package, if it's allowed by the package and the user.
     */
@@ -2441,6 +2464,9 @@ public class PreferencesHelper implements RankingConfig {
                        if (r.showBadge != DEFAULT_SHOW_BADGE) {
                            PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
                        }
                        if (r.soundTimeout != DEFAULT_SOUND_TIMEOUT) {
                            PackagePreferences.put("soundTimeout", r.soundTimeout);
                        }
                        JSONArray channels = new JSONArray();
                        for (NotificationChannel channel : r.channels.values()) {
                            channels.put(channel.toJson());
@@ -2716,6 +2742,7 @@ public class PreferencesHelper implements RankingConfig {
                p.groups = new ArrayMap<>();
                p.delegate = null;
                p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
                p.soundTimeout = DEFAULT_SOUND_TIMEOUT;
                p.bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
                p.importance = DEFAULT_IMPORTANCE;
                p.priority = DEFAULT_PRIORITY;
@@ -2953,6 +2980,7 @@ public class PreferencesHelper implements RankingConfig {
        boolean showBadge = DEFAULT_SHOW_BADGE;
        int bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
        int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
        long soundTimeout = DEFAULT_SOUND_TIMEOUT;
        // these fields are loaded on boot from a different source of truth and so are not
        // written to notification policy xml
        boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;