Loading core/java/android/provider/Settings.java +23 −1 Original line number Diff line number Diff line Loading @@ -12570,6 +12570,28 @@ public final class Settings { public static final String NOTIFICATION_SNOOZE_OPTIONS = "notification_snooze_options"; /** * Settings key for the ratio of notification dismissals to notification views - one of the * criteria for showing the notification blocking helper. * * <p>The value is a float ranging from 0.0 to 1.0 (the closer to 0.0, the more intrusive * the blocking helper will be). * * @hide */ public static final String BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT = "blocking_helper_dismiss_to_view_ratio"; /** * Settings key for the longest streak of dismissals - one of the criteria for showing the * notification blocking helper. * * <p>The value is an integer greater than 0. * * @hide */ public static final String BLOCKING_HELPER_STREAK_LIMIT = "blocking_helper_streak_limit"; /** * Configuration flags for SQLite Compatibility WAL. Encoded as a key-value list, separated * by commas. E.g.: compatibility_wal_supported=true, wal_syncmode=OFF Loading core/java/android/service/notification/NotificationAssistantService.java +4 −1 Original line number Diff line number Diff line Loading @@ -48,7 +48,10 @@ public abstract class NotificationAssistantService extends NotificationListenerS public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; private Handler mHandler; /** * @hide */ protected Handler mHandler; @Override protected void attachBaseContext(Context base) { Loading core/tests/coretests/src/android/provider/SettingsBackupTest.java +2 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,8 @@ public class SettingsBackupTest { Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS, Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS, Settings.Global.BLE_SCAN_BACKGROUND_MODE, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX, Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX, Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX, Loading packages/ExtServices/src/android/ext/services/notification/Assistant.java +75 −6 Original line number Diff line number Diff line Loading @@ -17,16 +17,20 @@ package android.ext.services.notification; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.service.notification.NotificationListenerService.Ranking .USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import android.app.INotificationManager; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.ext.services.R; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.storage.StorageManager; import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.NotificationAssistantService; import android.service.notification.NotificationStats; Loading Loading @@ -74,9 +78,12 @@ public class Assistant extends NotificationAssistantService { PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL); } private float mDismissToViewRatioLimit; private int mStreakLimit; // key : impressions tracker // TODO: prune deleted channels and apps ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>(); final ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>(); // SBN key : channel id ArrayMap<String, String> mLiveNotifications = new ArrayMap<>(); Loading @@ -86,6 +93,14 @@ public class Assistant extends NotificationAssistantService { public Assistant() { } @Override public void onCreate() { super.onCreate(); // Contexts are correctly hooked up by the creation step, which is required for the observer // to be hooked up/initialized. new SettingsObserver(mHandler); } private void loadFile() { if (DEBUG) Slog.d(TAG, "loadFile"); AsyncTask.execute(() -> { Loading Loading @@ -120,7 +135,7 @@ public class Assistant extends NotificationAssistantService { continue; } String key = parser.getAttributeValue(null, ATT_KEY); ChannelImpressions ci = new ChannelImpressions(); ChannelImpressions ci = createChannelImpressionsWithThresholds(); ci.populateFromXml(parser); synchronized (mkeyToImpressions) { ci.append(mkeyToImpressions.get(key)); Loading Loading @@ -184,7 +199,7 @@ public class Assistant extends NotificationAssistantService { String key = getKey( sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId()); ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, new ChannelImpressions()); createChannelImpressionsWithThresholds()); if (ranking.getImportance() > IMPORTANCE_MIN && ci.shouldTriggerBlock()) { adjustNotification(createNegativeAdjustment( sbn.getPackageName(), sbn.getKey(), sbn.getUserId())); Loading @@ -206,7 +221,7 @@ public class Assistant extends NotificationAssistantService { String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId); synchronized (mkeyToImpressions) { ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, new ChannelImpressions()); createChannelImpressionsWithThresholds()); if (stats.hasSeen()) { ci.incrementViews(); updatedImpressions = true; Loading Loading @@ -310,4 +325,58 @@ public class Assistant extends NotificationAssistantService { mkeyToImpressions.put(key, ci); } } private ChannelImpressions createChannelImpressionsWithThresholds() { ChannelImpressions impressions = new ChannelImpressions(); impressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit); return impressions; } /** * Observer for updates on blocking helper threshold values. */ private final class SettingsObserver extends ContentObserver { private final Uri STREAK_LIMIT_URI = Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT); private final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI = Settings.Global.getUriFor( Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT); public SettingsObserver(Handler handler) { super(handler); ContentResolver resolver = getApplicationContext().getContentResolver(); resolver.registerContentObserver( DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, getUserId()); resolver.registerContentObserver(STREAK_LIMIT_URI, false, this, getUserId()); // Update all uris on creation. update(null); } @Override public void onChange(boolean selfChange, Uri uri) { update(uri); } private void update(Uri uri) { ContentResolver resolver = getApplicationContext().getContentResolver(); if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) { mDismissToViewRatioLimit = Settings.Global.getFloat( resolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT); } if (uri == null || STREAK_LIMIT_URI.equals(uri)) { mStreakLimit = Settings.Global.getInt( resolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, ChannelImpressions.DEFAULT_STREAK_LIMIT); } // Update all existing channel impression objects with any new limits/thresholds. synchronized (mkeyToImpressions) { for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) { channelImpressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit); } } } } } No newline at end of file packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java +33 −10 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; Loading @@ -30,8 +32,8 @@ public final class ChannelImpressions implements Parcelable { private static final String TAG = "ExtAssistant.CI"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); static final double DISMISS_TO_VIEW_RATIO_LIMIT = .4; static final int STREAK_LIMIT = 2; static final float DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT = .4f; static final int DEFAULT_STREAK_LIMIT = 2; static final String ATT_DISMISSALS = "dismisses"; static final String ATT_VIEWS = "views"; static final String ATT_STREAK = "streak"; Loading @@ -40,18 +42,20 @@ public final class ChannelImpressions implements Parcelable { private int mViews = 0; private int mStreak = 0; public ChannelImpressions() { } private float mDismissToViewRatioLimit; private int mStreakLimit; public ChannelImpressions(int dismissals, int views) { mDismissals = dismissals; mViews = views; public ChannelImpressions() { mDismissToViewRatioLimit = DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT; mStreakLimit = DEFAULT_STREAK_LIMIT; } protected ChannelImpressions(Parcel in) { mDismissals = in.readInt(); mViews = in.readInt(); mStreak = in.readInt(); mDismissToViewRatioLimit = in.readFloat(); mStreakLimit = in.readInt(); } public int getStreak() { Loading @@ -71,6 +75,21 @@ public final class ChannelImpressions implements Parcelable { mStreak++; } void updateThresholds(float dismissToViewRatioLimit, int streakLimit) { mDismissToViewRatioLimit = dismissToViewRatioLimit; mStreakLimit = streakLimit; } @VisibleForTesting float getDismissToViewRatioLimit() { return mDismissToViewRatioLimit; } @VisibleForTesting int getStreakLimit() { return mStreakLimit; } public void append(ChannelImpressions additionalImpressions) { if (additionalImpressions != null) { mViews += additionalImpressions.getViews(); Loading @@ -94,8 +113,8 @@ public final class ChannelImpressions implements Parcelable { if (DEBUG) { Log.d(TAG, "should trigger? " + getDismissals() + " " + getViews() + " " + getStreak()); } return ((double) getDismissals() / getViews()) > DISMISS_TO_VIEW_RATIO_LIMIT && getStreak() > STREAK_LIMIT; return ((float) getDismissals() / getViews()) > mDismissToViewRatioLimit && getStreak() > mStreakLimit; } @Override Loading @@ -103,6 +122,8 @@ public final class ChannelImpressions implements Parcelable { dest.writeInt(mDismissals); dest.writeInt(mViews); dest.writeInt(mStreak); dest.writeFloat(mDismissToViewRatioLimit); dest.writeInt(mStreakLimit); } @Override Loading Loading @@ -148,7 +169,9 @@ public final class ChannelImpressions implements Parcelable { sb.append("mDismissals=").append(mDismissals); sb.append(", mViews=").append(mViews); sb.append(", mStreak=").append(mStreak); sb.append('}'); sb.append(", thresholds=(").append(mDismissToViewRatioLimit); sb.append(",").append(mStreakLimit); sb.append(")}"); return sb.toString(); } Loading Loading
core/java/android/provider/Settings.java +23 −1 Original line number Diff line number Diff line Loading @@ -12570,6 +12570,28 @@ public final class Settings { public static final String NOTIFICATION_SNOOZE_OPTIONS = "notification_snooze_options"; /** * Settings key for the ratio of notification dismissals to notification views - one of the * criteria for showing the notification blocking helper. * * <p>The value is a float ranging from 0.0 to 1.0 (the closer to 0.0, the more intrusive * the blocking helper will be). * * @hide */ public static final String BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT = "blocking_helper_dismiss_to_view_ratio"; /** * Settings key for the longest streak of dismissals - one of the criteria for showing the * notification blocking helper. * * <p>The value is an integer greater than 0. * * @hide */ public static final String BLOCKING_HELPER_STREAK_LIMIT = "blocking_helper_streak_limit"; /** * Configuration flags for SQLite Compatibility WAL. Encoded as a key-value list, separated * by commas. E.g.: compatibility_wal_supported=true, wal_syncmode=OFF Loading
core/java/android/service/notification/NotificationAssistantService.java +4 −1 Original line number Diff line number Diff line Loading @@ -48,7 +48,10 @@ public abstract class NotificationAssistantService extends NotificationListenerS public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; private Handler mHandler; /** * @hide */ protected Handler mHandler; @Override protected void attachBaseContext(Context base) { Loading
core/tests/coretests/src/android/provider/SettingsBackupTest.java +2 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,8 @@ public class SettingsBackupTest { Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS, Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS, Settings.Global.BLE_SCAN_BACKGROUND_MODE, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX, Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX, Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX, Loading
packages/ExtServices/src/android/ext/services/notification/Assistant.java +75 −6 Original line number Diff line number Diff line Loading @@ -17,16 +17,20 @@ package android.ext.services.notification; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.service.notification.NotificationListenerService.Ranking .USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import android.app.INotificationManager; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.ext.services.R; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.storage.StorageManager; import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.NotificationAssistantService; import android.service.notification.NotificationStats; Loading Loading @@ -74,9 +78,12 @@ public class Assistant extends NotificationAssistantService { PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL); } private float mDismissToViewRatioLimit; private int mStreakLimit; // key : impressions tracker // TODO: prune deleted channels and apps ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>(); final ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>(); // SBN key : channel id ArrayMap<String, String> mLiveNotifications = new ArrayMap<>(); Loading @@ -86,6 +93,14 @@ public class Assistant extends NotificationAssistantService { public Assistant() { } @Override public void onCreate() { super.onCreate(); // Contexts are correctly hooked up by the creation step, which is required for the observer // to be hooked up/initialized. new SettingsObserver(mHandler); } private void loadFile() { if (DEBUG) Slog.d(TAG, "loadFile"); AsyncTask.execute(() -> { Loading Loading @@ -120,7 +135,7 @@ public class Assistant extends NotificationAssistantService { continue; } String key = parser.getAttributeValue(null, ATT_KEY); ChannelImpressions ci = new ChannelImpressions(); ChannelImpressions ci = createChannelImpressionsWithThresholds(); ci.populateFromXml(parser); synchronized (mkeyToImpressions) { ci.append(mkeyToImpressions.get(key)); Loading Loading @@ -184,7 +199,7 @@ public class Assistant extends NotificationAssistantService { String key = getKey( sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId()); ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, new ChannelImpressions()); createChannelImpressionsWithThresholds()); if (ranking.getImportance() > IMPORTANCE_MIN && ci.shouldTriggerBlock()) { adjustNotification(createNegativeAdjustment( sbn.getPackageName(), sbn.getKey(), sbn.getUserId())); Loading @@ -206,7 +221,7 @@ public class Assistant extends NotificationAssistantService { String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId); synchronized (mkeyToImpressions) { ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, new ChannelImpressions()); createChannelImpressionsWithThresholds()); if (stats.hasSeen()) { ci.incrementViews(); updatedImpressions = true; Loading Loading @@ -310,4 +325,58 @@ public class Assistant extends NotificationAssistantService { mkeyToImpressions.put(key, ci); } } private ChannelImpressions createChannelImpressionsWithThresholds() { ChannelImpressions impressions = new ChannelImpressions(); impressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit); return impressions; } /** * Observer for updates on blocking helper threshold values. */ private final class SettingsObserver extends ContentObserver { private final Uri STREAK_LIMIT_URI = Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT); private final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI = Settings.Global.getUriFor( Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT); public SettingsObserver(Handler handler) { super(handler); ContentResolver resolver = getApplicationContext().getContentResolver(); resolver.registerContentObserver( DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, getUserId()); resolver.registerContentObserver(STREAK_LIMIT_URI, false, this, getUserId()); // Update all uris on creation. update(null); } @Override public void onChange(boolean selfChange, Uri uri) { update(uri); } private void update(Uri uri) { ContentResolver resolver = getApplicationContext().getContentResolver(); if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) { mDismissToViewRatioLimit = Settings.Global.getFloat( resolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT); } if (uri == null || STREAK_LIMIT_URI.equals(uri)) { mStreakLimit = Settings.Global.getInt( resolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, ChannelImpressions.DEFAULT_STREAK_LIMIT); } // Update all existing channel impression objects with any new limits/thresholds. synchronized (mkeyToImpressions) { for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) { channelImpressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit); } } } } } No newline at end of file
packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java +33 −10 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; Loading @@ -30,8 +32,8 @@ public final class ChannelImpressions implements Parcelable { private static final String TAG = "ExtAssistant.CI"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); static final double DISMISS_TO_VIEW_RATIO_LIMIT = .4; static final int STREAK_LIMIT = 2; static final float DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT = .4f; static final int DEFAULT_STREAK_LIMIT = 2; static final String ATT_DISMISSALS = "dismisses"; static final String ATT_VIEWS = "views"; static final String ATT_STREAK = "streak"; Loading @@ -40,18 +42,20 @@ public final class ChannelImpressions implements Parcelable { private int mViews = 0; private int mStreak = 0; public ChannelImpressions() { } private float mDismissToViewRatioLimit; private int mStreakLimit; public ChannelImpressions(int dismissals, int views) { mDismissals = dismissals; mViews = views; public ChannelImpressions() { mDismissToViewRatioLimit = DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT; mStreakLimit = DEFAULT_STREAK_LIMIT; } protected ChannelImpressions(Parcel in) { mDismissals = in.readInt(); mViews = in.readInt(); mStreak = in.readInt(); mDismissToViewRatioLimit = in.readFloat(); mStreakLimit = in.readInt(); } public int getStreak() { Loading @@ -71,6 +75,21 @@ public final class ChannelImpressions implements Parcelable { mStreak++; } void updateThresholds(float dismissToViewRatioLimit, int streakLimit) { mDismissToViewRatioLimit = dismissToViewRatioLimit; mStreakLimit = streakLimit; } @VisibleForTesting float getDismissToViewRatioLimit() { return mDismissToViewRatioLimit; } @VisibleForTesting int getStreakLimit() { return mStreakLimit; } public void append(ChannelImpressions additionalImpressions) { if (additionalImpressions != null) { mViews += additionalImpressions.getViews(); Loading @@ -94,8 +113,8 @@ public final class ChannelImpressions implements Parcelable { if (DEBUG) { Log.d(TAG, "should trigger? " + getDismissals() + " " + getViews() + " " + getStreak()); } return ((double) getDismissals() / getViews()) > DISMISS_TO_VIEW_RATIO_LIMIT && getStreak() > STREAK_LIMIT; return ((float) getDismissals() / getViews()) > mDismissToViewRatioLimit && getStreak() > mStreakLimit; } @Override Loading @@ -103,6 +122,8 @@ public final class ChannelImpressions implements Parcelable { dest.writeInt(mDismissals); dest.writeInt(mViews); dest.writeInt(mStreak); dest.writeFloat(mDismissToViewRatioLimit); dest.writeInt(mStreakLimit); } @Override Loading Loading @@ -148,7 +169,9 @@ public final class ChannelImpressions implements Parcelable { sb.append("mDismissals=").append(mDismissals); sb.append(", mViews=").append(mViews); sb.append(", mStreak=").append(mStreak); sb.append('}'); sb.append(", thresholds=(").append(mDismissToViewRatioLimit); sb.append(",").append(mStreakLimit); sb.append(")}"); return sb.toString(); } Loading