Add AggregateAppFunctionInventory abstract class
Bug: 374924489
Test: ./gradlew :appfunctions:appfunctions-runtime:test
Change-Id: I948c1c484dda6b35d94f6d0bf330ae77ee1d88cf
diff --git a/appfunctions/appfunctions-runtime/build.gradle b/appfunctions/appfunctions-runtime/build.gradle
index 3b37e08..a95d75a 100644
--- a/appfunctions/appfunctions-runtime/build.gradle
+++ b/appfunctions/appfunctions-runtime/build.gradle
@@ -35,6 +35,9 @@
// Internal dependencies
implementation("androidx.annotation:annotation:1.9.0-rc01")
implementation project(":appfunctions:appfunctions-common")
+
+ testImplementation(libs.junit)
+ testImplementation(libs.truth)
}
android {
diff --git a/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/internal/AggregateAppFunctionInventory.kt b/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/internal/AggregateAppFunctionInventory.kt
new file mode 100644
index 0000000..6f792de
--- /dev/null
+++ b/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/internal/AggregateAppFunctionInventory.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2025 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.appfunctions.internal
+
+import androidx.annotation.RestrictTo
+import androidx.appfunctions.metadata.AppFunctionMetadata
+
+/**
+ * An [AppFunctionInventory] that aggregates the function metadata from multiple
+ * [AppFunctionInventory] instances.
+ *
+ * AppFunction compiler will automatically generate the implementation of this class to access all
+ * generated [AppFunctionMetadata] exposed by the application.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class AggregateAppFunctionInventory : AppFunctionInventory {
+
+ /** The list of [AppFunctionInventory] instances that contribute to this aggregate. */
+ public abstract val inventories: List<AppFunctionInventory>
+
+ final override val functionIdToMetadataMap: Map<String, AppFunctionMetadata> by lazy {
+ // Empty collection can't be reduced
+ if (inventories.isEmpty()) return@lazy emptyMap()
+ inventories.map(AppFunctionInventory::functionIdToMetadataMap).reduce { acc, map ->
+ acc + map
+ }
+ }
+}
diff --git a/appfunctions/appfunctions-runtime/src/test/java/androidx/appfunctions/internal/AggregateAppFunctionInventoryTest.kt b/appfunctions/appfunctions-runtime/src/test/java/androidx/appfunctions/internal/AggregateAppFunctionInventoryTest.kt
new file mode 100644
index 0000000..da0da07
--- /dev/null
+++ b/appfunctions/appfunctions-runtime/src/test/java/androidx/appfunctions/internal/AggregateAppFunctionInventoryTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2025 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.appfunctions.internal
+
+import androidx.appfunctions.metadata.AppFunctionComponentsMetadata
+import androidx.appfunctions.metadata.AppFunctionDataTypeMetadata
+import androidx.appfunctions.metadata.AppFunctionMetadata
+import androidx.appfunctions.metadata.AppFunctionResponseMetadata
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class AggregateAppFunctionInventoryTest {
+
+ @Test
+ fun testEmpty() {
+ val aggregateInventory =
+ object : AggregateAppFunctionInventory() {
+ override val inventories: List<AppFunctionInventory> = emptyList()
+ }
+
+ assertThat(aggregateInventory.functionIdToMetadataMap).hasSize(0)
+ }
+
+ @Test
+ fun testUniqueInventories() {
+ val aggregateInventory =
+ object : AggregateAppFunctionInventory() {
+ override val inventories: List<AppFunctionInventory> =
+ listOf(Inventory1(), Inventory2())
+ }
+
+ assertThat(aggregateInventory.functionIdToMetadataMap).hasSize(2)
+ assertThat(aggregateInventory.functionIdToMetadataMap.keys)
+ .containsExactly(
+ "androix.appfunctions.internal#test1",
+ "androix.appfunctions.internal#test2"
+ )
+ }
+
+ @Test
+ fun testDuplicatedInventories() {
+ val aggregateInventory =
+ object : AggregateAppFunctionInventory() {
+ override val inventories: List<AppFunctionInventory> =
+ listOf(Inventory1(), Inventory1())
+ }
+
+ assertThat(aggregateInventory.functionIdToMetadataMap).hasSize(1)
+ assertThat(aggregateInventory.functionIdToMetadataMap.keys)
+ .containsExactly(
+ "androix.appfunctions.internal#test1",
+ )
+ }
+
+ private class Inventory1 : AppFunctionInventory {
+ override val functionIdToMetadataMap: Map<String, AppFunctionMetadata> =
+ mapOf(
+ "androix.appfunctions.internal#test1" to
+ AppFunctionMetadata(
+ id = "androix.appfunctions.internal#test1",
+ isEnabledByDefault = false,
+ isRestrictToTrustedCaller = false,
+ displayNameRes = 0,
+ schema = null,
+ parameters = emptyList(),
+ response =
+ AppFunctionResponseMetadata(
+ isNullable = false,
+ dataType =
+ AppFunctionDataTypeMetadata(
+ type = AppFunctionDataTypeMetadata.UNIT
+ )
+ ),
+ components = AppFunctionComponentsMetadata(dataTypes = emptyList())
+ )
+ )
+ }
+
+ private class Inventory2 : AppFunctionInventory {
+ override val functionIdToMetadataMap: Map<String, AppFunctionMetadata> =
+ mapOf(
+ "androix.appfunctions.internal#test2" to
+ AppFunctionMetadata(
+ id = "androix.appfunctions.internal#test2",
+ isEnabledByDefault = false,
+ isRestrictToTrustedCaller = false,
+ displayNameRes = 0,
+ schema = null,
+ parameters = emptyList(),
+ response =
+ AppFunctionResponseMetadata(
+ isNullable = false,
+ dataType =
+ AppFunctionDataTypeMetadata(
+ type = AppFunctionDataTypeMetadata.UNIT
+ )
+ ),
+ components = AppFunctionComponentsMetadata(dataTypes = emptyList())
+ )
+ )
+ }
+}