1 /* <lambda>null2 * Copyright 2025 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.appfunctions.internal 18 19 import android.content.Context 20 import android.util.Log 21 import androidx.annotation.RestrictTo 22 import androidx.appfunctions.AppFunctionConfiguration 23 import androidx.appfunctions.internal.Constants.APP_FUNCTIONS_TAG 24 import java.lang.reflect.InvocationTargetException 25 26 /** 27 * A factory that will incorporate [AppFunctionConfiguration] from [context] to create AppFunction 28 * enclosing classes. 29 * 30 * If the application context from [context] overrides [AppFunctionConfiguration.Provider], the 31 * customized factory method will be used to instantiate the enclosing class. Otherwise, it will use 32 * reflection to create the instance assuming the enclosing class has a no argument constructor. 33 * 34 * [createEnclosingClass] will throw [AppFunctionInstantiationException] if unable to instantiate 35 * the enclosing class. 36 */ 37 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 38 public class ConfigurableAppFunctionFactory<T : Any>( 39 private val context: Context, 40 ) { 41 public fun createEnclosingClass(enclosingClass: Class<T>): T { 42 val configurationProvider = context.applicationContext as? AppFunctionConfiguration.Provider 43 val customFactory = 44 configurationProvider 45 ?.appFunctionConfiguration 46 ?.enclosingClassFactories 47 ?.get(enclosingClass) 48 if (customFactory == null) { 49 Log.d(APP_FUNCTIONS_TAG, "Unable to find custom factory for [$enclosingClass]") 50 return getNoArgumentAppFunctionFactory<T>().invoke(enclosingClass) 51 } 52 53 val instance = customFactory.invoke() 54 @Suppress("UNCHECKED_CAST") return instance as T 55 } 56 57 /** Thrown when unable to instantiate the AppFunction enclosing class. */ 58 public class AppFunctionInstantiationException(errorMessage: String) : 59 RuntimeException(errorMessage) 60 61 private fun <T : Any> getNoArgumentAppFunctionFactory(): (Class<T>) -> T { 62 return { enclosingClass: Class<T> -> 63 try { 64 enclosingClass.getDeclaredConstructor().newInstance() 65 } catch (_: IllegalAccessException) { 66 throw AppFunctionInstantiationException( 67 "Cannot access the constructor of $enclosingClass" 68 ) 69 } catch (_: NoSuchMethodException) { 70 throw AppFunctionInstantiationException( 71 "$enclosingClass requires additional parameter to create. " + 72 "Please either remove the additional parameters or implement the " + 73 "factory and provide it in " + 74 "${AppFunctionConfiguration::class.qualifiedName}", 75 ) 76 } catch (_: InstantiationException) { 77 throw AppFunctionInstantiationException( 78 "$enclosingClass should have a public no-argument constructor" 79 ) 80 } catch (_: InvocationTargetException) { 81 throw AppFunctionInstantiationException( 82 "Something went wrong when creating $enclosingClass" 83 ) 84 } catch (_: ExceptionInInitializerError) { 85 throw AppFunctionInstantiationException( 86 "Something went wrong when creating $enclosingClass" 87 ) 88 } 89 } 90 } 91 } 92