blob: be22bd71cdeb591de03fbd011a6d953b647fc601 [file] [log] [blame]
/*
* 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.compose.ui.platform
import android.content.ComponentCallbacks2
import android.content.Context
import android.content.res.Configuration
import android.view.View
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.LocalSaveableStateRegistry
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.res.ImageVectorCache
import androidx.compose.ui.res.ResourceIdCache
import androidx.lifecycle.LifecycleOwner
import androidx.savedstate.SavedStateRegistryOwner
/**
* The Android [Configuration]. The [Configuration] is useful for determining how to organize the
* UI.
*/
val LocalConfiguration = compositionLocalOf<Configuration> {
noLocalProvidedFor("LocalConfiguration")
}
/**
* Provides a [Context] that can be used by Android applications.
*/
val LocalContext = staticCompositionLocalOf<Context> {
noLocalProvidedFor("LocalContext")
}
internal val LocalImageVectorCache = staticCompositionLocalOf<ImageVectorCache> {
noLocalProvidedFor("LocalImageVectorCache")
}
internal val LocalResourceIdCache = staticCompositionLocalOf<ResourceIdCache> {
noLocalProvidedFor("LocalResourceIdCache")
}
/**
* The CompositionLocal containing the current [LifecycleOwner].
*/
val LocalLifecycleOwner = staticCompositionLocalOf<LifecycleOwner> {
noLocalProvidedFor("LocalLifecycleOwner")
}
/**
* The CompositionLocal containing the current [SavedStateRegistryOwner].
*/
val LocalSavedStateRegistryOwner = staticCompositionLocalOf<SavedStateRegistryOwner> {
noLocalProvidedFor("LocalSavedStateRegistryOwner")
}
/**
* The CompositionLocal containing the current Compose [View].
*/
val LocalView = staticCompositionLocalOf<View> {
noLocalProvidedFor("LocalView")
}
@Composable
@OptIn(ExperimentalComposeUiApi::class)
internal fun ProvideAndroidCompositionLocals(
owner: AndroidComposeView,
content: @Composable () -> Unit
) {
val view = owner
val context = view.context
// Make a deep copy to compare to later, since the same configuration object will be mutated
// as part of configuration changes
var configuration by remember {
mutableStateOf(Configuration(context.resources.configuration))
}
owner.configurationChangeObserver = { configuration = Configuration(it) }
val uriHandler = remember { AndroidUriHandler(context) }
val viewTreeOwners = owner.viewTreeOwners ?: throw IllegalStateException(
"Called when the ViewTreeOwnersAvailability is not yet in Available state"
)
val saveableStateRegistry = remember {
DisposableSaveableStateRegistry(view, viewTreeOwners.savedStateRegistryOwner)
}
DisposableEffect(Unit) {
onDispose {
saveableStateRegistry.dispose()
}
}
val imageVectorCache = obtainImageVectorCache(context, configuration)
val resourceIdCache = obtainResourceIdCache(context)
CompositionLocalProvider(
LocalConfiguration provides configuration,
LocalContext provides context,
LocalLifecycleOwner provides viewTreeOwners.lifecycleOwner,
LocalSavedStateRegistryOwner provides viewTreeOwners.savedStateRegistryOwner,
LocalSaveableStateRegistry provides saveableStateRegistry,
LocalView provides owner.view,
LocalImageVectorCache provides imageVectorCache,
LocalResourceIdCache provides resourceIdCache
) {
ProvideCommonCompositionLocals(
owner = owner,
uriHandler = uriHandler,
content = content
)
}
}
@Stable
@Composable
private fun obtainResourceIdCache(context: Context): ResourceIdCache {
val resourceIdCache = remember { ResourceIdCache() }
val callbacks = remember {
object : ComponentCallbacks2 {
override fun onConfigurationChanged(newConfig: Configuration) {
resourceIdCache.clear()
}
override fun onLowMemory() {
resourceIdCache.clear()
}
override fun onTrimMemory(level: Int) {
resourceIdCache.clear()
}
}
}
DisposableEffect(resourceIdCache) {
context.applicationContext.registerComponentCallbacks(callbacks)
onDispose {
context.applicationContext.unregisterComponentCallbacks(callbacks)
}
}
return resourceIdCache
}
@Stable
@Composable
private fun obtainImageVectorCache(
context: Context,
configuration: Configuration?
): ImageVectorCache {
val imageVectorCache = remember { ImageVectorCache() }
val currentConfiguration: Configuration = remember {
Configuration().apply { configuration?.let { this.setTo(it) } }
}
val callbacks = remember {
object : ComponentCallbacks2 {
override fun onConfigurationChanged(configuration: Configuration) {
val changedFlags = currentConfiguration.updateFrom(configuration)
imageVectorCache.prune(changedFlags)
currentConfiguration.setTo(configuration)
}
override fun onLowMemory() {
imageVectorCache.clear()
}
override fun onTrimMemory(level: Int) {
imageVectorCache.clear()
}
}
}
DisposableEffect(imageVectorCache) {
context.applicationContext.registerComponentCallbacks(callbacks)
onDispose {
context.applicationContext.unregisterComponentCallbacks(callbacks)
}
}
return imageVectorCache
}
private fun noLocalProvidedFor(name: String): Nothing {
error("CompositionLocal $name not present")
}