Merge "Let NoMan know when something is no longer a bubble" into qt-dev

This commit is contained in:
Mady Mellor
2019-04-23 21:07:05 +00:00
committed by Android (Google) Code Review
6 changed files with 170 additions and 17 deletions

View File

@@ -76,6 +76,7 @@ interface IStatusBarService
in int notificationLocation, boolean modifiedBeforeSending);
void onNotificationSettingsViewed(String key);
void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
void onNotificationBubbleChanged(String key, boolean isBubble);
void onGlobalActionsShown();
void onGlobalActionsHidden();

View File

@@ -32,7 +32,6 @@ import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -52,6 +51,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.MainThread;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -131,8 +131,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private StatusBarStateListener mStatusBarStateListener;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private INotificationManager mNotificationManagerService;
private IStatusBarService mBarService;
// Used for determining view rect for touch interaction
private Rect mTempRect = new Rect();
@@ -207,13 +206,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
try {
mNotificationManagerService = INotificationManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
} catch (ServiceManager.ServiceNotFoundException e) {
e.printStackTrace();
}
mStatusBarWindowController = statusBarWindowController;
mStatusBarStateListener = new StatusBarStateListener();
Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
@@ -231,6 +223,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
mSurfaceSynchronizer = synchronizer;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
/**
@@ -462,6 +457,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (mStackView != null) {
mStackView.removeBubble(bubble);
}
if (!bubble.entry.showInShadeWhenBubble()) {
// The notification is gone & bubble is gone, time to actually remove it
mNotificationEntryManager.performRemoveNotification(bubble.entry.notification);
} else {
// The notification is still in the shade but we've removed the bubble so
// lets make sure NoMan knows it's not a bubble anymore
try {
mBarService.onNotificationBubbleChanged(bubble.getKey(), false /* isBubble */);
} catch (RemoteException e) {
// Bad things have happened
}
}
}
public void onBubbleUpdated(Bubble bubble) {

View File

@@ -46,6 +46,7 @@ public interface NotificationDelegate {
int notificationLocation);
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
void onNotificationBubbleChanged(String key, boolean isBubble);
/**
* Notifies that smart replies and actions have been added to the UI.

View File

@@ -1020,6 +1020,24 @@ public class NotificationManagerService extends SystemService {
}
}
}
@Override
public void onNotificationBubbleChanged(String key, boolean isBubble) {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
final StatusBarNotification n = r.sbn;
final int callingUid = n.getUid();
final String pkg = n.getPackageName();
if (isBubble && isNotificationAppropriateToBubble(r, pkg, callingUid,
null /* oldEntry */)) {
r.getNotification().flags |= FLAG_BUBBLE;
} else {
r.getNotification().flags &= ~FLAG_BUBBLE;
}
}
}
}
};
@VisibleForTesting
@@ -4782,6 +4800,19 @@ public class NotificationManagerService extends SystemService {
private void flagNotificationForBubbles(NotificationRecord r, String pkg, int userId,
NotificationRecord oldRecord) {
Notification notification = r.getNotification();
if (isNotificationAppropriateToBubble(r, pkg, userId, oldRecord)) {
notification.flags |= FLAG_BUBBLE;
} else {
notification.flags &= ~FLAG_BUBBLE;
}
}
/**
* @return whether the provided notification record is allowed to be represented as a bubble.
*/
private boolean isNotificationAppropriateToBubble(NotificationRecord r, String pkg, int userId,
NotificationRecord oldRecord) {
Notification notification = r.getNotification();
// Does the app want to bubble & have permission to bubble?
boolean canBubble = notification.getBubbleMetadata() != null
@@ -4807,12 +4838,7 @@ public class NotificationManagerService extends SystemService {
// OR something that was previously a bubble & still exists
boolean bubbleUpdate = oldRecord != null
&& (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0;
if (canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate)) {
notification.flags |= FLAG_BUBBLE;
} else {
notification.flags &= ~FLAG_BUBBLE;
}
return canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate);
}
private void doChannelWarningToast(CharSequence toastText) {

View File

@@ -1313,6 +1313,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
}
@Override
public void onNotificationBubbleChanged(String key, boolean isBubble) {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationBubbleChanged(key, isBubble);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {

View File

@@ -71,7 +71,6 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
@@ -5047,4 +5046,112 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.areBubblesAllowedForPackage(mContext.getPackageName(),
mUid + UserHandle.PER_USER_RANGE);
}
@Test
public void testNotificationBubbleChanged_false() throws Exception {
// Bubbles are allowed!
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
when(mPreferencesHelper.getNotificationChannel(
anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
mTestNotificationChannel);
when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
mTestNotificationChannel.getImportance());
// Notif with bubble metadata but not our other misc requirements
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
null /* tvExtender */, true /* isBubble */);
// Say we're foreground
when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
IMPORTANCE_FOREGROUND);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
// First we were a bubble
StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsBefore.length);
assertTrue((notifsBefore[0].getNotification().flags & FLAG_BUBBLE) != 0);
// Notify we're not a bubble
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false);
waitForIdle();
// Now we are not a bubble
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
}
@Test
public void testNotificationBubbleChanged_true() throws Exception {
// Bubbles are allowed!
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
when(mPreferencesHelper.getNotificationChannel(
anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
mTestNotificationChannel);
when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
mTestNotificationChannel.getImportance());
// Plain notification that has bubble metadata
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
null /* tvExtender */, true /* isBubble */);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
// Would be a normal notification because wouldn't have met requirements to bubble
StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsBefore.length);
assertEquals((notifsBefore[0].getNotification().flags & FLAG_BUBBLE), 0);
// Make the package foreground so that we're allowed to be a bubble
when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
IMPORTANCE_FOREGROUND);
// Notify we are now a bubble
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true);
waitForIdle();
// Make sure we are a bubble
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
assertTrue((notifsAfter[0].getNotification().flags & FLAG_BUBBLE) != 0);
}
@Test
public void testNotificationBubbleChanged_true_notAllowed() throws Exception {
// Bubbles are allowed!
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
when(mPreferencesHelper.getNotificationChannel(
anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
mTestNotificationChannel);
when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
mTestNotificationChannel.getImportance());
// Notif that is not a bubble
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
null /* tvExtender */, true /* isBubble */);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
// Would be a normal notification because wouldn't have met requirements to bubble
StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsBefore.length);
assertEquals((notifsBefore[0].getNotification().flags & FLAG_BUBBLE), 0);
// Notify we are now a bubble
mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true);
waitForIdle();
// We still wouldn't be a bubble because the notification didn't meet requirements
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
}
}