1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.systemui.statusbar.notification.collection.coordinator 17 18 import android.util.ArraySet 19 import com.android.systemui.Dumpable 20 import com.android.systemui.dump.DumpManager 21 import com.android.systemui.statusbar.notification.collection.ListEntry 22 import com.android.systemui.statusbar.notification.collection.NotifPipeline 23 import com.android.systemui.statusbar.notification.collection.NotificationEntry 24 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope 25 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender 26 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback 27 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener 28 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager 29 import com.android.systemui.statusbar.notification.row.NotificationGuts 30 import com.android.systemui.statusbar.notification.row.NotificationGutsManager 31 import java.io.FileDescriptor 32 import java.io.PrintWriter 33 import javax.inject.Inject 34 35 private const val TAG = "GutsCoordinator" 36 37 /** 38 * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline. 39 * Specifically, this just adds the lifetime extension necessary to keep guts from disappearing. 40 */ 41 @CoordinatorScope 42 class GutsCoordinator @Inject constructor( 43 private val notifGutsViewManager: NotifGutsViewManager, 44 private val logger: GutsCoordinatorLogger, 45 dumpManager: DumpManager 46 ) : Coordinator, Dumpable { 47 48 /** Keys of any Notifications for which we've been told the guts are open */ 49 private val notifsWithOpenGuts = ArraySet<String>() 50 51 /** Keys of any Notifications we've extended the lifetime for, based on guts */ 52 private val notifsExtendingLifetime = ArraySet<String>() 53 54 /** Callback for ending lifetime extension */ 55 private var onEndLifetimeExtensionCallback: OnEndLifetimeExtensionCallback? = null 56 57 init { 58 dumpManager.registerDumpable(TAG, this) 59 } 60 61 override fun attach(pipeline: NotifPipeline) { 62 notifGutsViewManager.setGutsListener(mGutsListener) 63 pipeline.addNotificationLifetimeExtender(mLifetimeExtender) 64 } 65 66 override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { 67 pw.println(" notifsWithOpenGuts: ${notifsWithOpenGuts.size}") 68 for (key in notifsWithOpenGuts) { 69 pw.println(" * $key") 70 } 71 pw.println(" notifsExtendingLifetime: ${notifsExtendingLifetime.size}") 72 for (key in notifsExtendingLifetime) { 73 pw.println(" * $key") 74 } 75 pw.println(" onEndLifetimeExtensionCallback: $onEndLifetimeExtensionCallback") 76 } 77 78 private val mLifetimeExtender: NotifLifetimeExtender = object : NotifLifetimeExtender { 79 override fun getName(): String { 80 return TAG 81 } 82 83 override fun setCallback(callback: OnEndLifetimeExtensionCallback) { 84 onEndLifetimeExtensionCallback = callback 85 } 86 87 override fun shouldExtendLifetime(entry: NotificationEntry, reason: Int): Boolean { 88 val isShowingGuts = isCurrentlyShowingGuts(entry) 89 if (isShowingGuts) { 90 notifsExtendingLifetime.add(entry.key) 91 } 92 return isShowingGuts 93 } 94 95 override fun cancelLifetimeExtension(entry: NotificationEntry) { 96 notifsExtendingLifetime.remove(entry.key) 97 } 98 } 99 100 private val mGutsListener: NotifGutsViewListener = object : NotifGutsViewListener { 101 override fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) { 102 logger.logGutsOpened(entry.key, guts) 103 if (guts.isLeavebehind) { 104 // leave-behind guts should not extend the lifetime of the notification 105 closeGutsAndEndLifetimeExtension(entry) 106 } else { 107 notifsWithOpenGuts.add(entry.key) 108 } 109 } 110 111 override fun onGutsClose(entry: NotificationEntry) { 112 logger.logGutsClosed(entry.key) 113 closeGutsAndEndLifetimeExtension(entry) 114 } 115 } 116 117 private fun isCurrentlyShowingGuts(entry: ListEntry) = 118 notifsWithOpenGuts.contains(entry.key) 119 120 private fun closeGutsAndEndLifetimeExtension(entry: NotificationEntry) { 121 notifsWithOpenGuts.remove(entry.key) 122 if (notifsExtendingLifetime.remove(entry.key)) { 123 onEndLifetimeExtensionCallback?.onEndLifetimeExtension(mLifetimeExtender, entry) 124 } 125 } 126 } 127