blob: 6c9ec9d5c54e7c36de0b18ca7cc9997c633b2cb2 [file] [log] [blame]
/*
* Copyright 2021 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.layout
import android.app.Activity
import android.graphics.Rect
import androidx.window.core.ConsumerAdapter
import androidx.window.extensions.WindowExtensionsProvider
import androidx.window.extensions.layout.WindowLayoutComponent
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import kotlin.reflect.KClass
internal class SafeWindowLayoutComponentProvider(
private val loader: ClassLoader,
private val consumerAdapter: ConsumerAdapter
) {
val windowLayoutComponent: WindowLayoutComponent?
get() {
return if (canUseWindowLayoutComponent()) {
try {
WindowExtensionsProvider.getWindowExtensions().windowLayoutComponent
} catch (e: UnsupportedOperationException) {
null
}
} else {
null
}
}
private fun canUseWindowLayoutComponent(): Boolean {
return isWindowLayoutProviderValid() &&
isWindowExtensionsValid() &&
isWindowLayoutComponentValid() &&
isFoldingFeatureValid()
}
private fun isWindowLayoutProviderValid(): Boolean {
return validate {
val providerClass = windowExtensionsProviderClass
val getWindowExtensionsMethod = providerClass.getDeclaredMethod("getWindowExtensions")
val windowExtensionsClass = windowExtensionsClass
getWindowExtensionsMethod.doesReturn(windowExtensionsClass) &&
getWindowExtensionsMethod.isPublic
}
}
private fun isWindowExtensionsValid(): Boolean {
return validate {
val extensionsClass = windowExtensionsClass
val getWindowLayoutComponentMethod =
extensionsClass.getMethod("getWindowLayoutComponent")
val windowLayoutComponentClass = windowLayoutComponentClass
getWindowLayoutComponentMethod.isPublic &&
getWindowLayoutComponentMethod.doesReturn(windowLayoutComponentClass)
}
}
private fun isFoldingFeatureValid(): Boolean {
return validate {
val foldingFeatureClass = foldingFeatureClass
val getBoundsMethod = foldingFeatureClass.getMethod("getBounds")
val getTypeMethod = foldingFeatureClass.getMethod("getType")
val getStateMethod = foldingFeatureClass.getMethod("getState")
getBoundsMethod.doesReturn(Rect::class) &&
getBoundsMethod.isPublic &&
getTypeMethod.doesReturn(Int::class) &&
getTypeMethod.isPublic &&
getStateMethod.doesReturn(Int::class) &&
getStateMethod.isPublic
}
}
private fun isWindowLayoutComponentValid(): Boolean {
return validate {
val consumerClass = consumerAdapter.consumerClassOrNull() ?: return@validate false
val windowLayoutComponent = windowLayoutComponentClass
val addListenerMethod = windowLayoutComponent
.getMethod(
"addWindowLayoutInfoListener",
Activity::class.java,
consumerClass
)
val removeListenerMethod = windowLayoutComponent
.getMethod("removeWindowLayoutInfoListener", consumerClass)
addListenerMethod.isPublic && removeListenerMethod.isPublic
}
}
private fun validate(block: () -> Boolean): Boolean {
return try {
block()
} catch (noClass: ClassNotFoundException) {
false
} catch (noMethod: NoSuchMethodException) {
false
}
}
private val Method.isPublic: Boolean
get() {
return Modifier.isPublic(modifiers)
}
private fun Method.doesReturn(clazz: KClass<*>): Boolean {
return doesReturn(clazz.java)
}
private fun Method.doesReturn(clazz: Class<*>): Boolean {
return returnType.equals(clazz)
}
private val windowExtensionsProviderClass: Class<*>
get() {
return loader.loadClass("androidx.window.extensions.WindowExtensionsProvider")
}
private val windowExtensionsClass: Class<*>
get() {
return loader.loadClass("androidx.window.extensions.WindowExtensions")
}
private val foldingFeatureClass: Class<*>
get() {
return loader.loadClass("androidx.window.extensions.layout.FoldingFeature")
}
private val windowLayoutComponentClass: Class<*>
get() {
return loader.loadClass("androidx.window.extensions.layout.WindowLayoutComponent")
}
}