ContextAwareHelper.kt

/*
 * Copyright 2020 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.activity.contextaware

import android.content.Context
import java.util.concurrent.CopyOnWriteArraySet

/**
 * Helper class for implementing [ContextAware]. Classes using this helper should
 * call [addOnContextAvailableListener] and [removeOnContextAvailableListener] as the respective
 * methods of [ContextAware] are called.
 *
 * You must call [dispatchOnContextAvailable] once the
 * [Context] is available to dispatch the callbacks to all registered listeners.
 *
 * Listeners added after the context has been made available via
 * [dispatchOnContextAvailable] will have the Context synchronously
 * delivered to them up until [clearAvailableContext] is called.
 */
class ContextAwareHelper {
    private val listeners: MutableSet<OnContextAvailableListener> = CopyOnWriteArraySet()

    @Volatile
    private var context: Context? = null

    /**
     * Get the [Context] if it is currently available. If this returns
     * `null`, you can use [addOnContextAvailableListener] to receive
     * a callback for when it available.
     *
     * @return the Context if it is currently available.
     */
    fun peekAvailableContext(): Context? {
        return context
    }

    /**
     * Add a new [OnContextAvailableListener] for receiving a callback for when
     * this class is associated with a [android.content.Context].
     *
     * @param listener The listener that should be added.
     * @see removeOnContextAvailableListener
     */
    fun addOnContextAvailableListener(listener: OnContextAvailableListener) {
        context?.let {
            listener.onContextAvailable(it)
        }
        listeners.add(listener)
    }

    /**
     * Remove a [OnContextAvailableListener] previously added via
     * [addOnContextAvailableListener].
     *
     * @param listener The listener that should be removed.
     * @see addOnContextAvailableListener
     */
    fun removeOnContextAvailableListener(listener: OnContextAvailableListener) {
        listeners.remove(listener)
    }

    /**
     * Dispatch the callback of [OnContextAvailableListener.onContextAvailable] to
     * all currently added listeners in the order they were added.
     *
     * @param context The [Context] the [ContextAware] object is now associated with.
     */
    fun dispatchOnContextAvailable(context: Context) {
        this.context = context
        for (listener in listeners) {
            listener.onContextAvailable(context)
        }
    }

    /**
     * Clear any [Context] previously made available via
     * [dispatchOnContextAvailable].
     */
    fun clearAvailableContext() {
        context = null
    }
}