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.EntryAdapter 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.PipelineEntry 25 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope 26 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender 27 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback 28 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener 29 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager 30 import com.android.systemui.statusbar.notification.row.NotificationGuts 31 import com.android.systemui.statusbar.notification.row.NotificationGutsManager 32 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi 33 import com.android.systemui.util.asIndenting 34 import com.android.systemui.util.printCollection 35 import com.android.systemui.util.println 36 import com.android.systemui.util.withIncreasedIndent 37 import java.io.PrintWriter 38 import javax.inject.Inject 39 40 private const val TAG = "GutsCoordinator" 41 42 /** 43 * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline. Specifically, 44 * this just adds the lifetime extension necessary to keep guts from disappearing. 45 */ 46 @CoordinatorScope 47 class GutsCoordinator 48 @Inject 49 constructor( 50 private val notifGutsViewManager: NotifGutsViewManager, 51 private val logger: GutsCoordinatorLogger, 52 dumpManager: DumpManager, 53 ) : Coordinator, Dumpable { 54 55 /** Keys of any Notifications for which we've been told the guts are open */ 56 private val notifsWithOpenGuts = ArraySet<String>() 57 58 /** Keys of any Notifications we've extended the lifetime for, based on guts */ 59 private val notifsExtendingLifetime = ArraySet<String>() 60 61 /** Callback for ending lifetime extension */ 62 private var onEndLifetimeExtensionCallback: OnEndLifetimeExtensionCallback? = null 63 64 init { 65 dumpManager.registerDumpable(this) 66 } 67 attachnull68 override fun attach(pipeline: NotifPipeline) { 69 notifGutsViewManager.setGutsListener(mGutsListener) 70 pipeline.addNotificationLifetimeExtender(mLifetimeExtender) 71 } 72 dumpnull73 override fun dump(pw: PrintWriter, args: Array<String>) = 74 pw.asIndenting().run { 75 withIncreasedIndent { 76 printCollection("notifsWithOpenGuts", notifsWithOpenGuts) 77 printCollection("notifsExtendingLifetime", notifsExtendingLifetime) 78 println("onEndLifetimeExtensionCallback", onEndLifetimeExtensionCallback) 79 } 80 } 81 82 private val mLifetimeExtender: NotifLifetimeExtender = 83 object : NotifLifetimeExtender { getNamenull84 override fun getName(): String { 85 return TAG 86 } 87 setCallbacknull88 override fun setCallback(callback: OnEndLifetimeExtensionCallback) { 89 onEndLifetimeExtensionCallback = callback 90 } 91 maybeExtendLifetimenull92 override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean { 93 val isShowingGuts = isCurrentlyShowingGuts(entry) 94 if (isShowingGuts) { 95 notifsExtendingLifetime.add(entry.key) 96 } 97 return isShowingGuts 98 } 99 cancelLifetimeExtensionnull100 override fun cancelLifetimeExtension(entry: NotificationEntry) { 101 notifsExtendingLifetime.remove(entry.key) 102 } 103 } 104 105 private val mGutsListener: NotifGutsViewListener = 106 object : NotifGutsViewListener { onGutsOpennull107 override fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) { 108 NotificationBundleUi.assertInLegacyMode() 109 logger.logGutsOpened(entry.key, guts) 110 if (guts.isLeavebehind) { 111 // leave-behind guts should not extend the lifetime of the notification 112 closeGutsAndEndLifetimeExtension(entry) 113 } else { 114 notifsWithOpenGuts.add(entry.key) 115 } 116 } 117 onGutsClosenull118 override fun onGutsClose(entry: NotificationEntry) { 119 NotificationBundleUi.assertInLegacyMode() 120 logger.logGutsClosed(entry.key) 121 closeGutsAndEndLifetimeExtension(entry) 122 } 123 onGutsOpennull124 override fun onGutsOpen(entryAdapter: EntryAdapter, guts: NotificationGuts) { 125 NotificationBundleUi.isUnexpectedlyInLegacyMode() 126 logger.logGutsOpened(entryAdapter.key, guts) 127 if (guts.isLeavebehind) { 128 // leave-behind guts should not extend the lifetime of the notification 129 closeGutsAndEndLifetimeExtension(entryAdapter) 130 } else { 131 notifsWithOpenGuts.add(entryAdapter.key) 132 } 133 } 134 onGutsClosenull135 override fun onGutsClose(entryAdapter: EntryAdapter) { 136 NotificationBundleUi.isUnexpectedlyInLegacyMode() 137 logger.logGutsClosed(entryAdapter.key) 138 closeGutsAndEndLifetimeExtension(entryAdapter) 139 } 140 } 141 isCurrentlyShowingGutsnull142 private fun isCurrentlyShowingGuts(entry: PipelineEntry) = 143 notifsWithOpenGuts.contains(entry.key) 144 145 private fun closeGutsAndEndLifetimeExtension(entry: NotificationEntry) { 146 notifsWithOpenGuts.remove(entry.key) 147 if (notifsExtendingLifetime.remove(entry.key)) { 148 onEndLifetimeExtensionCallback?.onEndLifetimeExtension(mLifetimeExtender, entry) 149 } 150 } 151 closeGutsAndEndLifetimeExtensionnull152 private fun closeGutsAndEndLifetimeExtension(entryAdapter: EntryAdapter) { 153 notifsWithOpenGuts.remove(entryAdapter.key) 154 if (notifsExtendingLifetime.remove(entryAdapter.key)) { 155 entryAdapter.endLifetimeExtension(onEndLifetimeExtensionCallback, mLifetimeExtender) 156 } 157 } 158 } 159