import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ForegroundServiceLifetimeExtender;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.NotificationGuts;
import com.android.systemui.statusbar.NotificationInfo;
+import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationSnooze;
import com.android.systemui.statusbar.RemoteInputController;
@Nullable private View mAmbientIndicationContainer;
private String mKeyToRemoveOnGutsClosed;
private SysuiColorExtractor mColorExtractor;
- private ForegroundServiceController mForegroundServiceController;
private ScreenLifecycle mScreenLifecycle;
@VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle;
+ protected ForegroundServiceController mForegroundServiceController;
private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
final int N = array.size();
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
+ mFGSExtender = new ForegroundServiceLifetimeExtender();
+ mFGSExtender.setCallback(key -> removeNotification(key, mLatestRankingMap));
+
mDisplay = mWindowManager.getDefaultDisplay();
updateDisplaySize();
}
Entry entry = mNotificationData.get(key);
+ if (entry != null && mFGSExtender.shouldExtendLifetime(entry)) {
+ extendLifetime(entry, mFGSExtender);
+ return;
+ }
+
if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
&& (entry.row != null && !entry.row.isDismissed())) {
mLatestRankingMap = ranking;
}
}
}
+ // Make sure no lifetime extension is happening anymore
+ cancelLifetimeExtension(entry);
setAreThereNotifications();
}
+ /** Lifetime extension keeps entries around after they would've otherwise been canceled */
+ private void extendLifetime(Entry entry, NotificationLifetimeExtender extender) {
+ // Cancel any other extender which might be holding on to this notification entry
+ NotificationLifetimeExtender activeExtender = mRetainedNotifications.get(entry);
+ if (activeExtender != null && activeExtender != extender) {
+ activeExtender.setShouldManageLifetime(entry, false);
+ }
+ mRetainedNotifications.put(entry, extender);
+ extender.setShouldManageLifetime(entry, true);
+ }
+
+ /** Tells the current extender (if any) to stop extending the entry's lifetime */
+ private void cancelLifetimeExtension(NotificationData.Entry entry) {
+ NotificationLifetimeExtender activeExtender = mRetainedNotifications.remove(entry);
+ if (activeExtender != null) {
+ activeExtender.setShouldManageLifetime(entry, false);
+ }
+ }
+
+ @VisibleForTesting
+ public Map<Entry, NotificationLifetimeExtender> getRetainedNotificationMap() {
+ return mRetainedNotifications;
+ }
+
/**
* Ensures that the group children are cancelled immediately when the group summary is cancelled
* instead of waiting for the notification manager to send all cancels. Otherwise this could
}
}
+ pw.println(" Lifetime-extended notifications:");
+ if (mRetainedNotifications.isEmpty()) {
+ pw.println(" None");
+ } else {
+ for (Map.Entry<NotificationData.Entry, NotificationLifetimeExtender> entry
+ : mRetainedNotifications.entrySet()) {
+ pw.println(" " + entry.getKey().notification + " retained by "
+ + entry.getValue().getClass().getName());
+ }
+ }
+
pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
pw.print(" mStatusBarWindowState=");
pw.println(windowStateToString(mStatusBarWindowState));
protected RemoteInputController mRemoteInputController;
+ // A lifetime extender that watches for foreground service notifications
+ @VisibleForTesting protected NotificationLifetimeExtender mFGSExtender;
+ private final Map<Entry, NotificationLifetimeExtender> mRetainedNotifications =
+ new ArrayMap<>();
+
// for heads up notifications
protected HeadsUpManager mHeadsUpManager;
Log.w(TAG, "Notification that was kept for guts was updated. " + key);
}
+ // No need to keep the lifetime extension around if an update comes in for it
+ cancelLifetimeExtension(entry);
+
Notification n = notification.getNotification();
mNotificationData.updateRanking(ranking);