Merge "Add ability to delete individual history items"
This commit is contained in:
committed by
Android (Google) Code Review
commit
6ae380d699
@@ -113,6 +113,7 @@ interface INotificationManager
|
||||
int getAppsBypassingDndCount(int uid);
|
||||
ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
|
||||
boolean isPackagePaused(String pkg);
|
||||
void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
|
||||
|
||||
void silenceNotificationSound();
|
||||
|
||||
|
||||
@@ -345,6 +345,26 @@ public final class NotificationHistory implements Parcelable {
|
||||
poolStringsFromNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an individual historical notification and regenerates the string pool
|
||||
*/
|
||||
public boolean removeNotificationFromWrite(String packageName, long postedTime) {
|
||||
boolean removed = false;
|
||||
for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) {
|
||||
HistoricalNotification hn = mNotificationsToWrite.get(i);
|
||||
if (packageName.equals(hn.getPackage())
|
||||
&& postedTime == hn.getPostedTimeMs()) {
|
||||
removed = true;
|
||||
mNotificationsToWrite.remove(i);
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
poolStringsFromNotifications();
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pooled strings in order to write them to disk
|
||||
*/
|
||||
|
||||
@@ -233,6 +233,40 @@ public class NotificationHistoryTest {
|
||||
.containsExactlyElementsIn(postRemoveExpectedEntries);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNotificationFromWrite() {
|
||||
NotificationHistory history = new NotificationHistory();
|
||||
|
||||
List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>();
|
||||
List<String> postRemoveExpectedStrings = new ArrayList<>();
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
HistoricalNotification n = getHistoricalNotification("pkg", i);
|
||||
|
||||
if (987654323 != n.getPostedTimeMs()) {
|
||||
postRemoveExpectedStrings.add(n.getPackage());
|
||||
postRemoveExpectedStrings.add(n.getChannelName());
|
||||
postRemoveExpectedStrings.add(n.getChannelId());
|
||||
postRemoveExpectedEntries.add(n);
|
||||
}
|
||||
|
||||
history.addNotificationToWrite(n);
|
||||
}
|
||||
|
||||
history.poolStringsFromNotifications();
|
||||
|
||||
assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
|
||||
// 1 package name and 10 unique channel names and ids
|
||||
assertThat(history.getPooledStringsToWrite().length).isEqualTo(21);
|
||||
|
||||
history.removeNotificationFromWrite("pkg", 987654323);
|
||||
|
||||
|
||||
// 1 package names and 9 * 2 unique channel names and ids
|
||||
assertThat(history.getPooledStringsToWrite().length).isEqualTo(19);
|
||||
assertThat(history.getNotificationsToWrite())
|
||||
.containsExactlyElementsIn(postRemoveExpectedEntries);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParceling() {
|
||||
NotificationHistory history = new NotificationHistory();
|
||||
|
||||
@@ -170,6 +170,11 @@ public class NotificationHistoryDatabase {
|
||||
mFileWriteHandler.post(rpr);
|
||||
}
|
||||
|
||||
public void deleteNotificationHistoryItem(String pkg, long postedTime) {
|
||||
RemoveNotificationRunnable rnr = new RemoveNotificationRunnable(pkg, postedTime);
|
||||
mFileWriteHandler.post(rnr);
|
||||
}
|
||||
|
||||
public void addNotification(final HistoricalNotification notification) {
|
||||
synchronized (mLock) {
|
||||
mBuffer.addNewNotificationToWrite(notification);
|
||||
@@ -367,6 +372,49 @@ public class NotificationHistoryDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
final class RemoveNotificationRunnable implements Runnable {
|
||||
private String mPkg;
|
||||
private long mPostedTime;
|
||||
private NotificationHistory mNotificationHistory;
|
||||
|
||||
public RemoveNotificationRunnable(String pkg, long postedTime) {
|
||||
mPkg = pkg;
|
||||
mPostedTime = postedTime;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setNotificationHistory(NotificationHistory nh) {
|
||||
mNotificationHistory = nh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (DEBUG) Slog.d(TAG, "RemovePackageRunnable");
|
||||
synchronized (mLock) {
|
||||
// Remove from pending history
|
||||
mBuffer.removeNotificationFromWrite(mPkg, mPostedTime);
|
||||
|
||||
Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator();
|
||||
while (historyFileItr.hasNext()) {
|
||||
final AtomicFile af = historyFileItr.next();
|
||||
try {
|
||||
NotificationHistory notificationHistory = mNotificationHistory != null
|
||||
? mNotificationHistory
|
||||
: new NotificationHistory();
|
||||
readLocked(af, notificationHistory,
|
||||
new NotificationHistoryFilter.Builder().build());
|
||||
if(notificationHistory.removeNotificationFromWrite(mPkg, mPostedTime)) {
|
||||
writeLocked(af, notificationHistory);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Cannot clean up file on notification removal "
|
||||
+ af.getBaseFile().getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class NotificationHistoryFileAttrProvider implements
|
||||
NotificationHistoryDatabase.FileAttrProvider {
|
||||
final static String TAG = "NotifHistoryFileDate";
|
||||
|
||||
@@ -130,7 +130,7 @@ public class NotificationHistoryManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void onPackageRemoved(int userId, String packageName) {
|
||||
public void onPackageRemoved(@UserIdInt int userId, String packageName) {
|
||||
synchronized (mLock) {
|
||||
if (!mUserUnlockedStates.get(userId, false)) {
|
||||
if (mHistoryEnabled.get(userId, false)) {
|
||||
@@ -150,6 +150,22 @@ public class NotificationHistoryManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
|
||||
synchronized (mLock) {
|
||||
int userId = UserHandle.getUserId(uid);
|
||||
final NotificationHistoryDatabase userHistory =
|
||||
getUserHistoryAndInitializeIfNeededLocked(userId);
|
||||
// TODO: it shouldn't be possible to delete a notification entry while the user is
|
||||
// locked but we should handle it
|
||||
if (userHistory == null) {
|
||||
Slog.w(TAG, "Attempted to remove notif for locked/gone/disabled user "
|
||||
+ userId);
|
||||
return;
|
||||
}
|
||||
userHistory.deleteNotificationHistoryItem(pkg, postedTime);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: wire this up to AMS when power button is long pressed
|
||||
public void triggerWriteToDisk() {
|
||||
synchronized (mLock) {
|
||||
|
||||
@@ -2780,7 +2780,7 @@ public class NotificationManagerService extends SystemService {
|
||||
if (!isSystemToast) {
|
||||
int count = 0;
|
||||
final int N = mToastQueue.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
final ToastRecord r = mToastQueue.get(i);
|
||||
if (r.pkg.equals(pkg)) {
|
||||
count++;
|
||||
@@ -2820,7 +2820,7 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
if (pkg == null || token == null) {
|
||||
Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " token=" + token);
|
||||
return ;
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mToastQueue) {
|
||||
@@ -2933,14 +2933,14 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
/**
|
||||
* 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 means
|
||||
* Additionally, this method marks the app importance as locked by the user, which
|
||||
* means
|
||||
* that notifications from the app will <b>not</b> be considered for showing a
|
||||
* blocking helper.
|
||||
*
|
||||
* @param pkg package that owns the notifications to update
|
||||
* @param uid uid of the app providing notifications
|
||||
* @param pkg package that owns the notifications to update
|
||||
* @param uid uid of the app providing notifications
|
||||
* @param enabled whether notifications should be enabled for the app
|
||||
*
|
||||
* @see #setNotificationsEnabledForPackage(String, int, boolean)
|
||||
*/
|
||||
@Override
|
||||
@@ -3030,6 +3030,12 @@ public class NotificationManagerService extends SystemService {
|
||||
mListeners.onStatusBarIconsBehaviorChanged(hide);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
|
||||
checkCallerIsSystem();
|
||||
mHistoryManager.deleteNotificationHistoryItem(pkg, uid, postedTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPackageImportance(String pkg) {
|
||||
checkCallerIsSystemOrSameApp(pkg);
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.internal.matchers.Not;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@@ -239,6 +240,52 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
|
||||
verify(af2, never()).openRead();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNotificationRunnable() throws Exception {
|
||||
NotificationHistory nh = mock(NotificationHistory.class);
|
||||
NotificationHistoryDatabase.RemoveNotificationRunnable rnr =
|
||||
mDataBase.new RemoveNotificationRunnable("pkg", 123);
|
||||
rnr.setNotificationHistory(nh);
|
||||
|
||||
AtomicFile af = mock(AtomicFile.class);
|
||||
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
|
||||
mDataBase.mHistoryFiles.addLast(af);
|
||||
|
||||
when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(true);
|
||||
|
||||
mDataBase.mBuffer = mock(NotificationHistory.class);
|
||||
|
||||
rnr.run();
|
||||
|
||||
verify(mDataBase.mBuffer).removeNotificationFromWrite("pkg", 123);
|
||||
verify(af).openRead();
|
||||
verify(nh).removeNotificationFromWrite("pkg", 123);
|
||||
verify(af).startWrite();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNotificationRunnable_noChanges() throws Exception {
|
||||
NotificationHistory nh = mock(NotificationHistory.class);
|
||||
NotificationHistoryDatabase.RemoveNotificationRunnable rnr =
|
||||
mDataBase.new RemoveNotificationRunnable("pkg", 123);
|
||||
rnr.setNotificationHistory(nh);
|
||||
|
||||
AtomicFile af = mock(AtomicFile.class);
|
||||
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
|
||||
mDataBase.mHistoryFiles.addLast(af);
|
||||
|
||||
when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(false);
|
||||
|
||||
mDataBase.mBuffer = mock(NotificationHistory.class);
|
||||
|
||||
rnr.run();
|
||||
|
||||
verify(mDataBase.mBuffer).removeNotificationFromWrite("pkg", 123);
|
||||
verify(af).openRead();
|
||||
verify(nh).removeNotificationFromWrite("pkg", 123);
|
||||
verify(af, never()).startWrite();
|
||||
}
|
||||
|
||||
private class TestFileAttrProvider implements NotificationHistoryDatabase.FileAttrProvider {
|
||||
public Map<File, Long> creationDates = new HashMap<>();
|
||||
|
||||
|
||||
@@ -288,6 +288,20 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase {
|
||||
verify(userHistoryAll, never()).onPackageRemoved(pkg);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteNotificationHistoryItem_userUnlocked() {
|
||||
String pkg = "pkg";
|
||||
long time = 235;
|
||||
NotificationHistoryDatabase userHistory = mock(NotificationHistoryDatabase.class);
|
||||
|
||||
mHistoryManager.onUserUnlocked(USER_SYSTEM);
|
||||
mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistory);
|
||||
|
||||
mHistoryManager.deleteNotificationHistoryItem(pkg, 1, time);
|
||||
|
||||
verify(userHistory, times(1)).deleteNotificationHistoryItem(pkg, time);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTriggerWriteToDisk() {
|
||||
NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
|
||||
|
||||
Reference in New Issue
Block a user