VisibilityTracker.kt
TLDR
The VisibilityTracker.kt
file contains an internal class VisibilityTracker
and an extension function registerVisibilityListener
.
The VisibilityTracker
class is responsible for tracking the visibility state of activities in an Android application. It implements the Application.ActivityLifecycleCallbacks
interface and the BroadcastReceiver
class. It keeps track of the number of started activities and determines if there are any visible activities. It also listens for screen on/off events and updates the visibility state accordingly. The registerVisibilityListener
function registers the VisibilityTracker
instance as an activity lifecycle callback and a broadcast receiver for screen on/off events.
Classes
VisibilityTracker
The VisibilityTracker
class is an internal class that implements the Application.ActivityLifecycleCallbacks
interface and the BroadcastReceiver
class. Its main purpose is to track the visibility state of activities in an Android application. It keeps track of the number of started activities and determines if there are any visible activities. It also listens for screen on/off events and updates the visibility state accordingly.
END
package leakcanary.internal
import android.app.Activity
import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_SCREEN_OFF
import android.content.Intent.ACTION_SCREEN_ON
import android.content.IntentFilter
import leakcanary.internal.friendly.noOpDelegate
internal class VisibilityTracker(
private val listener: (Boolean) -> Unit
) : Application.ActivityLifecycleCallbacks by noOpDelegate(), BroadcastReceiver() {
private var startedActivityCount = 0
/**
* Visible activities are any activity started but not stopped yet. An activity can be paused
* yet visible: this will happen when another activity shows on top with a transparent background
* and the activity behind won't get touch inputs but still need to render / animate.
*/
private var hasVisibleActivities: Boolean = false
/**
* Assuming screen on by default.
*/
private var screenOn: Boolean = true
private var lastUpdate: Boolean = false
override fun onActivityStarted(activity: Activity) {
startedActivityCount++
if (!hasVisibleActivities && startedActivityCount == 1) {
hasVisibleActivities = true
updateVisible()
}
}
override fun onActivityStopped(activity: Activity) {
// This could happen if the callbacks were registered after some activities were already
// started. In that case we effectively considers those past activities as not visible.
if (startedActivityCount > 0) {
startedActivityCount--
}
if (hasVisibleActivities && startedActivityCount == 0 && !activity.isChangingConfigurations) {
hasVisibleActivities = false
updateVisible()
}
}
override fun onReceive(
context: Context,
intent: Intent
) {
screenOn = intent.action != ACTION_SCREEN_OFF
updateVisible()
}
private fun updateVisible() {
val visible = screenOn && hasVisibleActivities
if (visible != lastUpdate) {
lastUpdate = visible
listener.invoke(visible)
}
}
}
internal fun Application.registerVisibilityListener(listener: (Boolean) -> Unit) {
val visibilityTracker = VisibilityTracker(listener)
registerActivityLifecycleCallbacks(visibilityTracker)
registerReceiver(visibilityTracker, IntentFilter().apply {
addAction(ACTION_SCREEN_ON)
addAction(ACTION_SCREEN_OFF)
})
}