[go: nahoru, domu]

blob: e81e16e53d43d56983fe3ce2b2b0297245fe55ed [file] [log] [blame]
/*
* Copyright 2022 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.window.core
import android.annotation.SuppressLint
import android.app.Activity
import androidx.annotation.CheckResult
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import kotlin.reflect.KClass
import kotlin.reflect.cast
/**
* An adapter over {@link java.util.function.Consumer} to workaround mismatch in expected extension
* API signatures after library desugaring. See b/203472665
*/
@SuppressLint("BanUncheckedReflection")
internal class ConsumerAdapter(
private val loader: ClassLoader
) {
internal fun consumerClassOrNull(): Class<*>? {
return try {
unsafeConsumerClass()
} catch (e: ClassNotFoundException) {
null
}
}
private fun unsafeConsumerClass(): Class<*> {
return loader.loadClass("java.util.function.Consumer")
}
internal interface Subscription {
fun dispose()
}
private fun <T : Any> buildConsumer(clazz: KClass<T>, consumer: (T) -> Unit): Any {
val handler = ConsumerHandler(clazz, consumer)
return Proxy.newProxyInstance(loader, arrayOf(unsafeConsumerClass()), handler)
}
fun <T : Any> addConsumer(
obj: Any,
clazz: KClass<T>,
methodName: String,
consumer: (T) -> Unit
) {
obj.javaClass.getMethod(methodName, unsafeConsumerClass())
.invoke(obj, buildConsumer(clazz, consumer))
}
@CheckResult
fun <T : Any> createSubscription(
obj: Any,
clazz: KClass<T>,
addMethodName: String,
removeMethodName: String,
activity: Activity,
consumer: (T) -> Unit
): Subscription {
val javaConsumer = buildConsumer(clazz, consumer)
obj.javaClass.getMethod(addMethodName, Activity::class.java, unsafeConsumerClass())
.invoke(obj, activity, javaConsumer)
val removeMethod = obj.javaClass.getMethod(removeMethodName, unsafeConsumerClass())
return object : Subscription {
override fun dispose() {
removeMethod.invoke(obj, javaConsumer)
}
}
}
private class ConsumerHandler<T : Any>(
private val clazz: KClass<T>,
private val consumer: (T) -> Unit
) : InvocationHandler {
override fun invoke(obj: Any, method: Method, parameters: Array<out Any>?): Any {
return when {
method.isAccept(parameters) -> {
val argument = clazz.cast(parameters?.get(0))
invokeAccept(argument)
}
method.isEquals(parameters) -> {
obj === parameters?.get(0)
}
method.isHashCode(parameters) -> {
consumer.hashCode()
}
method.isToString(parameters) -> {
consumer.toString()
}
else -> {
throw UnsupportedOperationException(
"Unexpected method call object:$obj, method: $method, args: $parameters"
)
}
}
}
fun invokeAccept(parameter: T) {
consumer(parameter)
}
private fun Method.isEquals(args: Array<out Any>?): Boolean {
return name == "equals" && returnType.equals(Boolean::class.java) && args?.size == 1
}
private fun Method.isHashCode(args: Array<out Any>?): Boolean {
return name == "hashCode" && returnType.equals(Int::class.java) && args == null
}
private fun Method.isAccept(args: Array<out Any>?): Boolean {
return name == "accept" && args?.size == 1
}
private fun Method.isToString(args: Array<out Any>?): Boolean {
return name == "toString" && returnType.equals(String::class.java) && args == null
}
}
}