ViewInterop.android.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.compose.ui.node
import android.view.View
import android.view.ViewGroup
import androidx.annotation.RestrictTo
import androidx.compose.ui.util.fastFirstOrNull
import androidx.compose.ui.util.fastForEach
/**
* @suppress
*/
// TODO(b/150806128): We should decide if we want to make this public API or not. Right now it is needed
// for convenient LayoutParams usage in compose with views.
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface ViewAdapter {
val id: Int
fun willInsert(view: View, parent: ViewGroup)
fun didInsert(view: View, parent: ViewGroup)
fun didUpdate(view: View, parent: ViewGroup)
}
/**
* @suppress
*/
// TODO(b/150806128): We should decide if we want to make this public API or not. Right now it is needed
// for convenient LayoutParams usage in compose with views.
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun <T : ViewAdapter> View.getOrAddAdapter(id: Int, factory: () -> T): T {
return getViewAdapter().get(id, factory)
}
internal class MergedViewAdapter : ViewAdapter {
override val id = 0
val adapters = mutableListOf<ViewAdapter>()
inline fun <T : ViewAdapter> get(id: Int, factory: () -> T): T {
@Suppress("UNCHECKED_CAST")
val existing = adapters.fastFirstOrNull { it.id == id } as? T
if (existing != null) return existing
val next = factory()
adapters.add(next)
return next
}
override fun willInsert(view: View, parent: ViewGroup) {
adapters.fastForEach { it.willInsert(view, parent) }
}
override fun didInsert(view: View, parent: ViewGroup) {
adapters.fastForEach { it.didInsert(view, parent) }
}
override fun didUpdate(view: View, parent: ViewGroup) {
adapters.fastForEach { it.didUpdate(view, parent) }
}
}
/**
* This function will take in a string and pass back a valid resource identifier for
* View.setTag(...). We should eventually move this to a resource id that's actually generated via
* AAPT but doing that in this project is proving to be complicated, so for now I'm just doing this
* as a stop-gap.
*/
internal fun tagKey(key: String): Int {
return (3 shl 24) or key.hashCode()
}
private val viewAdaptersKey = tagKey("ViewAdapter")
internal fun View.getViewAdapterIfExists(): MergedViewAdapter? {
return getTag(viewAdaptersKey) as? MergedViewAdapter
}
internal fun View.getViewAdapter(): MergedViewAdapter {
var adapter = getTag(viewAdaptersKey) as? MergedViewAdapter
if (adapter == null) {
adapter = MergedViewAdapter()
setTag(viewAdaptersKey, adapter)
}
return adapter
}