1 package com.android.systemui.statusbar.notification.collection.notifcollection 2 3 import android.os.Handler 4 import android.util.ArrayMap 5 import android.util.Log 6 import com.android.systemui.Dumpable 7 import com.android.systemui.statusbar.notification.collection.NotificationEntry 8 import java.io.PrintWriter 9 10 /** 11 * A helpful class that implements the core contract of the lifetime extender internally, 12 * making it easier for coordinators to interact with them 13 */ 14 abstract class SelfTrackingLifetimeExtender( 15 private val tag: String, 16 private val name: String, 17 private val debug: Boolean, 18 private val mainHandler: Handler 19 ) : NotifLifetimeExtender, Dumpable { 20 private lateinit var mCallback: NotifLifetimeExtender.OnEndLifetimeExtensionCallback 21 protected val mEntriesExtended = ArrayMap<String, NotificationEntry>() 22 private var mEnding = false 23 24 /** 25 * When debugging, warn if the call is happening during and "end lifetime extension" call. 26 * 27 * Note: this will warn a lot! The pipeline explicitly re-invokes all lifetime extenders 28 * whenever one ends, giving all of them a chance to re-up their lifetime extension. 29 */ 30 private fun warnIfEnding() { 31 if (debug && mEnding) Log.w(tag, "reentrant code while ending a lifetime extension") 32 } 33 34 fun endAllLifetimeExtensions() { 35 // clear the map before iterating over a copy of the items, because the pipeline will 36 // always give us another chance to extend the lifetime again, and we don't want 37 // concurrent modification 38 val entries = mEntriesExtended.values.toList() 39 if (debug) Log.d(tag, "$name.endAllLifetimeExtensions() entries=$entries") 40 mEntriesExtended.clear() 41 warnIfEnding() 42 mEnding = true 43 entries.forEach { mCallback.onEndLifetimeExtension(this, it) } 44 mEnding = false 45 } 46 47 fun endLifetimeExtensionAfterDelay(key: String, delayMillis: Long) { 48 if (debug) { 49 Log.d(tag, "$name.endLifetimeExtensionAfterDelay" + 50 "(key=$key, delayMillis=$delayMillis)" + 51 " isExtending=${isExtending(key)}") 52 } 53 if (isExtending(key)) { 54 mainHandler.postDelayed({ endLifetimeExtension(key) }, delayMillis) 55 } 56 } 57 58 fun endLifetimeExtension(key: String) { 59 if (debug) { 60 Log.d(tag, "$name.endLifetimeExtension(key=$key)" + 61 " isExtending=${isExtending(key)}") 62 } 63 warnIfEnding() 64 mEnding = true 65 mEntriesExtended.remove(key)?.let { removedEntry -> 66 mCallback.onEndLifetimeExtension(this, removedEntry) 67 } 68 mEnding = false 69 } 70 71 fun isExtending(key: String) = mEntriesExtended.contains(key) 72 73 final override fun getName(): String = name 74 75 final override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean { 76 val shouldExtend = queryShouldExtendLifetime(entry) 77 if (debug) { 78 Log.d(tag, "$name.shouldExtendLifetime(key=${entry.key}, reason=$reason)" + 79 " isExtending=${isExtending(entry.key)}" + 80 " shouldExtend=$shouldExtend") 81 } 82 warnIfEnding() 83 if (shouldExtend && mEntriesExtended.put(entry.key, entry) == null) { 84 onStartedLifetimeExtension(entry) 85 } 86 return shouldExtend 87 } 88 89 final override fun cancelLifetimeExtension(entry: NotificationEntry) { 90 if (debug) { 91 Log.d(tag, "$name.cancelLifetimeExtension(key=${entry.key})" + 92 " isExtending=${isExtending(entry.key)}") 93 } 94 warnIfEnding() 95 mEntriesExtended.remove(entry.key) 96 onCanceledLifetimeExtension(entry) 97 } 98 99 abstract fun queryShouldExtendLifetime(entry: NotificationEntry): Boolean 100 open fun onStartedLifetimeExtension(entry: NotificationEntry) {} 101 open fun onCanceledLifetimeExtension(entry: NotificationEntry) {} 102 103 final override fun setCallback(callback: NotifLifetimeExtender.OnEndLifetimeExtensionCallback) { 104 mCallback = callback 105 } 106 107 final override fun dump(pw: PrintWriter, args: Array<out String>) { 108 pw.println("LifetimeExtender: $name:") 109 pw.println(" mEntriesExtended: ${mEntriesExtended.size}") 110 mEntriesExtended.forEach { pw.println(" * ${it.key}") } 111 } 112 }