ProcessLifecycleOwner.kt

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.lifecycle

import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.os.Handler
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.ReportFragment.Companion.reportFragment

/**
 * Class that provides lifecycle for the whole application process.
 *
 * You can consider this LifecycleOwner as the composite of all of your Activities, except that
 * [Lifecycle.Event.ON_CREATE] will be dispatched once and [Lifecycle.Event.ON_DESTROY]
 * will never be dispatched. Other lifecycle events will be dispatched with following rules:
 * ProcessLifecycleOwner will dispatch [Lifecycle.Event.ON_START],
 * [Lifecycle.Event.ON_RESUME] events, as a first activity moves through these events.
 * [Lifecycle.Event.ON_PAUSE], [Lifecycle.Event.ON_STOP], events will be dispatched with
 * a **delay** after a last activity
 * passed through them. This delay is long enough to guarantee that ProcessLifecycleOwner
 * won't send any events if activities are destroyed and recreated due to a
 * configuration change.
 *
 * It is useful for use cases where you would like to react on your app coming to the foreground or
 * going to the background and you don't need a milliseconds accuracy in receiving lifecycle
 * events.
 */
class ProcessLifecycleOwner private constructor() : LifecycleOwner {
    // ground truth counters
    private var startedCounter = 0
    private var resumedCounter = 0
    private var pauseSent = true
    private var stopSent = true
    private var handler: Handler? = null
    private val registry = LifecycleRegistry(this)
    private val delayedPauseRunnable = Runnable {
        dispatchPauseIfNeeded()
        dispatchStopIfNeeded()
    }
    private val initializationListener: ReportFragment.ActivityInitializationListener =
        object : ReportFragment.ActivityInitializationListener {
            override fun onCreate() {}

            override fun onStart() {
                activityStarted()
            }

            override fun onResume() {
                activityResumed()
            }
        }

    companion object {
        @VisibleForTesting
        internal const val TIMEOUT_MS: Long = 700 // mls
        private val newInstance = ProcessLifecycleOwner()

        /**
         * The LifecycleOwner for the whole application process. Note that if your application
         * has multiple processes, this provider does not know about other processes.
         *
         * @return [LifecycleOwner] for the whole application.
         */
        @JvmStatic
        fun get(): LifecycleOwner {
            return newInstance
        }

        @JvmStatic
        internal fun init(context: Context) {
            newInstance.attach(context)
        }
    }

    internal fun activityStarted() {
        startedCounter++
        if (startedCounter == 1 && stopSent) {
            registry.handleLifecycleEvent(Lifecycle.Event.ON_START)
            stopSent = false
        }
    }

    internal fun activityResumed() {
        resumedCounter++
        if (resumedCounter == 1) {
            if (pauseSent) {
                registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
                pauseSent = false
            } else {
                handler!!.removeCallbacks(delayedPauseRunnable)
            }
        }
    }

    internal fun activityPaused() {
        resumedCounter--
        if (resumedCounter == 0) {
            handler!!.postDelayed(delayedPauseRunnable, TIMEOUT_MS)
        }
    }

    internal fun activityStopped() {
        startedCounter--
        dispatchStopIfNeeded()
    }

    internal fun dispatchPauseIfNeeded() {
        if (resumedCounter == 0) {
            pauseSent = true
            registry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        }
    }

    internal fun dispatchStopIfNeeded() {
        if (startedCounter == 0 && pauseSent) {
            registry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
            stopSent = true
        }
    }

    @Suppress("DEPRECATION")
    internal fun attach(context: Context) {
        handler = Handler()
        registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
        val app = context.applicationContext as Application
        app.registerActivityLifecycleCallbacks(object : EmptyActivityLifecycleCallbacks() {
            @RequiresApi(29)
            override fun onActivityPreCreated(
                activity: Activity,
                savedInstanceState: Bundle?
            ) {
                // We need the ProcessLifecycleOwner to get ON_START and ON_RESUME precisely
                // before the first activity gets its LifecycleOwner started/resumed.
                // The activity's LifecycleOwner gets started/resumed via an activity registered
                // callback added in onCreate(). By adding our own activity registered callback in
                // onActivityPreCreated(), we get our callbacks first while still having the
                // right relative order compared to the Activity's onStart()/onResume() callbacks.
                Api29Impl.registerActivityLifecycleCallbacks(activity,
                    object : EmptyActivityLifecycleCallbacks() {
                        override fun onActivityPostStarted(activity: Activity) {
                            activityStarted()
                        }

                        override fun onActivityPostResumed(activity: Activity) {
                            activityResumed()
                        }
                    })
            }

            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                // Only use ReportFragment pre API 29 - after that, we can use the
                // onActivityPostStarted and onActivityPostResumed callbacks registered in
                // onActivityPreCreated()
                if (Build.VERSION.SDK_INT < 29) {
                    activity.reportFragment.setProcessListener(initializationListener)
                }
            }

            override fun onActivityPaused(activity: Activity) {
                activityPaused()
            }

            override fun onActivityStopped(activity: Activity) {
                activityStopped()
            }
        })
    }

    override fun getLifecycle(): Lifecycle {
        return registry
    }

    @RequiresApi(29)
    internal object Api29Impl {
        @DoNotInline
        @JvmStatic
        fun registerActivityLifecycleCallbacks(
            activity: Activity,
            callback: Application.ActivityLifecycleCallbacks
        ) {
            activity.registerActivityLifecycleCallbacks(callback)
        }
    }
}